为什么在这种情况下Java比C语言更快(或更慢)?

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倍以上,太慢了,让人惊讶。考虑到字符串对象的创建、垃圾收集、-server等问题。当我发现实际上几乎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 的启动时间)?

6
在控制台打印一些数据真的不是科学基准。这只会遇到I/O瓶颈,而不能测试Java和C的速度。 - Tobias P.
1
你有没有和没有格式化字符串的C版本进行比较? - zacheusz
我猜缓存和写操作数量在这里有很大的影响,至少对于第三个问题是如此。根据我所知,您仅写入一次,而 C 版本则会多次写入。 - You
这表明,一个写得好的Java应用程序将比一个写得不太好的C程序运行得更快。如果你将Java和C都调整到最大,通常C程序会很快,因为你有更多的选项(这需要更多的知识和实验)。然而,对于适度的努力和知识,不能保证C比Java更快。 - Peter Lawrey
2个回答

5
为什么从C到Java的字面转录(即PrintStream)如此缓慢?
您正在对System.out和各种stdio(C vs Java)实现进行基准测试。
为什么注释掉的printf()如此缓慢[可能随时间性能下降]?
将浮点数转换为字符串(反之亦然)是一项非常复杂且缓慢的操作。请查看glibc源代码。
最后:为什么我的解决方案比C(包括JVM启动时间)更快?
因为您正在进行基准测试并比较不同的内容:
一个Java代码,它执行整数转换为字符串(由于明显原因而比浮点数快得多)和一些微不足道的字符串操作,希望Java VM可以很好地完成,再加上out.println()
与运行解释语言的C代码在每个循环迭代上(是的,printf是一个小语言的解释器,其中“%d \ t%6.2f \ n”是一个程序),并进行浮点到字符串的转换(本身比整数慢得多),再加上stdio。
不清楚C版本是否使用单精度还是双精度。

你是对的,使用浮点数转换为字符串时,Java 比 C 落后。 - tcn

3

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


Java之所以胜出,是因为有缓存机制。Java代码只应该在out.close()处打印一次,而C代码则会多次打印。 - You

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接