-
Notifications
You must be signed in to change notification settings - Fork 801
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use mmap.PAGESIZE constant as value for first read #505
Use mmap.PAGESIZE constant as value for first read #505
Conversation
9d459f7
to
695f095
Compare
With 60000 small files, switching
(with a stable timing on multiple runs) to
With a strong speedup on second run thanks to the OS filesystem cache. I admit that such numbers depend heavily on:
Therefore I don't think there is an always perfect |
It's not good practice to poke around internal constants of code. If there's a better value we should change it. |
I agree it is not ideal. A small I thought about adding a
Would this make more sense ? |
Thinking a bit more, I'm not sure that this should make a difference at all. If the file is sparse then they'll be handled via the zero page, which doesn't use file cache. Do you have benchmarks? |
Well my benchmark is kinda primitive. I create my 60k files by launching 60k times this python program:
and I time with a simplified version of what the library performs:
Before each run, I wipe the file cache with With a bunch of free memory (~ 26 GB) and a read size of 65535 bytes:
With a bunch of free memory (~ 26 GB) and a read size of 4096 bytes:
Now with ~ 2 GB of free memory and a read size of 65535 bytes:
And finally with ~ 2 GB of free memory and a read size of 4096 bytes:
And sure enough, if I constraint the system even more to have less than a 500 MB of free memory, the performance is bad even with 4096:
And in the same way, even with 25 GB of available memory, if I push the read size to 650 350 kbytes:
|
Thanks, looks like we should change to value to 4k.
…On Thu 30 Jan 2020, 16:49 Xavier Fernandez, ***@***.***> wrote:
Well my benchmark is kinda primitive.
I create my 60k files by launching 60k times this python program:
import os
os.environ['prometheus_multiproc_dir'] = 'tmp'
import random
import prometheus_client
METRIC = prometheus_client.Summary(
'some_duration_seconds',
'time spent somewhere',
labelnames=('label1', 'second_label'),
namespace='profile',
subsystem='lot_of_files',
)
METRIC.labels(random.choice(('success', 'error')), f'test{random.randint(0, 10)}').observe(random.random())
METRIC.labels(random.choice(('success', 'error')), f'test{random.randint(0, 10)}').observe(random.random())
METRIC.labels(random.choice(('success', 'error')), f'test{random.randint(0, 10)}').observe(random.random())
and I time with a simplified version of what the library performs:
# This is read_simple.py
import os
import sys
import time
BLOCK_SIZE = int(sys.argv[1])
print('BLOCK_SIZE', BLOCK_SIZE)
start = time.perf_counter()
for filename in os.listdir('tmp'):
if filename.endswith('.db'):
with open(os.path.join('tmp', filename), 'rb') as infp:
data = infp.read(BLOCK_SIZE)
print(time.perf_counter() - start)
Before each run, I wipe the file cache with echo 1 >
/proc/sys/vm/drop_caches).
------------------------------
With a bunch of free memory (~ 26 GB) and a read size of 65535 bytes:
$ ls tmp/ | wc -l
60000
$ free
total used free shared buff/cache available
Mem: 32537864 5188908 26183948 668028 1165008 26234928
Swap: 0 0 0
$ python read_simple.py 65535
BLOCK_SIZE 65535
12.427678888998344
0.9561608399963006
0.8500389300024835
0.8534953799971845
0.8507285489904461
0.8569888329948299
0.8777932550001424
0.8816033009934472
0.8621571760013467
0.8545428220095346
With a bunch of free memory (~ 26 GB) and a read size of 4096 bytes:
(prometheus_profile) ***@***.*** prometheus_profile]$ free
total used free shared buff/cache available
Mem: 32537864 5148264 26250408 645920 1139192 26298164
Swap: 0 0 0
(prometheus_profile) ***@***.*** prometheus_profile]$ python read_simple.py 4096
BLOCK_SIZE 4096
10.32166458798747
0.4410311039973749
0.39926853599899914
0.4384756700019352
0.4004494149994571
0.40352619100303855
0.3965474760043435
0.3989230669976678
0.39654788600455504
0.4002341330051422
Now with ~ 2 GB of free memory and a read size of 65535 bytes:
$ free
total used free shared buff/cache available
Mem: 32537864 29285200 2293668 663724 958996 2171332
Swap: 0 0 0
$ python read_simple.py 65535
BLOCK_SIZE 65535
12.443866290006554
11.633168715998181
12.085821863001911
11.749885090990574
12.8851961879991
12.385939428990241
11.35605888698774
11.659072276990628
11.294882508998853
11.368941648004693
And finally with ~ 2 GB of free memory and a read size of 4096 bytes:
$ free
total used free shared buff/cache available
Mem: 32537864 29260832 2405596 646916 871436 2247648
Swap: 0 0 0
$ python read_simple.py 4096
BLOCK_SIZE 4096
12.362105008010985
0.40657658501004335
0.3870056059968192
0.4089814879989717
0.381813013998908
0.3841210550017422
0.38265788700664416
0.38066420500399545
0.3836389770003734
0.3772799519938417
And sure enough, if I constraint the system even more to have less than a
500 MB of free memory, the performance is bad even with 4096:
(prometheus_profile) ***@***.*** prometheus_profile]$ free
total used free shared buff/cache available
Mem: 32537864 31400812 335304 621992 801748 155100
Swap: 0 0 0
(prometheus_profile) ***@***.*** prometheus_profile]$ python read_simple.py 4096
BLOCK_SIZE 4096
11.380508398986422
10.212263110995991
12.595423141989158
11.597996773009072
9.844564571001683
11.246935460992972
11.166533361989423
10.640836154998397
10.942926752992207
11.625870076008141
And in the same way, even with 25 GB of available memory, if I push the
read size to 650 350 kbytes:
$ free
total used free shared buff/cache available
Mem: 32537864 5259756 26470680 632860 807428 26287820
Swap: 0 0 0
$ python read_simple.py 655350
BLOCK_SIZE 655350
22.036876083991956
20.698863850993803
23.11240423300478
21.609464114997536
21.656152483003098
22.610070716997143
22.320307180998498
24.78865629399661
23.689863965002587
20.960628273998736
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#505?email_source=notifications&email_token=ABWJG5TXIIDU2RLP5P6GV2TRALZKJA5CNFSM4KNLPZI2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEKLO54Q#issuecomment-580316914>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABWJG5SY4KYW36UHG7YFZVTRALZKJANCNFSM4KNLPZIQ>
.
|
But why 4k and not 1k or 8k ? Depending on the number of files, the number of bytes used and the available memory, surely the optimum value might differ ? |
4k is the page size, so Iower than that won't help. The performance numbers for 4k are acceptable, so I don't see a need to consider 8k - unless you've more data. |
This size can have a real impact on performance when a lot of files need to be read. The performance relies heavily on the file system cache so reading as little pages as possible helps a lot on memory constrained systems. Signed-off-by: Xavier Fernandez <xavier.fernandez@polyconseil.fr>
I tried to generate less but bigger files with:
The resulting files ended up using ~ 58kB. And I updated the
(which means 4096 will require a second read but 65535 won't). With very little RAM, the performance is slightly better with 65535 but not dramatically so:
And with lot of RAM, as expected, there is little impact:
So 4096 seems like a good compromise 👍 I updated the PR to use |
695f095
to
e7874ee
Compare
Yip, that seems reasonable. Thanks! |
Thanks for merging :) |
I don't think there's anything pressing for release currently, I'll probably wait until the OpenMetrics stuff is a bit more finalised. |
This size can have a real impact on performance when a lot of files need
to be read.
The performance relies heavily on the file system cache and having the
possibility to adapt _INITIAL_READ_SIZE can help on memory constrained
systems.