13

Go 和 Java 的性能对比,真的如此吗? | Go 技术论坛

 4 years ago
source link: https://learnku.com/articles/39690?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

/ 312 / 7 / 发布于 10个月前 / 更新于 10个月前

前两天我看到了一篇文章,测试 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 协议》,转载必须注明作者和本文链接
那小子阿伟

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK