Как действительно работает fread?
Объявление fread
выглядит следующим образом:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
Вопрос: есть ли разница в производительности чтения двух таких вызовов на fread
:
char a[1000];
-
fread(a, 1, 1000, stdin);
-
fread(a, 1000, 1, stdin);
Будет ли каждый раз читать 1000
байты?
Ответы
Ответ 1
В производительности может быть и не быть. Существует различие в семантике.
fread(a, 1, 1000, stdin);
пытается прочитать 1000 элементов данных, каждый из которых имеет длину 1 байт.
fread(a, 1000, 1, stdin);
пытается прочитать 1 элемент данных длиной 1000 байт.
Они разные, потому что fread()
возвращает количество элементов данных, которые он смог прочитать, а не количество байтов. Если он достигает конца файла (или условия ошибки) перед чтением полных 1000 байт, первая версия должна указывать точно, сколько байтов оно читает; вторая просто терпит неудачу и возвращает 0.
На практике это, вероятно, просто вызовет функцию нижнего уровня, которая пытается читать 1000 байтов и указывает, сколько байтов она действительно читает. Для более крупных чтений он может выполнять несколько вызовов нижнего уровня. Вычисление значения, возвращаемого функцией fread()
, отличается, но счет вычисления тривиален.
Может быть разница, если реализация может сказать, прежде чем пытаться прочитать данные, что недостаточно данных для чтения. Например, если вы читаете из 900-байтного файла, первая версия будет читать все 900 байт и возвращать 900, а вторая, возможно, не удосужилась что-либо прочитать. В обоих случаях индикатор позиции файла продвигается на число успешно прочитанных символов, т.е. 900.
Но в целом вы, вероятно, должны выбрать, как его называть, исходя из того, какая информация вам нужна. Прочитайте один элемент данных, если частичное чтение не лучше, чем вообще ничего не читать. Читайте в небольших кусках, если частичные чтения полезны.
Ответ 2
Согласно спецификация, эти два варианта могут быть обработаны по-разному.
Если ваш файл меньше 1000 байт, fread(a, 1, 1000, stdin)
(чтение 1000 элементов по 1 байт каждый) все равно будет копировать все байты до EOF. С другой стороны, результат fread(a, 1000, 1, stdin)
(считанный 1 1000-байтовый элемент), хранящийся в a
, не указан, потому что недостаточно данных для завершения чтения "первого" (и только) 1000-байтового элемента.
Конечно, некоторые реализации могут все же скопировать элемент "partial" на столько байтов, сколько необходимо.
Ответ 3
Это будет детализация реализации. В 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
), файл будет оставлен в заданном состоянии (C99 §7.19.8.1/2).
Изменить: см. другие ответы о POSIX.
Ответ 4
fread
вызывает getc
внутренне. в Minix
количество раз getc
вызывается просто size*nmemb
, поэтому количество вызовов getc
будет зависеть от продукта этих двух. Итак, оба fread(a, 1, 1000, stdin)
и fread(a, 1000, 1, stdin)
будут запускать getc
1000=(1000*1)
Times.
Вот реализация siimple fread
из Minix
size_t fread(void *ptr, size_t size, size_t nmemb, register FILE *stream){
register char *cp = ptr;
register int c;
size_t ndone = 0;
register size_t s;
if (size)
while ( ndone < nmemb ) {
s = size;
do {
if ((c = getc(stream)) != EOF)
*cp++ = c;
else
return ndone;
} while (--s);
ndone++;
}
return ndone;
}
Ответ 5
Не может быть разницы в производительности, но эти вызовы не совпадают.
-
fread
возвращает количество прочитанных элементов, поэтому эти вызовы возвращают разные значения.
- Если элемент не может быть полностью прочитан, его значение неопределенно:
Если возникает ошибка, результирующее значение индикатора позиции файла для потока неопределенный. Если частичный элемент считывается, его значение является неопределенным. (ISO/IEC 9899: TC2 7.19.8.1)
Там нет большой разницы в glibc-реализации, которая просто умножает размер элемента на количество элементов, чтобы определить, сколько байтов для чтения и делит количество, считанное размером члена в конце. Но версия с указанием размера элемента 1 всегда будет указывать правильное количество прочитанных байтов. Однако, если вы только заботитесь о полностью читаемых элементах определенного размера, использование другой формы избавит вас от выполнения разделения.
Ответ 6
Еще одна форма предложения http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html примечательна
Функция fread() должна считывать в массив, на который указывает ptr до элементов nitems, размер которых задается размером в байтах, из потока, на который указывает поток. Для каждого объекта вызовы размера должны выполняться функции fgetc() и результаты, хранящиеся, в порядке чтения в массиве без знака char, который точно накладывает объект.
В обоих случаях данные будут доступны через fgetc()...!
Ответ 7
Я хотел пояснить ответы здесь. 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, чтобы вы получили количество прочитанных байтов.