完成以下测试后:
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 :(得分:27)
如何,以及... printf本身调用write的事实,这可能吗?有什么东西我不见了吗?
是的,有些东西你不见了。 printf
不一定每次都会调用write
。相反,printf
缓冲其输出。也就是说,它通常将其结果存储在内存缓冲区中,仅在缓冲区已满时调用write
,或者在其他条件下调用。{/ p>
write
是一个相当昂贵的调用,比将数据复制到printf
的缓冲区要昂贵得多,因此减少write
调用的数量可以获得净效果。
如果您的标准输出被定向到终端设备,那么printf
每次看到write
时都会调用\n
- 在您的情况下,每次调用它时。如果你的stdout被定向到一个文件(或/dev/null
),那么printf
只在内部缓冲区已满时调用write。
假设您正在重定向输出,并且printf
的内部缓冲区是4K字节,则第一个循环调用write
3000000 /(4096/12)== 8780次。但是,第二个循环调用write
3000000次。
除了write
的通话次数减少之外,write
的调用的大小。硬盘中的存储量是一个扇区 - 通常是512字节。写入比扇区少的数据可能涉及读取扇区中的原始数据,修改它,并将结果写回。但是,为完整扇区调用write
可能会更快,因为您不必读取原始数据。 printf
的缓冲区大小选择为典型扇区大小的倍数。这样,系统可以最有效地将数据写入磁盘。
我希望你的第一个循环比第二个循环快得多。
答案 1 :(得分:4)
你不是在比较苹果和苹果,因为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
,以使您的基准更加公平。