Go 和 Java 的性能对比,真的如此吗?

前两天我看到了一篇文章,测试Java和Go和Python的性能,其测试内容是一个排序,排序一亿次,然后看那个语言耗时最短,我先贴一下这个文章的测试结果,Java竟然比Go快了一倍不止,Go不是号称接近C的性能吗,难道?结尾我会把我看的这篇文章链接共享出来,接下来听我分析,

Java

准备测试代码

Java测试代码

可以看的出来逻辑很简单,对一个固定数组排序,for循环执行一亿次,记录总的耗时时间,代码和我看过的文章代码一致。

public static void main(String[] args) {
    //记录开始时间
    long start = System.nanoTime();
    int num = 100000000;
    for (int i = 0; i < num; i++) {
        BubbleSort(1, 2, 3, 4, 5, 6, 7, 8, 9);
    }
    //打印耗时时间
    System.out.println(System.nanoTime() - start);
}
//排序
public static void BubbleSort(int... arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] < arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}
Go测试代码

和Java的功能是一样的,也是一亿次排序,代码和我看过的文章代码一致。

func Sort() {
   start := time.Now().UnixNano()
   var arr []int
   const NUM int = 100000000
   for i := 0; i < NUM; i++ {
      arr = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
      bubbleSort(arr)
   }
   //打印消耗时间
   fmt.Println(time.Now().UnixNano() - start)
}
//排序
func bubbleSort(arr []int) {
   for j := 0; j < len(arr)-1; j++ {
      for k := 0; k < len(arr)-1-j; k++ {
         if arr[k] < arr[k+1] {
            temp := arr[k]
            arr[k] = arr[k+1]
            arr[k+1] = temp
         }
      }
   }
}

我们分别执行上面这两段代码,看看结果到底多少呢,我的本地环境如下:
Java : jdk1.8 GoLang :1.12
i7处理器,16G内存,Windows10系统

1.Java结果:3263111300 ns = 3263 ms = 3.2 s
2.Go结果: 7165483700 ns = 7165 ms = 7.1 s

看到这个结果你信了吗? Java比Go的性能要好,快了一倍不止,我以前看到的文章难道都欺骗了我吗?

解密开始

仔细观察两段代码,其实是有一些细微区别的,有时候一点点的细微区别导致的结果千差万别,甚至让你得出一个错误结论从而误导你,看下面Go的代码,这个代码片段是在Sort方法中出现的,我们看到有一个arr变量,这个变量并没有在for循环中定义,而是在for循环外定义的,在for循环里面不断被重新赋值。

var arr []int
const NUM int = 100000000
for i := 0; i < NUM; i++ {
   arr = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
   bubbleSort(arr)
}

将上面的代码改成如下:

const NUM int = 100000000
for i := 0; i < NUM; i++ {
   arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
   bubbleSort(arr)
}

完成之后,我们再次测试,结果如下,这下心里稍微平衡一点了,要不然我一直热爱的Go语言竟然性能如此之差,怎么受得了呢。那么为什么就改了一行代码,差距如此之大。
1.Java结果:3263111300 ns = 3263 ms = 3.2 s
2.Go结果: 4137247700 ns = 4137 ms = 4.1 s

这其实设计到Go的变量分配问题,Go内存分两种堆和栈,一个变量要么被分配在堆上,要么分配在栈上。
:由 GC 负责回收。对应于进程地址空间的堆
:不涉及 GC 操作。每个 goroutine 都有自己的栈,初始时被分配在进程地址空间的栈上,扩容时被分配在进程地址空间的堆上。

我们这里的arr变量是一个局部变量,那么到底Go将它分配在哪里呢?,我们对这两段代码做反编译分析,写一个main.go文件,在main函数中分别调用两个排序函数,然后执行这个命令:go tool compile -m main.go,得到结果如下,其中第一个圈红的是修改前的代码arr变量是被分配在堆上,修改后的代码arr变量是被分配在栈上,这是Go自主优化的,是怎么确定呢?Go通过做逃逸分析得出的,相信大家已经明白一些了,关于变量分配就说到这里,这个话题很深,可以聊很久,后面公众号会单独谈这个问题的,现在能说明白问题就行。

Go和Java的性能对比,真的如此吗?

事实上,如果你再深入一下,你对Java和Go的for循环次数调整一下,比如都调整为循环一千次,你再比较结果,你会发现Go的性能比Java的好,为什么呢?我提个醒,你可以从GC和Java的JIT优化方面思考一下。

再有如果对Go的bubbleSort方法改为指针传递,如下,那么Go的执行性能又将如何,你可以试一试,留言区讨论。

func bubbleSort(arr *[]int) {
}

写在最后

抛开应用场景去谈性能都是耍流氓,每个语言都有自己的应用场景,有编译时,运行时的优化等,单单靠一个排序函数的结果探讨Java和Go的性能是错误的。网上也有很多实验来说明Go的性能是Java的好几倍,希望你见到时好好思考一下,这个测试是客观的吗?为什么会出现这个我意料之外的结果?

我看到的文章链接:https://studygolang.com/articles/25933

觉得有用大家关注一下公众号啊,微信搜索 “技术人技术事” 就可以了,阅读更多精彩文章
Java

本作品采用《CC 协议》,转载必须注明作者和本文链接
那小子阿伟
讨论数量: 9
Summer

Java 胜在编译器很成熟,Go 多年后应该能追赶上哈

4年前 评论

@Summer 大佬厉害,一阵见血。 :+1:

4年前 评论

求知若渴👍👍

4年前 评论

@pigzzz 大佬关注下公众号呗 :smiley:

4年前 评论
pigzzz 4年前

实测 func bubbleSort(arr *[]int) 会更慢:

100000000 次:慢1s

10000000 次:慢100ms

1000000 次: 慢10ms

100000 次: 慢1-2ms

4年前 评论

一开始把golang的循环少写了个0,导致结果是golang比java快了6倍多,很是吃惊,还好后来发现了!!!实测循环小于百万时,golang是比java快的,百万级以后java速度稳定在golang的1.5倍左右。

4年前 评论

emmm 不是引战。反正我认为不出3年 Golang的性能必定吊打Java
引用这个网站上的测试:
https://benchmarksgame-team.pages.debian.n...

4年前 评论
bronya0 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!