为什么Go语言在性能上比Java慢?

115

从2010年的计算机语言基准测试游戏可以看出:

  • Go平均比C慢10倍
  • Go比Java慢3倍 !?

考虑到Go编译器生成用于执行的本机代码,这可能是怎么回事呢?
是Go编译器不够成熟吗?还是Go语言存在某些内在问题?

编辑:
大多数答案都否认了Go语言内在的缓慢性,声称问题存在于不成熟的编译器中。
因此,我进行了一些自己的测试来计算斐波那契数列:迭代算法在Go中运行(freebsd、6g)与C中(使用O3选项)具有相同的速度。愚蠢的递归算法在Go中运行比C慢 2倍(使用-O3选项;使用-O0-相同)。但我没有看到像基准测试游戏中那样的10倍下降。


38
公正地说,C 语言是伪装成汇编语言的,而 Java 在这些天下面有一些严重的优化。 - Matthew Scharley
16
也许基准测试也不能反映出 Go 语言的优势,可能其他基准测试比这个更快。此外,通常代码的可读性比性能更重要。 - extraneon
7
我同意。请记住,Go语言是为Google公司设计的,而Google公司通常会在200万个核心上运行代码。我认为参加基准测试游戏(Benchmarks Game)的只有4个核心。 - Jörg W Mittag
5
你的问题假设过多:“大多数回答否认Go语言固有的缓慢性”是一个不正确的短语用于问题中。你是要提出一个问题,还是要发表一个声明?请参见http://www.c2.com/cgi/wiki?HostileStudent以了解你的错误。 - Chris
4
目前基准测试表明,大多数情况下,Go程序的性能比C代码慢了约两倍(与Java相比,速度大致相同,但内存使用更少)。 - fuz
显示剩余12条评论
10个回答

104

6g和8g编译器并不是特别优化,因此它们生成的代码并不是特别快。

它们被设计成自己运行得很快,并且生成的代码还可以(有一点优化)。gccgo使用GCC现有的优化传递,可能会与C提供更有意义的比较,但是gccgo还没有完全实现所有功能。

基准测试数据几乎完全取决于实现质量。除了在运行时支持基准测试实际上并不需要的语言功能之外,它们与语言本身几乎没有什么关系。在大多数编译语言中,一个足够聪明的编译器理论上可以剥离不需要的内容,但是当你开始妥协演示时,你会发现很少有真正使用该语言的用户会编写不使用该功能的程序。将东西移到一边而不完全删除它们(例如,在JIT编译的Java中预测虚拟调用目标)开始变得棘手。

FWIW,在我自己非常微不足道的Go测试中(基本上是整数加法的循环),与等效的C相比,gccgo生成了一端接近gcc -O0gcc -O2 之间的快速代码。Go本质上并不慢,但编译器还没有完成所有工作。对于一个只有10分钟历史的语言来说,这并不奇怪。


7
此外,可能由于在“计算机语言基准测试游戏”中的Go程序并未像C和Java那样进行过优化。 - el.pescado - нет войне
我不同意你的说法:“基准测试数据几乎完全取决于实现的质量,与语言本身关系不大……” - user811773
1
@xitrium:我认为Go的意图是实现不需要强制进行协作调度,如果他们想要,他们可以进行抢占。例如,请参见https://code.google.com/p/go/issues/detail?id=543,该问题尚未关闭为“荒谬的,修复这个所谓的错误将违反Go语言定义”,如果禁止Go实现进行抢占,那么它应该是这样的 :-) 该问题的复杂性在于,默认情况下,Go仅使用单个主机线程,无论有多少goroutine可运行。 - Steve Jessop
6
答案可能有点过时了。最近,Go 1.1的第一个beta版已经发布(http://tip.golang.org/doc/go1.1#performance),他们声称编译程序的性能提高了约30%至40%。请有人再次进行这些测试。 - fuz
@fuz 基准测试比赛的测量结果已针对每个 Go 版本进行更新。 - igouy
显示剩余3条评论

53
Go FAQ的下一个版本中,将会出现类似以下内容的东西。
性能
为什么Go在基准测试X上表现很差?
Go的设计目标之一是对于可比较的程序接近C的性能,但在一些基准测试中它表现得相当糟糕,包括test/bench中的几个。最慢的依赖于库,而这些库在Go中没有可比性能的版本。例如,pidigits依赖于多精度数学包,而C版本(不像Go版本)使用了GMP(它是用优化汇编语言编写的)。依赖于正则表达式的基准测试(例如regex-dna)本质上是将Go的临时regexp包与成熟的、高度优化的正则表达式库(如PCRE)进行比较。
基准测试游戏需要广泛调整,大多数基准测试的Go版本需要关注。如果你测量可比较的C和Go程序(reverse-complement就是一个例子),你会发现这两种语言在原始性能方面要比这个套件所示的要接近得多。
尽管如此,还有改进的空间。编译器很好但可以更好,许多库需要进行重大的性能工作,垃圾收集器还不够快(即使它快了,注意不生成不必要的垃圾可能会产生巨大的影响)。

以下是最近邮件列表中关于计算机基准测试游戏的更多详细信息。

gccgo中的垃圾回收和性能(1)

gccgo中的垃圾回收和性能(2)

需要注意的是,计算机基准测试游戏只是一种游戏。有经验的性能测量和容量规划人员会仔细匹配实际工作负载上的相似点,而不是玩游戏。


1
这里是同一讨论串中你排除的一些细节 - http://groups.google.com/group/golang-nuts/msg/2e568d2888970308 - igouy
3
需要注意的是,“基准测试是一种骗局”——不仅仅是作为基准游戏发布的基准测试——http://shootout.alioth.debian.org/flawed-benchmarks.php - igouy
18
当我看到Go仅比这些基准测试中最快的程序慢两倍时,虽然这只是一个“游戏”,但我的第一印象是“哇,Go好像很快”,因为我知道这些基准测试有缺陷。相反,当我看到Ruby比最快的程序慢65倍时,我会想,“下一个高并发数值密集型计算项目不会使用Ruby”。所以虽然这只是一个“游戏”,但如果你抱着一颗谨慎的心来玩,其中还是有些真实性的。 - SyntaxT3rr0r
容量规划有一个非常重要的方面:成本。你需要 X 个盒子还是 2*X 个盒子最终会产生巨大的差异。由于没有人能够准确估计未来会在这些盒子上运行什么,最好的方法是查看不同的工作负载。我检查了一些实现,并发现它们大多数都还可以。我认为这些结果可以用作估算的基础。 - Agoston Horvath
通常,现实世界的系统受到IO的限制而不是CPU。因此,Go语言的速度慢2倍或5倍对于容量规划来说并没有像故障转移、负载平衡、缓存、数据库拓扑等问题那样重要。这就是为什么像YouTube这样规模的应用程序可以承担在Python中运行许多系统的原因。 - Sujoy Gupta

36

虽然我的回答不像其他人的那么技术性,但我认为它仍然相关。当我决定开始学习 Go 时,在计算机基准测试游戏网站上看到了相同的基准测试结果。但是,我认为这些合成基准测试在确定是否足够快的意义上是毫无意义的。

最近,我使用 Tornado+TornadIO+ZMQ 在 Python 中编写了一条消息服务器,并决定在我的第一个 Go 项目中使用 Go 重写该服务器。到目前为止,当我让 Go 程序达到与 Python 版本相同的功能时,我的测试结果显示 Go 程序的速度提高了约4.7倍。请注意,我只学习 Go 不到一周,而且已经使用 Python 编程超过5年了。

随着他们继续改进 Go,它只会变得更快,我认为真正关键的是它在实际应用程序中的表现,而不是微小的计算基准测试。对我来说,Go 显然可以产生比我在 Python 中能够生成的更高效的程序。这就是我对这个问题的看法。


3
你认为用Go写代码会比用Python慢多少? - Erik Engheim
7
@AdamSmith - 我会比写Python慢地写Go代码,这是因为我已经使用Python编码了7年多,而只学了一点点Go。但是相对于其他静态类型的编译语言,我认为我会比其他语言更快地写Go。就个人而言,我觉得它是最接近Python简洁性的语言之一,速度介于C和C ++之间。 - jdi
5
我有类似的故事。我刚开始学习Go语言,根据Benchmarks Game的测试结果,Go比JavaScript V8慢。但是我的程序需要进行二进制运算,发现未经优化的Go代码比高度优化的V8虚拟机快了10倍。虽然在很多操作上比C语言慢,但没有人会用C写网站。Go已经是一个完全可行的选择,并且随着新的库、框架和工具的出现,它只会变得更好。 - if __name__ is None
1
@user962247 这是一个疯狂且不准确的笼统说法。我已经写Go语言多年了,它非常快速。没有人声称它会在每个合成基准测试中击败C/C++/Java。但它在某些方面胜出(请参见基准测试游戏网站)。相信我这个实际上已经写了多年生产Go代码的人,它既快速又高效。 - jdi
1
你的应用程序是终极基准。 - igouy
显示剩余2条评论

8
事情已经发生了改变。
我认为你提出的问题的当前正确答案是质疑go语言慢的观点。在你提问的时候,你的判断是有道理的,但是go语言的性能在此之后得到了很大的提高。现在,它还没有像C语言那么快,但就一般情况而言也远远不会慢10倍。
参考计算机语言基准测试结果。
截至本文撰写时:
source  secs    KB      gz      cpu     cpu load

reverse-complement
1.167x
Go      0.49    88,320  1278    0.84    30% 28% 98% 34%
C gcc   0.42    145,900 812     0.57    0% 26% 20% 100%

pidigits
1.21x
Go      2.10    8,084   603 2.10    0% 100% 1% 1%
C gcc   1.73    1,992   448 1.73    1% 100% 1% 0%

fasta
1.45x
Go      1.97    3,456   1344    5.76    76% 71% 74% 73%
C gcc   1.36    2,800   1993    5.26    96% 97% 100% 97%

regex-dna
1.64x
Go      3.89    369,380 1229    8.29    43% 53% 61% 82%
C gcc   2.43    339,000 2579    5.68    46% 70% 51% 72%

fannkuch-redux
1.72x
Go      15.59   952 900 62.08   100% 100% 100% 100%
C gcc   9.07    1,576   910 35.43   100% 99% 98% 94%

spectral-norm
2x
Go      3.96    2,412   548 15.73   99% 99% 100% 99%
C gcc   1.98    1,776   1139    7.87    99% 99% 100% 99%

n-body
2.27x
Go      21.73   952 1310    21.73   0% 100% 1% 2%
C gcc   9.56    1,000   1490    9.56    1% 100% 1% 1%

k-nucleotide
2.40x
Go      15.48   149,276 1582    54.68   88% 97% 90% 79%
C gcc   6.46    130,076 1500    17.06   51% 37% 89% 88%

mandelbrot
3.19x
Go      5.68    30,756  894 22.56   100% 100% 99% 99%
C gcc   1.78    29,792  911 7.03    100% 99% 99% 98%

尽管如此,在二叉树基准测试中,它确实遭受了惨重的打击:

binary-trees
12.16x
Go      39.88   361,208 688 152.12  96% 95% 96% 96%
C gcc   3.28    156,780 906 10.12   91% 77% 59% 83%

1
现在它已经与Java持平,但是Go是不是专门为了比Java更快而创建的,同时用于相同的事情(服务器端网络应用程序)? - MWB
1
@MaxB 不,它的创建并不是为了比 Java 更快。它的目标是具有良好的性能、比 C++ 编译更快以及更容易和本地并发,以使开发人员更加高效。超越其他语言的运行时速度并不是一个主要因素。 - jdi

5
尽管Go在CPU周期使用效率方面表现不太好,但Go并发模型比Java的线程模型快得多,在某些情况下可以与C++线程模型相媲美。
请注意,在线程环形基准测试中,Go比Java快16倍。在同样的情况下,Go CSP几乎可以与C++相媲美,但使用的内存少4倍。
Go语言的强大之处在于其并发模型,由Tony Hoare在70年代指定的通信顺序进程(CSP),易于实现,并适用于高度并发的需求。

2
Java比Go和C++更快,甚至在许多情况下也比C语言更快,主要有两个基本原因:
1)JIT编译器。它可以根据运行时的配置文件,在多层级中内联虚函数调用,即使涉及面向对象的类也可以实现。这在静态编译语言中是不可能的(尽管基于记录的配置文件的重新编译可以帮助)。这对于大多数涉及重复算法的基准测试非常重要。
2)垃圾回收(GC)。基于GC的内存分配几乎是免费的,而与malloc相比则要便宜得多。'free'的惩罚可以分摊到整个运行时 - 通常因为程序在所有垃圾需要被收集之前就终止了,所以可以省略掉。
有数百(千?)极具才华的开发人员致力于使GC/JVM更加高效。认为自己可以“比他们都写得好”是一种愚蠢的想法。从根本上说,这是人类自我中心的问题 - 人类很难接受这样一个事实,即在经过才华横溢的人类适当培训后,计算机将比编程它的人表现得更好。
顺便说一下,如果不使用任何面向对象的特性,C++可以和C一样快,但这时你与其说是在用C++,还不如说是在用C。
最重要的是,这些测试中的“速度差异”通常是没有意义的。IO成本比性能差异高出数个数量级,因此,正确设计以最小化IO成本为目标的系统总是获胜的,即使在解释型语言中也是如此。很少有系统是CPU受限的。
最后需要注意的是,人们将“计算机语言基准测试游戏”称为“科学测量”。这些测试是完全有缺陷的。例如,如果您查看Java的nbody测试。当我在相同的操作系统/硬件上运行测试时,Java大约需要7.6秒,而C只需要4.7秒 - 这是合理的,而不是测试报告的4倍缓慢。这是点击诱饵,虚假新闻,旨在产生网站流量。
最后,最后的说明... 我使用Go运行了测试,结果是7.9秒。当你点击Go时,它与Java进行比较,当你点击Java时,它与C进行比较,这应该是任何认真工程师的警示信号。
Java、Go 和 C++ 的实际比较请参见 https://www.biorxiv.org/content/10.1101/558056v1。警告:在原始性能方面,Java 位居首位,而在综合内存使用和墙钟时间方面,Go 位居首位。

//顺便说一下,如果你不使用任何面向对象的特性,C++可以和C一样快,但那时你已经几乎等于是在用C进行编程了。如果你这样说,那么你对C++知道得很少,为了你自己的利益,请不要使用它。C和C++讨厌魔法和中世纪思维,对超自然现象充满迷信,比如“我听说了”,“我在互联网上看到了”,“那肯定是真的”之类的言论...远离C和C++,否则它们会反噬你的,朋友(真心建议)。 - user9826550
C是C++的祖先,仍有许多人在使用它... C++是更好的C,您可以在不付出代价的情况下做更多事情。Java、C#和Go的作者们没有理解这一点,当然,他们也理解了,但他们能做什么呢?同样适用于与现有(C)代码兼容。现实生活中有大量的C代码!Python是一个不错的玩具,享受吧,我希望他们能把它搞对,但是嘿,Python之禅应该以“编译器是你的朋友”开始... - user9826550
显示 Java 程序的 CPU 利用率为 30%,而不是 "1% 0% 0% 100%"。 - igouy
@user962247,对于大多数C++程序来说,你是错误的。研究虚函数表和内联函数 - 你会发现,当以面向对象的方式编写时,大多数C++比等效的C慢(我认为基准测试游戏证明了这一点,因为两者都是本地预编译的,这是一个相当公平的比较)。你的其他评论只是毫无意义的,我建议进一步学习。 - user9778120
1
@igouy 我承认这可能是我犯的错误 - 当我看到“load”时,我是从“系统负载”的角度进行解释,并且假设了用户/系统/IO/空闲 - 这是我的错误,而且是一个相当大的错误。 - user9778120
显示剩余22条评论

1
我认为一个经常被忽视的事实是,JIT编译可以比静态编译更适用于(运行时)后期绑定的函数或方法。热点JIT在运行时决定哪些方法要内联,甚至可能根据当前运行的CPU的缓存大小/架构调整数据布局。 总的来说,C/C++可以通过直接访问硬件弥补不足(并且总体上仍然表现更好)。对于Go来说,情况可能会有所不同,因为它与C相比更高级,但目前缺乏运行时优化系统/编译器。 我的直觉告诉我,由于Go不太强制执行指针追踪,并鼓励更好的数据结构局部性+需要较少的分配,因此Go可能比Java更快。

1
事实上,Go 不仅在设计时优雅高效,运行时也非常高效。关键在于使用正确的操作系统即 LINUX。在 Windows 和 Mac OS 下,性能分析结果比较差,相差一到两个数量级。

0
在Linux下,Go运行时非常快,与C/C++完全可比。而Windows和Unix下的Go运行时则不在同一水平上。
与Java相比并不那么重要,Go既适用于系统开发,也适用于应用程序开发(而Java更像是专门用于应用程序开发的蓝领工人)。我不会详细讨论,但当像Kubernetes这样的东西用Go编写时,你就会意识到它不是一个企业顾问友好型的玩具。
我不记得Google曾经提到过你所提到的妥协。Go设计良好、简单、优雅、高效,适用于设计系统和应用程序级别的程序,具有指针、高效的内存分配和释放,避免了由于实现继承容易被误用而产生的复杂性,为你提供协程和其他现代化的编写高性能应用程序的方式,而且在Linux下超级快速,这正是它的设计初衷(非常高兴它能做到)。

-4

Java和C都更加明确其数据和方法(函数)的定义。C是静态类型的,而Java则在其继承模型中没有这么多限制。这意味着数据将在编译期间被处理的方式已经基本确定。

Go对其数据和函数的定义更加隐含。内置函数性质更加普遍,并且缺乏类型层次结构(类似于Java或C ++),这使得Go在速度上处于劣势。

请记住,Google开发Go语言的目标是在执行速度和编程速度之间取得可接受的妥协。我认为他们在早期尝试中达到了一个很好的平衡点,并且随着更多的工作完成,情况只会变得更好。

如果您将Go与那些主要优势在于编程速度的动态类型语言进行比较,则会看到Go的执行速度优势。在您使用的那些基准测试中,Go比perl快8倍,比Ruby 1.9和Python 3快6倍。

无论如何,更好的问题是Go是否在编程易用性与执行速度之间取得了良好的平衡?我的答案是肯定的,并且它应该会变得更好。


20
“缺少类型层次结构(例如Java或C ++)使得Go速度劣势” —是什么意思? - Erik Kaplun
6
“Go在数据和函数定义方面更加隐式。”这是不正确的。你是指类型可以实现方法而无需显式说明吗?编译器会检测到类型-接口成员资格,这很快。 “内置函数的性质更加通用”不是,像其他所有内容一样,内置函数也是编译的。C++模板也是如此。 “缺乏类型层次结构(如Java或C ++)使得Go速度劣势” - 不正确,类型层次结构与运行时执行无关。 - Malcolm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接