Подтвердить что ты не робот

"Оптимальная" буферизация ввода-вывода - задача программиста или ядра?

Моя задача очень проста: прочитать и проанализировать большой файл на С++ в Linux. Существует два способа:

  • Разбор байта по байтам.

    while(/*...*/) {
            ... = fgetc(...);
            /* do something with the char */
    }
    
  • Буфер анализа буфера.

    while(/*...*/) {
            char buffer[SOME_LARGE_NUMBER];
            fread(buffer, SOME_LARGE_NUMBER, 1, ...);
            /* parse the buffer */
    }
    

Теперь для меня проще разбирать байты по байт (не проверяйте, насколько заполнен буфер и т.д.). Однако я слышал, что чтение больших предметов более эффективно.

Какова философия? Является ли "оптимальным" буферизация задачи ядра, поэтому он уже буферизуется, когда я вызываю fgetc()? Или он предложил мне справиться с этим, чтобы получить максимальную эффективность?

Кроме того, помимо всей философии: что такое реальность в Linux здесь?

4b9b3361

Ответ 1

Независимо от производительности или базовой буферизации fgetc(), вызов функции для каждого отдельного байта, который вам нужен, по сравнению с наличием приличного размера буфера для итерации - это накладные расходы, которые ядро ​​не может вам помочь.

Я сделал несколько быстрых и грязных таймингов для своей локальной системы (очевидно, YMMV).

Я выбрал файл ~ 200 тыс. и суммировал каждый байт. Я делал это 20000 раз, чередуя каждые 1000 циклов между чтением с помощью fgetc() и чтения с помощью fread(). Я рассчитал каждые 1000 циклов как единый кусок. Я скомпилировал сборку релизов с включенными оптимизациями.

Вариант цикла fgetc() был последовательно 45x медленнее, чем цикл fread().

После запроса в комментариях я также сравнил getc(), а также изменил буфер stdio. Не было заметных изменений в производительности.

Ответ 2

Буфер stdio не является частью ядра. Это часть пользовательского пространства.

Однако вы можете влиять на размер этого буфера, используя setbuf. Когда этот буфер недостаточно заполнен, библиотека stdio заполнит его, выпустив системную функцию чтения.

Поэтому не имеет значения, используя fgetc или fread это условия переключения между ядром и пользователем.

Ответ 3

Не важно, действительно. Даже из SSD накладные расходы ввода-вывода затмевают время, затрачиваемое на буферизацию. Конечно, теперь микросекунды вместо миллисекунд, но функциональные вызовы измеряются в наносекундах.

Ответ 4

Причина медленности fgetc - это не количество вызовов функций, а количество системных вызовов. fgetc часто реализуется как int fgetc(FILE *fp) { int ch; return (fread(&ch,1,1,fp)==1?ch:-1); }

Несмотря на то, что сам файл может содержать буфер 64k или 1k, служебные данные системного вызова различаются по сравнению, например,

 int fgetc_buffered(FILE *fp) {
     static int head=0,tail=0; 
     static unsigned char buffer[1024];
     if (head>tail) return buffer[tail++];
     tail=0;head=fread(buffer,1,1024,fp);
     if (head<=0) return -1;
     return buffer[tail++];
 }

Ответ 5

Процедуры stdio выполняют буферизацию пользовательского пространства. Когда вы вызываете getc, fgetc, fread, они извлекают данные из буфера пространства пользователя stdio. Когда буфер пуст, stdio будет использовать вызов чтения ядра, чтобы получить больше данных.

Люди, которые разрабатывают файловые системы, знают, что доступ к диску (в основном ищет) очень дорог. Так что даже если stdio использует размер блока размером в 512 байт, файловая система может использовать размер блока 4 КБ, и ядро ​​будет читать файл размером 4 КБ за раз.

Как правило, ядро ​​инициирует запрос диска/сети после его чтения. Для диска, если он видит, что вы читаете файл последовательно, он начнет читать вперед (получение блоков, прежде чем вы их попросите), чтобы данные были доступны быстрее.

Также ядро ​​будет кэшировать файлы в памяти. Поэтому, если файл, который вы читаете, подходит в памяти, после одного запуска вашей программы файл останется в памяти до тех пор, пока ядро ​​не решит, что лучше кэшировать другие файлы, на которые вы ссылаетесь.

Использование mmap не сможет извлечь выгоду из чтения ядра.