2009-09-01 2 views
20

После предоставления той же программы, которая считывает случайный сгенерированный входной файл и эхо-сигнала той же строки, которую он считывает на выходе. Единственное различие заключается в том, что с одной стороны я предоставляю методы чтения и записи из системных вызовов linux, а с другой стороны я использую fread/fwrite.Почему функция fwrite libc быстрее, чем функция записи в syscall?

Сроки ввода приложения с размером 10 МБ и эхо его/dev/null, и убедитесь, что файл не кэширован, я обнаружил, что fwrite libc быстрее по шкале LARGE при использовании очень маленьких буферов (1 байт в случае).

Вот мой выход от времени, используя FWRITE:

real 0m0.948s 
user 0m0.780s 
sys  0m0.012s 

И с помощью системного вызова записи:

real 0m8.607s 
user 0m0.972s 
sys  0m7.624s 

Единственная возможность, что я могу думать о том, что внутренне Libc уже буферные мой вход ... К сожалению, я не мог найти такую ​​информацию в Интернете, поэтому, возможно, гуру здесь могут помочь мне.

+4

«внутренне libc уже выполняет буферизацию ввода». Это именно то, что он делает. Возможно, вы даже можете прочитать исходный код libc, если хотите, и посмотреть, как он это делает. – kquinn

ответ

29

Timing мое приложение с входом 10Mb в размере и вторя его /Dev/нуль, и убедившись, что файл в не кэшируются, я обнаружил, что frwite LibC является быстрее БОЛЬШОЕ шкала, когда с использованием очень маленьких буферов (1 байт в кейс).

fwrite работает на потоках, которые буферизуются. Поэтому многие небольшие буферы будут быстрее, потому что он не будет запускать дорогостоящий системный вызов до тех пор, пока буфер не заполнит (или вы его не закроете или не закроете). С другой стороны, небольшие буферы, отправляемые на write, будут выполнять дорогостоящий системный вызов для каждого буфера - вот где вы теряете скорость. С буфером потока 1024 байта и написанием буферов с 1 байтом вы смотрите на 1024 write звонков для каждого килобайт, а не 1024 fwrite звонки превращаются в один write - см. Разницу?

Для больших буферов разница будет небольшой, потому что будет меньше буферизации и, следовательно, более последовательное количество системных вызовов между fwrite и write.

Другими словами, fwrite(3) - это всего лишь библиотечная процедура, которая собирает выходные данные в куски, а затем вызывает write(2). Теперь write(2), является системным вызовом который ловушки в ядро ​​. Это то, что происходит на самом деле. Есть некоторые накладные расходы для простого вызова в ядро, а затем есть время, которое требуется, чтобы написать что-то. Если вы используете большие буферы, вы обнаружите, что write(2) быстрее, потому что в конечном итоге его нужно вызывать в любом случае, и если вы пишете одно или несколько раз на fwrite, то накладные расходы на флериту будут такими: больше накладных расходов.

Если вы хотите узнать больше об этом, вы можете посмотреть на this document, что объясняет стандартные потоки ввода-вывода.

10

Вы также можете отключить буферизацию с помощью функции setbuf(). Когда буферизация отключена, функция fwrite() будет медленнее, чем write(), если не медленнее.

Более подробную информацию по этому вопросу можно найти здесь: http://www.gnu.org/s/libc/manual/html_node/Controlling-Buffering.html

14

write (2) является фундаментальной операцией ядра.

fwrite (3) - это функция библиотеки, которая добавляет буферизацию поверх write (2).

Для небольших (например, по времени) байтов счетчика, fwrite (3) быстрее, из-за накладных расходов, просто выполняющих вызов ядра.

Для больших байтов байтов ввода-вывода write (2) работает быстрее, потому что он не беспокоит буферизацию, и вы должны вызвать ядро ​​в обоих случаях.

Если вы посмотрите на источник на cp (1), вы не увидите никакой буферизации.

Наконец, есть последнее соображение: ISO C vs Posix. Буферизованные функции библиотеки, такие как fwrite, указаны в ISO C, тогда как вызовы ядра, такие как write, являются Posix. Хотя многие системы требуют Posix-совместимости, особенно при попытке получить право на государственные контракты, на практике это специфично для Unix-подобных систем. Таким образом, буферизованные операционные системы более переносимы. В результате Linux cp, безусловно, будет использовать write, но программа на C, которая должна работать на межплатформенной платформе, возможно, придется использовать fwrite.

+0

У меня недавно было интервью, и я дал то же рассуждение о различиях b/w write и fwrite, и ответ, который я получил, «вы знаете об этом различии полностью фиктивным» !!. Интервьюер казался мне очень высокомерным. Тем не менее, я просто хотел подтвердить, если есть какая-либо другая разница между вызовами, сделанными с помощью glibc и вызовов, сделанных непосредственно в ядро? –

+0

@PK, я обновил свой ответ ... – DigitalRoss

Смежные вопросы