2012-06-26 3 views
10

После этого следующий тест:написать или распечатать, что быстрее?

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, это возможно? Есть что-то, что мне не хватает?

Оценены все мысли и предложения.

+0

это зависит от вашей системы – JMBise

+3

printf делает буферизацию. –

+9

Действительно? Вы вычисляете длину строки каждый раз, а затем измеряете ее как часть таймингов? –

ответ

22

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

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

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

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

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

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

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

+1

Это объясняет все довольно хорошо. Спасибо! Что касается вашего комментария о размере данных ...почему исходные данные не должны быть прочитаны только потому, что они идеально вписываются в сектор? Разве вызов записи еще не должен знать данные, которые необходимо записать? – Ataraxia

4

Вы не сравниваете яблоки с яблоками, потому что петля с write пробегов strlen3000000 раз, в то время как 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 из обеих строк, чтобы сделать ваши тесты более справедливыми.

+3

В моей системе gcc-4.5.1 оценивает 'strlen' во время компиляции даже без оптимизаций. Кажется, что промывка/буферизация делает разницу. –

+0

@ DanielFischer Спасибо! Приятно знать, что 'gcc' достаточно умен, чтобы сбрасывать выражение 'strlen' в константу. – dasblinkenlight

+2

'printf' делает ** не ** заподлицо на каждом' \ n', если выход перенаправляется в файл. Кроме того, более точно сказать, что 'write' сбрасывается каждый раз, без содержания, в конце концов,« flush »в этом контексте означает просто« называет 'write'." –

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