【问题标题】:C++ vs Java speed(Loops with Arithmetic)C++ vs Java 速度(算术循环)
【发布时间】:2018-02-20 03:35:58
【问题描述】:

以下小程序计算 1 到 10 亿的所有数字的总和,我们是用 C++ 和 Java 编写的,尽我所能。我的理解是 C++ 是“更快”的语言,但这段代码的 java 版本在 ~.5 秒内完成,而 C++ 则在 ~3 秒内完成。

C++(GCC 编译器):

int main(){
    long long x = 0;
    for (long i=0;i<1000000001;i++){
    x=x+i;
    }
    cout << x << endl;
    return 0;
}

JAVA:

public class Main {
    public static void main(String[] args)  {
        long x=0;
        for (long i=0;i<1000000001;i++){
            x=x+i;
        }
        System.out.println(x);

    }

}

如何将 C++ 代码优化为与 JAVA 版本一样快?有可能吗?

【问题讨论】:

  • 编译C++代码的时候有没有开启优化?
  • cout &lt;&lt; (1000000001)*(1000000002)/2 &lt;&lt; endl;
  • 这两种语言都没有任何固有的速度优势。两种语言的不同实现可能更快或更慢,答案也可能取决于您可能选择的特定于实现的编译时和/或运行时选项。早在 Java 还很新的时候,only 真正的 Java 实现比在相同硬件上几乎任何 C 实现慢,但那是几十年前的事了。
  • 除了优化编译器可以识别和处理某些循环。所以这更像是建议星际迷航中允许使用传送器的赛车手应该这样做。他们应该这样做。

标签: java c++ performance loops optimization


【解决方案1】:

这个问题很好地说明了不该做什么。整个循环相当于一次赋值,任何优化编译器都知道。因此,您正在测量启动程序并输出一行需要多长时间。

那么 Java 必须因任何您希望的因素而失败,因为运行 Java 代码包括启动 JVM,这非常慢。此外,它还包括优化编译。 javac 所做的只是从 Java 源代码编译为 Java 字节码,并没有尝试优化任何东西。所有优化都发生在运行时(字节码到机器码)。 1

因此我们可以得出结论,对于任何耗时不到几秒的任务,Java 都非常慢。如果您足够努力,您可以获得 20 或无穷大(除以零)的因子。

更重要的结论是它没有意义。如果您想要一个有意义的结果,请参阅How do I write a correct micro-benchmark in Java?


1 这适用于桌面 Java。在 Android 上,情况有所不同。

【讨论】:

    【解决方案2】:

    使用 -O 选项编译 C 代码。

    没有 -O 生成的程序集包含大量内存访问(慢):

    main:
      push rbp
      mov rbp, rsp
      mov QWORD PTR [rbp-8], 0
      mov QWORD PTR [rbp-16], 0
    .L3:
      cmp QWORD PTR [rbp-16], 1000000000
      jg .L2
      mov rax, QWORD PTR [rbp-16]
      add QWORD PTR [rbp-8], rax
      add QWORD PTR [rbp-16], 1
      jmp .L3
    .L2:
    

    使用 -O 生成的程序集仅使用寄存器:

    main:
      mov eax, 1000000001
    .L2:
      sub rax, 1
      jne .L2
    

    查看 Godbolt 的 GCC 浏览器输出:https://godbolt.org/g/rx1Va4

    EDIT:在优化模式下,编译器识别出输出是一个常量,这就是为什么没有添加指令的原因。请参阅 Nathan 的示例输出:https://godbolt.org/g/r1PxvL

    【讨论】:

    • 你的例子有点缺陷,因为你没有做输出。这意味着编译器只是优化循环,因为它不需要在第二个版本中计算值。不过很酷的是,如果您添加 x 的打印输出,您可以看到 gcc 只是在编译时计算值并将一个常量插入到程序集中:godbolt.org/g/r1PxvL
    • @NathanOliver 是的,传送获胜 :)
    【解决方案3】:

    如果您使用优化进行编译,那么 C++ 版本的速度会相当快。

    Java:

    javac Main.java
    
    $ time java Main
    500000000500000000
    
    real    0m0.727s
    user    0m0.724s
    sys     0m0.004s
    

    C++:

    clang -O3 main.cpp -o cpp
    
    $ time ./cpp 
    500000000500000000
    
    real    0m0.003s
    user    0m0.000s
    sys     0m0.000s
    

    我的 Clang 版本:

    $ clang --version
    clang version 4.0.0-1ubuntu1 (tags/RELEASE_400/rc1)
    Target: x86_64-pc-linux-gnu
    Thread model: posix
    InstalledDir: /usr/bin
    

    我的 Java 版本:

    $ javac -version
    javac 1.8.0_144
    

    原因是优化是一个缓慢的过程;如果关闭优化,您将获得更快的编译时间。这对开发来说更好,所以这是 Clang 开发人员选择的默认值。 Java 可能更快,因为它在运行时进行了更多优化。 JVM 字节码与它编译的源代码并没有不同!

    【讨论】:

    • JVM 字节码与源代码一样与速度相关。没有当代 java 源代码到 java 字节码编译器甚至尝试进行 any 优化;它们都发生在运行时。您的 0.727 s 部分是 JVM 启动的时间,部分是编译时间(字节码到机器码)。好的,还有一些执行时间。
    • ...我强烈怀疑 clang 还在编译时将循环转换为常量(就像 gcc 在 this answer 中所做的那样)
    • @Hulk 几乎可以肯定......但这不是重点吗? C++ 提供了一些很棒的工具,因此开发人员不必担心发现这些优化。
    • @sdgfsdh 是的,Java 的 JIT 编译器可能能够动态优化它在运行时实际接收到的数据——这里唯一要了解的是,这个基准测试并没有衡量 OP 的预期测量。
    • @sdgfsdh 当然,这绝对是真的
    猜你喜欢
    • 1970-01-01
    • 2016-07-30
    • 2012-11-07
    • 1970-01-01
    • 2016-06-25
    • 1970-01-01
    • 2021-05-27
    • 2012-03-03
    • 2011-01-16
    相关资源
    最近更新 更多