为什么这个 Java 程序比对应的 Ada 程序快 1000 倍?

Why is this Java program 1000 times faster than its Ada equivalent?

我有两个程序,它们简单地循环十亿次并递增一个整数。我正在对这两个操作进行计时并比较两个结果。对于 Ada 程序,我使用的是 GNAT FSF 编译器。两个程序都在 Windows 上 运行。我还尝试了 运行 计算每个代码的倍数并对测量的持续时间进行平均,结果相同。

我预计会发生两件事,要么我的 Ada 程序没有正确编写(我对这门语言很陌生),要么它编写正确,但 Java 编译器正在做我不知道的代码内容。

这是 Ada 程序:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure Main is
    c : Integer := 1;

    startTime, endTime : Time;
    milliS : Duration;
begin
    startTime := Clock;
    while c <= 1000000000 loop
        c := c + 1;
    end loop;

    endTime := Clock;
    milliS := (endTime - startTime) * 1000;

    put_line("Runtime = " & Duration'Image(milliS) & " milliseconds.");
end Main;

和Java代码:

public class Test {

    public static void main(String[] args) {
        int c = 1;
        long start = System.nanoTime();

        while (c<=1000000000) {
            c = c + 1;
        }

        long stop = System.nanoTime();

        float duration = (float) ((start - stop)/1000000.0);
        System.out.println(duration);
    }
}

Ada 直接编译为机器码,所以我预计它会比 Java 程序快。

Java JIT 编译器足够聪明,可以意识到循环可以被优化掉。它就是这样做的。

如果您修改 Java 版本以在最后打印出 c 的值,您将获得与(未优化的)Ada 版本大致相当的执行时间。如果使用 c 的值,则无法将循环优化掉 1

Ada compiles straight to machine code, so I expected it to be faster than the Java program.

Java JIT 编译器也可以编译为机器代码,但不会立即编译。


1 - 直到我们得到一个 omniscient JIT 编译器,它意识到我们不关注输出:-)

我无法重现你的问题。

我用

构建了你的程序
$ gnatmake -gnata -gnato -fstack-check -gnat12 -gnatyO -gnatv -gnati1 -gnatf -gnatn -O3 main.adb
gcc-6 -c -gnata -gnato -fstack-check -gnat12 -gnatyO -gnatv -gnati1 -gnatf -gnatn -O3 main.adb

GNAT 6.3.0 20170516
Copyright 1992-2016, Free Software Foundation, Inc.

Compiling: main.adb
Source file time stamp: 2018-08-01 08:20:21
Compiled at: 2018-08-01 10:20:30
 19 lines: No errors
gnatbind-6 -x main.ali
gnatlink-6 main.ali -fstack-check -O3
$

然后我运行程序使用time检查执行时间:

$ time ./main
Runtime =  0.002000000 milliseconds.
./main  0,00s user 0,00s system 82% cpu 0,005 total
$

如果您不进行优化,那么 Ada 编译器将尝试确保您在源文本和机器代码之间尽可能接近匹配,以便轻松证明您的可执行文件执行正确的操作. - 在这种情况下,您当然应该在循环中进行 1_000_000_000 次迭代。