Как на самом деле работает fread?

декларация fread выглядит следующим образом:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

вопрос в том, есть ли разница в производительности чтения двух таких вызовов fread:

char a[1000];
  1. fread(a, 1, 1000, stdin);
  2. fread(a, 1000, 1, stdin);

будет ли он читать 1000 байт сразу каждый раз?

7 ответов


может быть или не может быть никакой разницы в производительности. Есть разница в семантике.

fread(a, 1, 1000, stdin);

пытается прочитать 1000 элементов данных, каждый из которых занимает 1 байт.

fread(a, 1000, 1, stdin);

пытается прочитать 1 элемент данных длиной 1000 байт.

они разные, потому что fread() возвращает количество элементов данных, которые он смог прочитать, а не количество байтов. Если он достигает конца файла (или условия ошибки) перед чтением полного 1000 байты, первая версия должна точно указать, сколько байтов она прочитала; вторая просто терпит неудачу и возвращает 0.

на практике это, вероятно, просто вызовет функцию более низкого уровня, которая пытается прочитать 1000 байтов и указывает, сколько байтов она фактически прочитала. Для больших чтений он может совершать несколько вызовов более низкого уровня. Вычисление значения, которое будет возвращено fread() отличается, но расходы на расчет тривиальны.

может быть разница если реализация может сказать, прежде чем пытаться прочитать данные, что для чтения недостаточно данных. Например, если Вы читаете из 900-байтового файла, первая версия будет читать все 900 байт и возвращать 900, в то время как вторая может ничего не читать. В обоих случаях индикатор положения файла расширяется на число символы успешно прочитано, т. е. 900.

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


По данным спецификация, эти два могут рассматриваться по-разному реализацией.

Если ваш файл меньше 1000 байт fread(a, 1, 1000, stdin) (прочитайте 1000 элементов по 1 байту каждый) все равно скопирует все байты до EOF. С другой стороны, результат fread(a, 1000, 1, stdin) (чтение 1 1000-байтового элемента), хранящегося в a Не указано, потому что недостаточно данных для завершения чтения "первого" (и только) 1000-байтового элемента.

конечно, некоторые реализации могут по-прежнему копировать "частичный" элемент в столько байтов, сколько необходимо.


это будет деталь реализации. В glibc они идентичны по производительности, так как реализованы в основном как (Ref http://sourceware.org/git/?p=glibc.git;a=blob;f=libio / iofread.c):

size_t fread (void* buf, size_t size, size_t count, FILE* f)
{
    size_t bytes_requested = size * count;
    size_t bytes_read = read(f->fd, buf, bytes_requested);
    return bytes_read / size;
}

обратите внимание, что C и POSIX стандарт не гарантирует полный объект размера size нужно читать каждый раз. Если весь объект не может быть прочитан (например,stdin только 999 байт, но вы просили size == 1000), то файл будет левый в состоянии interdeterminate (С99 §7.19.8.1/2).

Edit: см. другие ответы о POSIX.


fread звонки getc внутренне. в Minix несколько раз getc называется просто size*nmemb так сколько раз getc будет вызываться в зависимости от продукт из этих двух. Так Как fread(a, 1, 1000, stdin) и fread(a, 1000, 1, stdin) будет работать getc


может не быть разницы в производительности, но эти вызовы не совпадают.

  • fread возвращает количество прочитанных элементов, поэтому эти вызовы будут возвращать разные значения.
  • если элемент не может быть полностью прочитан, его значение неопределенно:

при возникновении ошибки результирующее значение индикатора положения файла для потока сомнительный. Если частичный элемент считывается, его значение не определено. (ISO / IEC 9899:ск2 7.19.8.1)

нет большой разницы в реализация glibc, который просто умножает размер элемента на количество элементов, чтобы определить, сколько байтов для чтения и делит количество прочитанных на размер элемента в конце. Но версия, указывающая размер элемента 1, всегда скажет вам правильное количество прочитанных байтов. Однако, если вы заботитесь только о полностью прочитанных элементах определенного размера, использование другой формы спасает вас от занимаюсь подразделением.


еще одна форма предложения http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html примечательно

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

Inshort в обоих случаях данные будут доступны fgetc ()...!


Я хотел уточнить ответы здесь. fread выполняет буферизованный ввод-вывод. Фактические размеры блоков чтения, используемые fread, определяются используемой реализацией C.

все современные библиотеки C будут иметь одинаковую производительность с двумя вызовами:

fread(a, 1, 1000, file);
fread(a, 1000, 1, file);

даже что-то вроде:

for (int i=0; i<1000; i++)
  a[i] = fgetc(file)

должно привести к тем же шаблонам доступа к диску, хотя fgetc будет медленнее из-за большего количества вызовов стандартных библиотек c и в некоторых случаях необходимости диска для выполнения дополнительных поисков, которые в противном случае были бы оптимизированы.

возвращаясь к разнице между двумя формами fread. Первый возвращает фактическое число прочитанных байтов. Последний возвращает 0, если размер файла меньше 1000, в противном случае она возвращает 1. В обоих случаях буфер будет заполнен одними и теми же данными, т. е. содержимым файла до 1000 байт.

В общем, вы, вероятно, хотите сохранить 2-й параметр (размер) равным 1, чтобы вы получаете количество прочитанных байтов.