Write или printf, что быстрее?

После выполнения следующего теста:

for( i = 0; i < 3000000; i++ ) {
    printf( "Test string\n" );
}

for( i = 0; i < 3000000; i++ ) {
    write( STDOUT_FILENO, "Test string\n", strlen( "Test string\n" ) );
}

получается, что вызовы printf занимают в общей сложности 3 секунды, а призывы к записи - колоссальные 46 секунд. Как, при всей фантазии магии форматирования, что printf делает, и тот факт, что printf сам называет write, возможно ли это? Есть что-то, что мне не хватает?

Приветствуются любые мысли и ввод.

Ответ 1

Как, с... тем фактом, что printf сам вызывает запись, возможно ли это? Есть что-то, что мне не хватает?

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

write - довольно дорогостоящий вызов, намного более дорогостоящий, чем копирование данных в буфер printf, поэтому уменьшение количества вызовов write обеспечивает чистый выигрыш в производительности.

Если ваш stdout направлен на терминальное устройство, то printf вызывает write каждый раз, когда видит \n - в вашем случае, каждый раз, когда он вызывается. Если ваш stdout направлен на файл (или на /dev/null), то printf вызывает запись только при заполнении внутреннего буфера.

Предположим, что вы перенаправляете свой вывод и что внутренний буфер printf составляет 4 Кбайта, тогда первый цикл вызывает write 3000000/(4096/12) == 8780 раз. Однако ваш второй цикл вызывает write 3000000 раз.

Помимо меньшего количества вызовов на write, это размер вызовов write. Квантом хранения на жестком диске является сектор - часто 512 байт. Чтобы написать меньший объем данных, чем сектор, может потребоваться считывание исходных данных в секторе, его изменение и запись результата. Однако вызов write с полным сектором может ускориться, поскольку вам не нужно читать исходные данные. printf размер буфера выбран как кратный типичному размеру сектора. Таким образом, система может наиболее эффективно записывать данные на диск.

Я ожидаю, что ваш первый цикл пройдет намного быстрее, чем второй.

Ответ 2

Вы не сравниваете яблоки с яблоками, потому что цикл с write работает strlen 3000000 раз, а printf не делает ничего из этого; он также не делает никакого форматирования, поэтому "магия фантазии магии" вряд ли применима.

size_t len = strlen( "Test string\n" );
for( i = 0; i < 3000000; i++ ) {
    write( STDOUT_FILENO, "Test string\n", len );
}

Другим важным отличием является то, что printf сбрасывается каждый раз, когда вы проходите \n, а write - нет. Вы должны удалить \n из обеих строк, чтобы сделать ваши тесты более справедливыми.