Читает из файлов в файловой системе Linux на твердотельных накопителях NVMe значительно быстрее при загрузке, кратной 128 КиБ?
Я вижу странную аномалию производительности при попытке прочитать большой файл (~20 ГБ), находящийся в файловой системе ext4, поддерживаемой SSM NVMe, на RedHat Linux. В моем упрощенном эксперименте (весь код ниже) я просто пытаюсь прочитать 10 000 000 000 (10 миллиардов) байтов из этого файла выше. Мое чтение может начинаться с любого смещения (так, чтобы по крайней мере 10 миллиардов байтов были доступны после этого), и я читаю 1 МБ за раз. Это начальное смещение настраивается в моей программе с помощью параметра командной строки. Вот что я вижу: - когда мое начальное смещение, если 0 или выровнено по 128 КиБ (т.е. кратно 131072 байтам), считывание завершается примерно за 3,663 секунды. - при других произвольных начальных смещениях считывание занимает от 6,8 до 7,9 секунд; т.е. примерно в два раза выровненные старты. [Некоторые примеры проб / наблюдений со временем приведены ниже]
Мои вопросы:- 1. Это ожидаемое поведение в стандартных файловых системах? 2. Я не смог найти какой-либо настроенный параметр, который указывает мне на это 128 КиБ. Итак, я надеюсь, что кто-то может указать мне, где искать дальше.
== исходный код моего эксперимента ==
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <malloc.h>
#include <assert.h>
#include <iostream>
#define __min(a,b) ((a) < (b) ? (a) : (b))
int main(int argc, char **argv)
{
char* buffer = NULL;
if (argc < 4) {
std::cout << "usage: app <filename-to-read-in-mem> <offset> <nr_bytes_to_read>\n";
std::cout << "sysconf(_SC_PAGE_SIZE): " << sysconf(_SC_PAGE_SIZE) << std::endl;
return 1;
}
off_t offset = atoll(argv[2]);
ssize_t nr_bytes_to_read = atoll(argv[3]);
int fd = open(argv[1], O_RDONLY);
assert(fd > 0);
assert(lseek(fd, offset, SEEK_SET) == offset);
assert(posix_fadvise(fd, offset, 0, POSIX_FADV_SEQUENTIAL) == 0);
size_t chunksize = 1024*1024; //1 MiB
int rv = posix_memalign((void **) &buffer, 4096 /*sysconf(_SC_PAGE_SIZE)*/, chunksize);
assert(rv == 0 && buffer != NULL);
size_t yet_to_read = nr_bytes_to_read;
size_t readsize = chunksize;
ssize_t rc = 0;
size_t total_bytes_read = 0;
while (yet_to_read > 0) {
readsize = __min(yet_to_read, chunksize); //adjust for the last read
rc = read(fd, buffer, readsize);
assert(rc >= 0); //Not handling for EINTR for the experiment.
yet_to_read -= rc;
total_bytes_read += rc;
if (rc == 0) break;
//Do something with the buffer and then move on to the next chunk.
}
std::cout << "Read " << total_bytes_read << " at offset " << offset << std::endl;
free(buffer);
close(fd);
return 0;
}
== конец исходного кода ==
== пробные прогоны ==
[root@ninja save]# pwd
/ninjalocal2/gautam/save
[root@ninja save]# ls -la parcel.0001
-rw-r--r-- 1 root root 21434411376 Nov 1 10:50 parcel.0001
[root@ninja save]# df -Th .
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/rhel_ninja2-ninjalocal2 ext4 1.8T 245G 1.5T 15% /ninjalocal2
[root@ninja save]#
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 0 10000000000
Read 10000000000 at offset 0
real 0m3.664s
user 0m0.002s
sys 0m1.762s
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 4096 10000000000
Read 10000000000 at offset 4096
real 0m7.964s
user 0m0.000s
sys 0m1.890s
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 8192 10000000000
Read 10000000000 at offset 8192
real 0m7.939s
user 0m0.002s
sys 0m1.861s
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 65536 10000000000
Read 10000000000 at offset 65536
real 0m6.818s
user 0m0.001s
sys 0m1.907s
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 131072 10000000000
Read 10000000000 at offset 131072
real 0m3.662s
user 0m0.004s
sys 0m1.716s
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 131072 10000000000
Read 10000000000 at offset 131072
real 0m3.664s
user 0m0.001s
sys 0m1.735s
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 13107200 10000000000
Read 10000000000 at offset 13107200
real 0m3.663s
user 0m0.001s
sys 0m1.729s
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 131072000 10000000000
Read 10000000000 at offset 131072000
real 0m3.663s
user 0m0.002s
sys 0m1.719s
[root@ninja save]# sync; echo 3 > /proc/sys/vm/drop_caches; time ./stackru.bin ./parcel.0001 324567890 10000000000
Read 10000000000 at offset 324567890
real 0m7.432s
user 0m0.003s
sys 0m1.944s
== конец пробных прогонов ======