Обзор
У меня есть программа, существенно ограниченная IO, и я пытаюсь ускорить ее.Использование mmap казалось хорошей идеей, но на самом деле это снижает производительность по сравнению с использованием серии вызовов fgets.
Некоторый демо-код
Я сжал демки до самых основных, тестируя файл размером 800 Мб с примерно 3,5 миллионами строк:
С помощью fgets:
char buf[4096];
FILE * fp = fopen(argv[1], "r");
while(fgets(buf, 4096, fp) != 0) {
// do stuff
}
fclose(fp);
return 0;
Время выполнения для файла 800 МБ:
[juhani@xtest tests]$ time ./readfile /r/40/13479/14960
real 0m25.614s
user 0m0.192s
sys 0m0.124s
Версия mmap:
struct stat finfo;
int fh, len;
char * mem;
char * row, *end;
if(stat(argv[1], &finfo) == -1) return 0;
if((fh = open(argv[1], O_RDONLY)) == -1) return 0;
mem = (char*)mmap(NULL, finfo.st_size, PROT_READ, MAP_SHARED, fh, 0);
if(mem == (char*)-1) return 0;
madvise(mem, finfo.st_size, POSIX_MADV_SEQUENTIAL);
row = mem;
while((end = strchr(row, '\n')) != 0) {
// do stuff
row = end + 1;
}
munmap(mem, finfo.st_size);
close(fh);
Время выполнения варьируется весьма незначительно,но не быстрее, чем fgets:
[juhani@xtest tests]$ time ./readfile_map /r/40/13479/14960
real 0m28.891s
user 0m0.252s
sys 0m0.732s
[juhani@xtest tests]$ time ./readfile_map /r/40/13479/14960
real 0m42.605s
user 0m0.144s
sys 0m0.472s
Другие заметки
- Наблюдая за процессом, запущенным сверху, версия memmapped вызвала несколько тысяч сбоев страниц по пути.
- Процессор и использование памяти очень низки для версии fgets.
Вопросы
- Почему это так?Это просто потому, что доступ к буферизованному файлу, реализованный fopen / fgets, лучше, чем агрессивная предварительная выборка, которую mmap с madvise POSIX_MADV_SEQUENTIAL?
- Есть ли альтернативный способ сделать это быстрее (кроме сжатия на лету/ декомпрессия для переноса нагрузки ввода-вывода на процессор)?Глядя на время выполнения 'wc -l' в том же файле, я предполагаю, что это может быть не так.