Как фред действительно работает? - PullRequest
70 голосов
/ 21 декабря 2011

Объявление 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 ]

98 голосов
/ 21 декабря 2011

Может быть или не быть никакой разницы в производительности.Существует различие в семантике.

fread(a, 1, 1000, stdin);

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

fread(a, 1000, 1, stdin);

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

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

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

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

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

16 голосов
/ 21 декабря 2011

Согласно спецификации , реализация может трактоваться по-разному.

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

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

13 голосов
/ 21 декабря 2011

Это будет деталь реализации. В 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.

3 голосов
/ 21 декабря 2011

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;
}
3 голосов
/ 21 декабря 2011

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

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

Если возникает ошибка, результирующее значение индикатора положения файла для потока является неопределенным.Если частичный элемент читается, его значение является неопределенным.(ИСО / МЭК 9899: TC2 7.19.8.1)

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

1 голос
/ 21 декабря 2011

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

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

Inshort inоба случая будут доступны через fgetc () ...!

0 голосов
/ 08 июня 2012

Я хотел уточнить ответы здесь.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 байтов.

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

...