13

次のテストを行った後:

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秒かかりますが、writeの呼び出しにはなんと46秒かかります。どのように、それが行うすべての派手なフォーマットの魔法と、それ自体が呼び出すprintfという事実で、これは可能ですか?足りないものはありますか?printfwrite

ありとあらゆる考えとインプットに感謝します。

4

2 に答える 2

31

どのように...printf自体がwriteを呼び出すという事実で、これは可能ですか?足りないものはありますか?

はい、足りないものがあります。毎回printf呼び出すとは限りません。むしろ、その出力をバッファリングします。つまり、多くの場合、結果をメモリバッファに格納し、バッファがいっぱいになったとき、またはその他の条件でのみ呼び出します。write printfwrite

writeはかなり高価な呼び出しであり、データをのバッファにコピーするよりもはるかに高価でprintfあるため、呼び出しの数を減らすとwrite、正味のパフォーマンスが向上します。

stdoutが端末デバイスに送信されている場合は、-が表示されるたびにprintf呼び出します。この場合、呼び出されるたびに呼び出します。stdoutがファイル(またはに)に向けられている場合、内部バッファがいっぱいになったときにのみwriteを呼び出します。write\n/dev/nullprintf

出力をリダイレクトしていて、その内部バッファが4Kバイトであるとすると、最初のループは3000000 /(printf4096/12)==8780回呼び出します。writeただし、2番目のループはwrite3000000回呼び出します。

への呼び出しが少ないという効果を超えて、への呼び出しwriteサイズwriteがあります。ハードドライブのストレージの量はセクターであり、多くの場合512バイトです。セクターよりも少量のデータを書き込むには、セクター内の元のデータを読み取り、それを変更して、結果を書き戻す必要があります。writeただし、元のデータを読み込む必要がないため、完全なセクターでの呼び出しは高速になる可能性があります 。printfのバッファサイズは、通常のセクターサイズの倍数になるように選択されます。これにより、システムは最も効率的にデータをディスクに書き込むことができます。

最初のループは2番目のループよりもはるかに速く進むと思います。

于 2012-06-26T18:34:37.430 に答える
5

write実行strlen 3000000時間のあるループは実行されないため、リンゴとリンゴを比較しprintfていません。書式設定も行わないため、「派手な書式設定の魔法」はほとんど適用されません。

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

もう1つの重要な違いは、printf通過するたびにフラッシュしますが、通過\nwriteないことです。\nベンチマークをより公平にするために、両方の文字列から削除する必要があります。

于 2012-06-26T17:48:54.947 に答える