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 每次都打电话。相反,printf缓冲其输出。也就是说,它通常将其结果存储在内存缓冲区中,仅write在缓冲区已满时或在某些其他条件下调用。

write是一个相当昂贵的调用,比将数据复制到printf's 缓冲区要昂贵得多,因此减少write调用次数可以带来净性能提升。

如果您的标准输出被定向到终端设备,那么每次它看到一个- 在您的情况下,每次调用它时都会调用printf。如果您的 stdout 被定向到文件(或),则仅在其内部缓冲区已满时才调用 write。write\n/dev/nullprintf

假设您正在重定向输出,并且printf内部缓冲区为 4Kbytes,那么第一个循环调用write3000000 / (4096 / 12) == 8780 次。但是,您的第二个循环调用了write3000000 次。

除了调用次数减少的影响之外,调用次数write大小也是如此write。硬盘中的存储量是一个扇区——通常是 512 字节。要写入比扇区更少的数据量,可能需要读取扇区中的原始数据,对其进行修改,然后将结果写回。但是,使用完整扇区调用write可能会更快,因为您不必读取原始数据。 printf的缓冲区大小选择为典型扇区大小的倍数。这样系统可以最有效地将数据写入磁盘。

我希望您的第一个循环比第二个循环快得多。

于 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 );
}

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

于 2012-06-26T17:48:54.947 回答