Ata*_*xia 12 c unix io performance system-calls
完成以下测试后:
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" ) );
}
Run Code Online (Sandbox Code Playgroud)
事实证明,对printf的调用总共需要3秒钟,而写入调用需要花费46秒.如何,所有花哨的格式化魔法printf,以及它printf本身所称的事实,write这可能吗?有什么东西我不见了吗?
任何和所有的想法和意见都表示赞赏.
Rob*_*obᵩ 28
如何,以及... printf本身调用write的事实,这可能吗?有什么东西我不见了吗?
是的,有些东西你不见了.printf并不一定write 每次都打电话.相反,printf缓冲其输出.也就是说,它通常将其结果存储在内存缓冲区中,仅write在缓冲区已满时调用,或者在某些其他条件下调用.
write是一个相当昂贵的调用,比将数据复制到printf缓冲区要昂贵得多,因此减少write调用次数可以获得净性能.
如果你的stdout被定向到一个终端设备,那么每次看到它时都会printf调用- 在你的情况下,每次调用它时.如果将stdout定向到文件(或),则仅在其内部缓冲区已满时调用write.write\n/dev/nullprintf
假设您正在重定向输出,并且printf内部缓冲区是4K字节,则第一个循环调用write3000000 /(4096/12 )== 8780次.然而,你的第二个循环调用write3000000次.
除了少调用的效果write,是大小的调用的write.硬盘中的存储量是一个扇区 - 通常是512字节.写入比扇区少的数据可能涉及读取扇区中的原始数据,修改它,并将结果写回.write但是,调用完整的扇区可能会更快,因为您不必读取原始数据.  printf选择缓冲区大小为典型扇区大小的倍数.这样,系统可以最有效地将数据写入磁盘.
我希望你的第一个循环比第二个循环快得多.
您不是在比较苹果与苹果,因为循环write运行strlen 3000000时间,而printf不执行任何操作;它也不进行任何格式化,因此“花哨的格式化魔法”几乎不适用。
size_t len = strlen( "Test string\n" );
for( i = 0; i < 3000000; i++ ) {
    write( STDOUT_FILENO, "Test string\n", len );
}
Run Code Online (Sandbox Code Playgroud)
另一个重要的区别是printf每次通过时都会刷新\n,而write不会。您应该\n从这两个字符串中删除以使您的基准测试更加公平。