Почему функция libc fwrite быстрее, чем функция записи syscall? - PullRequest
21 голосов
/ 01 сентября 2009

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

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

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

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

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

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

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

Ответы [ 3 ]

34 голосов
/ 01 сентября 2009

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

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

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

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

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

15 голосов
/ 01 сентября 2009

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

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

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

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

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

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

11 голосов
/ 01 сентября 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...