写或打印,哪个更快?

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选择缓冲区大小为典型扇区大小的倍数.这样,系统可以最有效地将数据写入磁盘.

我希望你的第一个循环比第二个循环快得多.


das*_*ght 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 );
}
Run Code Online (Sandbox Code Playgroud)

另一个重要的区别是printf每次通过时都会刷新\n,而write不会。您应该\n从这两个字符串中删除以使您的基准测试更加公平。

  • 在我的系统上,gcc-4.5.1 即使没有优化也会在编译时评估“strlen”。刷新/缓冲似乎是造成差异的原因。 (3认同)
  • 如果输出重定向到文件,则“printf”不会在每个“\n”上刷新。另外,更准确的说法是“write”每次都会刷新,无论内容如何——毕竟,在这种情况下“flush”仅仅意味着“调用“write”。 (2认同)