2012-06-26 107 views
10

执行下列试验后:write或printf,哪个更快?

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

这取决于您的系统 – JMBise

+3

printf会缓冲。 –

+9

真的吗?你是每次计算字符串长度,然后测量它作为时间的一部分? –

回答

22

怎么,与...事实上,printf本身调用写入,这可能吗?有什么我失踪了吗?

是的,有些东西是你缺少的。 printf不一定每请拨打write。而是,printf缓冲其输出。也就是说,它通常会将其结果存储在内存缓冲区中,缓冲区已满或其他条件时仅调用write

write是一个相当昂贵的调用,将数据复制到printf的缓冲区,因此减少了write呼叫的数量提供了一个净值表现赢昂贵得多。

如果你的标准输出指向一个终端设备,那么每当它看到一个\n时,printf就会调用write - 就你而言,每次调用时都会如此。如果你的stdout被定向到一个文件(或者到/dev/null),那么printf调用只有在其内部缓冲区已满时才会写入。

假设您重定向您的输出,并且printf的内部缓冲区为4K字节,则第一个循环调用write 3000000 /(4096/12)== 8780次。然而,你的第二个循环调用了3000000次write

超越更少的调用的效果write,是大小调用write。硬盘中的存储量是一个扇区 - 通常是512个字节。要写入比扇区少的数据量,可能需要读取扇区中的原始数据,修改它并将结果写回。然而,使用完整扇区调用write可能会更快,因为您不必读入原始数据。 printf的缓冲区大小被选为典型扇区大小的倍数。这样系统可以最有效地将数据写入磁盘。

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

+1

这解释了一切都很好。谢谢!关于你对数据大小的评论......为什么原始数据不能仅仅因为它完全适合该领域而被读取呢?写入调用不需要知道需要写入的数据吗? – Ataraxia

4

你是不是比较苹果和苹果,因为与write运行循环strlen3000000倍,而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,以使您的基准更加公平。

+3

在我的系统上,即使没有优化,gcc-4.5.1在编译时也会评估'strlen'。冲洗/缓冲似乎是造成差异的原因。 –

+0

@DanielFischer谢谢!很高兴知道'gcc'足够聪明,可以将'strlen'表达式折叠成一个常量。 – dasblinkenlight

+2

如果输出被重定向到一个文件,'printf'不会在每个'\ n'上刷新**。此外,更准确地说,每次都写“写”,无内容 - 毕竟,在这种情况下,“刷新”仅仅意味着“调用”写入“。 –