为什么在Go vs Java中运行此代码要花费更长的时间

Cug*_*uga 1 java performance go

最近一直在挑选Go,我是一个忠实的粉丝,来自Java背景.

我正在以不同的方式比较这些语言,并且惊讶于一个简单的循环计数高达200亿在Golang和Java中花了相当长的时间.

想知道是否有人可以提供任何见解,如果我在这里遗漏了什么.这就是我做的:

Java的

写下面的代码,从普通main()方法执行它,用Gradle构建一个可执行jar,并使用以下命令从命令行执行它:java -jar build/libs/my-executable.jar

private void countToTwentyBillion() {
    long count = 0;
    long start = System.currentTimeMillis();
    for (int k = 0; k < 10; k++) {
        System.out.println("On step " + k);
        for (int i = 0; i < 2_000_000_000; i++) {
            // Do nothing but count
            count++;
        }
    }
    long end = System.currentTimeMillis();
    System.out.println("Total time took: " + (end - start) + " ms to get at count: " + count);
}
Run Code Online (Sandbox Code Playgroud)

在3个独立的试验中,我得到了以下结果:

// Total time took: 396 ms to get at count: 20000000000
// Total time took: 393 ms to get at count: 20000000000
// Total time took: 388 ms to get at count: 20000000000
// 392 ms average
Run Code Online (Sandbox Code Playgroud)

在Go中构建此文件,使用"go build"构建它并在命令行执行 ./loop-counter

package main

import (
    "fmt"
    "time"
)

func main() {
    count := 0

    nanos := time.Now().UnixNano()
    start := nanos / 1000000

    for i := 0; i < 10; i++ {
        fmt.Printf("On step %d\n", i)
        for k := 0; k < 2000000000; k++ {
            count++
        }
    }
    nanos = time.Now().UnixNano()
    end := nanos / 1000000

    timeLength := end - start

    fmt.Printf("Total time took: %d ms to get at count: %d\n", timeLength, count)

}
Run Code Online (Sandbox Code Playgroud)

经过3次单独的试验,我得到了以下结果:

// Total time took: 5812 ms to get at count: 20000000000
// Total time took: 5834 ms to get at count: 20000000000
// Total time took: 5907 ms to get at count: 20000000000
// 5,851 ms average
Run Code Online (Sandbox Code Playgroud)

我进入了这个期待Go更快,最终感到惊讶.所有试验均在相同条件下的同一台机器上进行.

任何人都可以说出什么给出

谢谢

lwi*_*lwi 8

我不是Go专家,但java确实优化了循环.

假设您有一个单核处理器3Ghz,每条指令给出0.3ns,假设每个增量都是一条指令.所以0.3ns *20 bilion = 6s是一个会粗略估计没有就地任何优化的性能.

您可以通过提供-XX:LoopUnrollLimit=1给您的程序来验证java在这里做了些什么.这告诉JVM几乎不进行循环展开,因此可以防止大多数JIT优化在您的示例中发生.

这样做,你的java示例的运行时现在6s在我的机器上,可与Go基准相媲美.

可能还有一个选项可以在Go版本中启用循环展开等优化(请参阅Go手册).

最后,这再次表明,基准测试很难实现.他们经常欺骗自己去做错误的事情.