Почему моя функция cat с системными вызовами работает медленнее по сравнению с cat в Linux?

Я сделал эту функцию на C, используя системные вызовы (открытие, чтение и запись), чтобы имитировать функцию «кошка» в системах Linux, и она медленнее, чем реальная...

Я использую тот же размер буфера, что и настоящий «кошка», и, используя «strace», я думаю, что он выполняет такое же количество системных вызовов. Но вывод моего "кота" немного медленнее, чем у настоящего "кота".

Это код, который у меня есть:

#define BUFSIZ 32768

int sysWriteBuffer(int fdout, char *buffer, ssize_t readBytes) {
    ssize_t writtenBytes = 0;

    while(writtenBytes < readBytes) {
        writtenBytes += write(fdout,
            buffer + writtenBytes, readBytes - writtenBytes);
        if(writtenBytes == -1) {
            return -1;
        }
    }

    return 0;
}

int catPrint(int fdin, int fdout) {
    char buffer[BUFSIZ];
    ssize_t readBytes;

    do {
        readBytes = read(fdin, buffer, BUFSIZ);

        if(readBytes == -1) {
            return -1;
        }

        if(sysWriteBuffer(fdout, buffer, readBytes) == -1) {
            return -1;
        }
    } while(readBytes > 0);

    return 0;
}

Я читаю из файла (который я передаю в качестве аргумента в main, я думаю, что код здесь не нужен), затем я вызываю функцию catPrint() с этим файловым дескриптором и 1 для выходного дескриптора, поэтому он печатает на стандартный вывод.

Я не понимаю, почему это медленнее, потому что я использую один и тот же файл для тестирования, и с обоими (настоящим «котом» и моим) есть только один read() и один write() для всего текста. Разве весь текст не должен просто появляться на экране?

P.S. Я отметил это как домашнее задание, хотя мой вопрос здесь (почему это медленнее) не является частью домашнего задания. Мне нужно было использовать системные вызовы только для создания функции типа «кошка», что и сделано. Меня просто заинтриговал мой код, который немного медленнее.

ПРОБЛЕМА РЕШЕНА ОТ МЕНЯ ПО ТУПОСТИ:
Я просто решил несколько раз вызвать исходную кошку linux для одного и того же файла, один за другим, и я только что понял, что он также был медленным, некоторые из раз я назвал это, так же медленно, как мой собственный. Я думаю, все в порядке, чем...

Извините за потраченное время, как эти люди.


person rfgamaral    schedule 20.04.2009    source источник
comment
ИМХО, тег homework вводит в заблуждение. Ваш вопрос касается интересного фонового факта. homework подразумевает либо утомительную работу для новичков, либо (на другом конце шкалы) вопрос викторины.   -  person Konrad Rudolph    schedule 20.04.2009
comment
Кстати, обработка ошибки (т. е. записи, возвращающей -1) неверна, если ошибка возникает при второй записи().   -  person jpalecek    schedule 20.04.2009
comment
Вы можете удалить тег домашнего задания, если считаете, что так лучше... Что ты имеешь в виду, jpalecek? Там только одна запись (как в системном вызове) у меня только вспомогательная функция. Если write() внутри этой вспомогательной функции не работает, мне нужно вернуть -1 до того места, где был вызван catPrint()...   -  person rfgamaral    schedule 20.04.2009
comment
Просто спрашиваю здесь - использует ли cat системные вызовы, а не fread, fwrite, которые буферизуются?   -  person Liran Orevi    schedule 20.04.2009
comment
@Blingo: cat, скорее всего, будет использовать системные вызовы, а не fread() и fwrite(). Ему не нужны маленькие блоки ввода-вывода, оптимизируемые стандартным вводом-выводом (fread() и др.); он имеет дело с большими кусками файла - вероятно, большими кусками, чем стандартный размер буфера ввода-вывода.   -  person Jonathan Leffler    schedule 21.04.2009
comment
У системного кота больше жизней ;)   -  person Milan Babuškov    schedule 24.10.2009
comment
Читайте источник. Загрузите исходный код gnu cat, чтобы узнать, какие интересные трюки вы можете выучить.   -  person ctrl-alt-delor    schedule 07.02.2011


Ответы (6)


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

person Chas. Owens    schedule 20.04.2009

Исследование mmap(2).

Вы отбросите тонкости ftell/fread, но он пропустит уровень косвенности, если пропускная способность чтения действительно важна.

person Pasi Savolainen    schedule 20.04.2009
comment
Мне не разрешено использовать что-либо еще для этого упражнения. - person rfgamaral; 20.04.2009

Возможно, вы скомпилировали без оптимизации (или без такой высокой настройки оптимизации)?

Кроме того, ваш код будет вызывать sysWriteBuffer один раз с readBytes равным нулю - может быть, это (частично) объясняет это?

Вы также можете встроить sysWriteBuffer (с помощью переключателя компилятора или вручную).

встраивание означает копирование тела функции на сайт ее вызова, чтобы устранить накладные расходы вызов функции. Иногда компиляторы делают это автоматически (я думаю, -O3 включает эту оптимизацию в gcc). Вы также можете использовать ключевое слово inline в gcc, чтобы указать компилятор для встраивания функции. Если вы сделаете это, ваше объявление будет выглядеть так:

static inline int sysWriteBuffer(int fdout, char *buffer, ssize_t readBytes) {
....
person Rick Copeland    schedule 20.04.2009
comment
Если вы используете strace для cat, вы увидите, что это также происходит и там, поэтому я просто оставил его... И я использую флаг -O2. - person rfgamaral; 20.04.2009
comment
Вы можете попробовать -O3 -funroll_loops и посмотреть, как это работает. Еще лучше было бы определить точные флаги, с которыми компилировался cat. - person Rick Copeland; 20.04.2009
comment
Просто примечание: откидная створка -funroll-loops (второй дефис, а не подчеркивание), и я не думаю, что в этом случае это что-то даст. - person Anthony; 20.04.2009
comment
Спасибо за исправление по поводу флага - кстати, это флаг, а не откидная створка (вы не хотите, чтобы мы могли редактировать эти комментарии?) :) - person Rick Copeland; 20.04.2009
comment
Что такое встраивание (существует ли вообще это слово?) sysWriteBuffer? - person rfgamaral; 20.04.2009

Без сравнения исходников сложно сказать. Если вы сравниваете свою кошку с кошкой GNU, помните, что вы сравниваете код, которому несколько часов/дней, с кодом, который развивался более двадцати лет.

Возможно, вы захотите провести более полный анализ производительности, запустив обе программы с разными размерами входных данных, с разных устройств (подойдет RAM-диск) и несколько раз подряд. Вы должны попытаться определить, ГДЕ в вашей программе это медленнее.

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

person Juliano    schedule 20.04.2009

Сколько? Канонический кот - это что-то вроде

char bufr[BUFSIZ];
ssize_t len;

while((len=read(fdin, bufr, BUFSIZ)) >0)
     write(fdout, bufr, len);

что сохраняет несколько инструкций.

person Charlie Martin    schedule 20.04.2009
comment
Это может быть каноническая версия, но неправильная (например, если сигнал приходит, пока вы пишете()) - person jpalecek; 20.04.2009
comment
Какую часть чего-то подобного вы пропустили? - person Charlie Martin; 20.04.2009
comment
Как я уже сказал, исходный кот и мой кот вызывают один read() с размером буфера 32768 и write() с тем же размером буфера и последним read() в конце (когда он больше ничего не читает). и заканчивается). - person rfgamaral; 20.04.2009
comment
Верно, но в вашей версии есть несколько тестов == больше инструкций. Однако похоже, что настоящая проблема заключалась в борьбе за циклы ЦП. - person Charlie Martin; 20.04.2009

Вы сравнивали straces обоих? Вы можете попробовать использовать параметр -tt, чтобы получить время системных вызовов.

person jpalecek    schedule 20.04.2009
comment
Мои познания в strace невелики, и я попробовал параметр -tt, и появилась куча цифр, но я не могу понять их значение. - person rfgamaral; 20.04.2009
comment
Попробуйте найти чтение и запись части (вывод должен иметь формат time syscall(parameters) = return value, поэтому найдите read() или write()) и опубликуйте его. - person jpalecek; 20.04.2009