Как фред действительно работает?
Декларация 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
байт сразу каждый раз?
7 ответов
Там может или не может быть никакой разницы в производительности. Существует разница в семантике.
fread(a, 1, 1000, stdin);
пытается прочитать 1000 элементов данных, каждый из которых имеет длину 1 байт.
fread(a, 1000, 1, stdin);
пытается прочитать 1 элемент данных длиной 1000 байт.
Они разные, потому что fread()
возвращает количество элементов данных, которые он смог прочитать, а не количество байтов. Если он достигает конца файла (или состояния ошибки) перед чтением полных 1000 байтов, первая версия должна точно указать, сколько байтов она прочитала; второй просто терпит неудачу и возвращает 0.
На практике это, вероятно, просто вызовет функцию более низкого уровня, которая пытается прочитать 1000 байтов и показывает, сколько байтов она фактически прочитала. Для больших чтений он может сделать несколько вызовов более низкого уровня. Вычисление значения, которое будет возвращено fread()
отличается, но за счет расчета тривиально.
Может быть разница, если реализация может сказать, прежде чем пытаться прочитать данные, что данных недостаточно для чтения. Например, если вы читаете из 900-байтового файла, первая версия будет читать все 900 байтов и возвращать 900, а вторая может ничего не читать. В обоих случаях индикатор положения файла опережает количество успешно прочитанных символов, т. Е. 900.
Но в целом вам, вероятно, следует выбрать способ его вызова, исходя из того, какая информация вам нужна. Прочитайте один элемент данных, если частичное чтение ничем не лучше, чем вообще ничего не читать. Читайте небольшими порциями, если частичное чтение полезно.
Это будет деталь реализации. В glibc они одинаковы по производительности, так как они реализованы в основном как (ссылка 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.
В соответствии со спецификацией, эти два варианта могут трактоваться по-разному.
Если ваш файл меньше 1000 байт, fread(a, 1, 1000, stdin)
(прочитайте 1000 элементов по 1 байту каждый) все равно будет копировать все байты до EOF. С другой стороны, результат fread(a, 1000, 1, stdin)
(прочитайте 1 1000-байтовый элемент), сохраненный в a
не указан, так как недостаточно данных для завершения чтения первого (и единственного) 1000-байтового элемента.
Конечно, некоторые реализации все еще могут копировать "частичный" элемент в столько байтов, сколько необходимо.
fread
звонки getc
внутренне. в Minix
количество раз getc
называется просто size*nmemb
так сколько раз getc
будет называться в зависимости от произведения этих двух. Так что оба fread(a, 1, 1000, stdin)
а также fread(a, 1000, 1, stdin)
побежит getc
1000=(1000*1)
Времена. Вот простая реализация 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;
}
Может не быть разницы в производительности, но эти вызовы не совпадают.
fread
возвращает количество прочитанных элементов, поэтому эти вызовы будут возвращать разные значения.- Если элемент не может быть полностью прочитан, его значение не определено:
Если возникает ошибка, результирующее значение индикатора положения файла для потока является неопределенным. Если частичный элемент читается, его значение является неопределенным. (ИСО / МЭК 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 и в некоторых случаях необходимости для диска выполнять дополнительные операции поиска, которые в противном случае были бы оптимизированы.
Возвращаясь к разнице между двумя формами фреда. Первый возвращает фактическое количество прочитанных байтов. Последний возвращает 0, если размер файла меньше 1000, в противном случае он возвращает 1. В обоих случаях буфер будет заполнен одними и теми же данными, т.е. содержимым файла до 1000 байтов.
В общем, вы, вероятно, хотите, чтобы 2-й параметр (размер) был установлен в 1, так что вы получите количество прочитанных байтов.