3

一些人刚开始通过阅读 K&R 学习 C,并在第一页上打印了它的华氏到摄氏度的转换循环:

#include <stdio.h>

main ()                                                                                                                                                       
{
  int fahr;                                                                                                                                                    

  for (fahr = 0; fahr<= 200000000; fahr = fahr + 20)                                                                                                                                                                                
    printf("%d\t%6.2f\n", fahr, (5.0 / 9.0) * (fahr-32));                                                                                                                                                                                       
}   

他被告知 Java 速度很慢。所以,告诉他 Java 现在非常有竞争力,但在这种简单的情况下,C 可能会更快。想证明他,基本上添加了“System.out”。在 printf() 前面。

它慢了 10 倍以上。太多了。我很困惑。想到了String对象的创建,GC,-server,yada,yada,yada。

当我发现几乎 100% 的时间实际上都花在了 printf() 上时,我更加困惑了(PrintSteam.write()输出通过管道传输到 /dev/null)。

经过一番摆弄,我想出了这个(现在不做 %f 的四舍五入):

public static void main(String... args) throws Exception {                                                                                                       
   int fahr=0;                                                                                                                                                    

    PrintWriter out = new PrintWriter(Channels.newWriter(Channels.newChannel(System.out), "US-ASCII") );                                                                                                

    int max = 2000000000;                                                                                                                                         
    for (fahr = 0; fahr<= max; fahr = fahr + 20)
      // out.printf("%d\t%6.2f\n", fahr, (5.0 / 9.0) * (fahr-32));                                                                                                       
      out.println( fahr + "\t" + f(((5.0 / 9.0) * (fahr-32)) ));                                                                                                        

    out.close();                                                                                                                                            
 }                                                                                                                                                                

 private static final String f(double d) {                                                                                                                         
      return (int)d + "." + (int)((d - (int)d)*100);                                                                                                              
 }                                                                                                                                                           
} 

所以,这使用了 NIO。它在两台经过测试的机器上优于gcc -O2。

问题:

  • 为什么从 C 到 Java(即PrintStream)的文字转录如此缓慢?
  • (为什么评论out.printf()这么慢[也许性能会随着时间的推移而下降]?)
  • 最后:为什么我的解决方案比 C 更快(包括 JVM 启动时间)?
4

2 回答 2

5

为什么从 C 到 Java(即 PrintStream)的文字转录这么慢?

您正在对 System.out 和各种 stdio(C 与 Java)实现进行基准测试

(为什么注释 out.printf() 这么慢[也许性能会随着时间的推移而下降]?)

将浮点数转换为字符串(反之亦然)是一项非常复杂且缓慢的操作。看看 glibc 源代码。

最后:为什么我的解决方案比 C 更快(包括 JVM 启动时间)?

因为您正在对不同的事物进行基准测试和比较:

  • 一个将整数转换为字符串的 java 代码(由于显而易见的原因,它比浮点快得多)和一些琐碎的字符串操作,希望 java VM 可以做得很好,加上 out.println()
  • 针对在每次循环迭代时运行解释语言的 C 代码(是的,printf 是一种小型语言的解释器,其中 "%d\t%6.2f\n" 是一个程序)并执行浮点到字符串的转换(它本身比整数慢很多倍),加上stdio。

目前尚不清楚 C 版本是使用单精度还是双精度。

于 2011-07-23T11:54:20.277 回答
3

基本上你的实验表明,这里的计算完全无关紧要,绝大多数时间都花在格式化和打印输出上。因此,您不是在测试 C 与 Java 作为语言的性能,而是测试不同的字符串格式化库代码(C 似乎优化得更好)以及 stdout 如何连接到实际控制台(Java 在这里胜出)。

于 2011-07-23T11:44:07.593 回答