为什么Go这么慢(与Java相比)?


109

我们可以从2010年的计算机语言基准游戏中看到:

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

考虑到Go编译器会生成要执行的本机代码,这怎么可能?
Go的编译器不成熟?还是Go语言存在一些内在问题?

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


36
公平地讲,C是伪装的ASM,而Java在当今已经进行了一些重大优化。
马修·沙利

16
基准测试也许也不能反映出Go的优势。可能其他基准实际上比这更快。此外,最重要的通常不是性能,而是代码的可读性。
Extraneon 2010年

7
@extraneon:我同意。请记住,Go是专为Google设计的,Google通常在200 万个内核上运行代码。我相信基准测试仅使用4个核心。
约尔格W¯¯米塔格

4
@extraneon:我总体上同意,但是Go在设计时特别考虑了速度,例如,“结果程序的运行速度几乎与可比较的C或C ++代码一样快”。
shosti 2010年

4
您的问题假设太多:“大多数答案都否认Go languge的内在缓慢”是在问题中使用的错误短语。您有要问的问题或要发表的声明吗?请参阅c2.com/cgi/wiki?HostileStudent了解您的错误。
克里斯,

Answers:


102

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

它们旨在快速运行自己并生成可以的代码(有些优化)。gccgo使用GCC的现有优化遍历,并且可能与C进行更有意义的比较,但是gccgo尚未完成功能。

基准数字几乎完全与执行质量有关。它们与这种语言没有太多关系,除非实现花费了运行时支持基准测试实际上不需要的语言功能。在大多数已编译语言中,理论上足够聪明的编译器可以剔除不需要的内容,但是您到了要演示的地方,因为很少有真正的语言用户编写不使用该功能的程序。 。将事情移开而不完全删除它们(例如,在JIT编译的Java中预测虚拟调用目标)开始变得棘手。

FWIW是我自己对Go进行的非常琐碎的测试(基本上是整数加法循环),gccgo朝着介于C gcc -O0gcc -O2C 之间的范围的快端生成了代码。Go 本质上并不慢,但是编译器还不能做所有事情。对于使用10分钟的语言来说,这不足为奇。


7
而且,可能是计算机语言基准游戏中的Go程序没有像C和Java那样经过优化。
el.pescado

在gcc -O0和gcc -O3之间呢?编译器是否打算“做所有事情”?
igouy

@igouy:好吧,我很确定gccgo打算进行垃圾收集,而目前还没有。g编译器还具有一些功能,例如,它们当前不能很好地使用主机线程(特别是goroutine调度程序不是抢先的)。除此之外,我不知道Google的计划,也不知道g编译器是否会进行激烈的优化,或者仅gccgo会优化。
史蒂夫·杰索普

1
@xitrium:我认为Go的用意是不要求实现合作安排日程,如果愿意,它们可以抢占先机。参见例如code.google.com/p/go/issues/detail?id=543,该代码尚未被关闭为“荒谬的,修复此所谓的bug会与Go语言的定义相抵触”,如果禁止抢先实现Go实现:-)由于默认情况下,无论有多少个goroutine可运行,Go都只使用一个主机线程,这使问题更加复杂。
史蒂夫·杰索普

6
到目前为止,答案可能有些过时。最近,Go 1.1的第一个beta 发布了,他们指出已编译程序的性能提高了大约30%至40%。有人请再次进行这些测试。
2013年

51

Go FAQ的下一版本中,应显示类似以下内容。

性能

为什么Go在基准X上表现不佳?

Go的设计目标之一是接近可比程序的C性能,但在某些基准上却表现不佳,包括在测试/基准测试中的一些基准。最慢的速度取决于在Go中没有可比性能的版本的库。例如,数字位数取决于多精度数学程序包,而与Go语言不同,C语言版本使用GMP(用优化的汇编程序编写)。依赖于正则表达式(例如regex-dna)的基准本质上是将Go的权宜之计regexp软件包与成熟的,高度优化的正则表达式库(如PCRE)进行比较。

基准测试通过广泛的调整赢得了胜利,大多数基准的Go版本需要引起注意。如果您测量可比较的C和Go程序(反向补码就是一个例子),您会发现两种语言的原始性能比该套件所表明的要紧密得多。

尽管如此,仍有改进的空间。编译器很好,但可能会更好,许多库需要大量的性能工作,并且垃圾收集器还不够快(即使这样做,注意不要生成不必要的垃圾也会产生巨大的影响)。

这是来自最近的邮件列表线程的有关计算机基准测试的更多详细信息。

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

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

重要的是要注意,计算机基准测试只是一个游戏。在性能评估和容量规划方面有经验的人员会仔细地匹配实际和实际的工作负载;他们不玩游戏。


1
在这里,来自同一线程的一些细节可以排除- groups.google.com/group/golang-nuts/msg/2e568d2888970308
igouy

3
这是需要注意的是“基准是一个瓦罐”重要-不仅发布为基准游戏的基准- shootout.alioth.debian.org/flawed-benchmarks.php
igouy

18
(和每个人...)当然,这是一个“游戏”,但是当我看到Go的速度比这些基准测试中最快的速度慢两倍时,我的第一印象是“哇,Go看起来很快”,因为我知道这些基准测试是有缺陷的。相反,当我看到Ruby比最快的速度慢65倍时,我认为自己“不会在接下来的并发数字密集型工作中使用Ruby”。因此,这可能是一个“游戏”,但是如果您带着一粒盐吃下去,其中就有一些道理。
SyntaxT3rr0r 2011年

容量规划有一个非常重要的方面:成本。最终需要X盒还是2 * X都将带来巨大的不同。而且由于没有人能估计出未来将要发生的情况,因此最好的选择是看一下不同的工作负载。我检查了这些的一些实现,发现它们基本上都可以。我认为结果可以用作估算的基础。
Agoston Horvath

通常,现实世界的系统受IO限制,而不受CPU限制。因此,无论Go是2倍还是5倍都比较慢,这几乎不会像故障转移,负载平衡,缓存,数据库拓扑等对容量规划产生太大影响。这就是为什么YouTube规模的应用程序可以负担得起在Python中运行其许多系统的原因。
Sujoy Gupta

34

我的答案不像其他人那么专业,但是我认为它仍然很重要。当我决定开始学习Go时,我在计算机基准测试游戏中看到了相同的基准。但老实说,我认为所有这些综合基准对于确定Go是否足够快都没有意义。

我最近使用Tornado + TornadIO + ZMQ用Python编写了一个消息服务器,对于我的第一个Go项目,我决定用Go重写服务器。到目前为止,通过使服务器具有与Python版本相同的功能,我的测试向我展示了Go程序中速度提高了4.7倍。请注意,我只用Go编写了一个星期的代码,而我用Python编写了5年以上。

随着他们不断努力,Go只会变得越来越快,我认为这实际上取决于它在实际应用程序中的性能,而不是很小的计算基准。对我来说,Go显然比我用Python生产的程序效率更高。这是我对这个问题的答案的看法。


3
您认为编写Go代码要比Python慢​​多少?
Erik Engheim 2013年

7
@AdamSmith-我会说我将编写Go代码的速度比Python慢​​,仅是因为我已经使用Python编写了7年多,而编写Go的时间却很少。但是就Go与其他静态类型的编译语言相比,我敢保证我会比其他人更快地编写Go。就我个人而言,我认为它与Python的简单性最接近,其速度介于C和C ++之间
jdi 2013年

5
我有类似的故事。我刚开始学习Go,根据Benchmarks Game的说法,Go比JavaScript V8慢。事实证明,对于未经优化的Go代码,我的二进制操作密集型程序的运行速度比高度优化的V8 VM快10倍。Go在许多操作中可能比C慢一些,但是没有人用C编写网站。Go已经是一个完全可行的选择,而且随着新的库,框架和工具的出现,它应该只会变得更好。
如果__name__为None(

1
@ user962247这是一个疯狂和虚假的毯子声明。我已经写Go了很多年了,它发展很快。没有人声称它将在所有可能的综合基准上击败C / C ++ / Java。但它在某些方面胜出(请参阅基准游戏网站)。从实际上已经编写了生产Go代码多年的人那里获取它。快速而高效。
jdi


6

事情变了。

我认为当前对您问题的正确答案是反驳“走慢”的概念。在您询问时,您的判断是有道理的,但是从那以后,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%

它现在可以与Java相提并论,但是不是Go被明确创建为比Java更快,同时又用于相同的东西(服务器端网络应用程序)吗?
MaxB

1
@MaxB不,它不是为比Java更快而创建的。创建它的目的是具有良好的性能,比C ++更快的编译速度以及更简单的本机并发性,以使开发人员能够提高工作效率。击败其他语言的运行时速度并不是驱动因素。
jdi

5

尽管Go的CPU周期使用效率不高,但是Go并发模型比Java中的线程模型要快得多,并且可以与C ++线程模型媲美。

请注意,在线程环基准测试中,Go 比Java快16倍。在相同的情况下,Go CSP几乎可以与C ++相提并论,但是使用的内存却少了4倍。

Go语言的强大功能是其并发模型,即通信顺序过程,CSP,由Tony Hoare在20世纪70年代指定,它易于实现并适合高度并发的需求。


2

Java比Go和C ++更快,并且在许多情况下可以比C更快的原因有两个:

1)JIT编译器。它可以基于运行时配置文件,通过多个级别(甚至使用OO类)内联虚拟函数调用。这在静态编译语言中是不可能的(尽管基于记录的配置文件进行较新的重新编译可以有所帮助)。这对于涉及重复算法的大多数基准测试非常重要。

2)GC。与malloc相比,基于GC的内存分配几乎是免费的。而且“免费”的罚款可以在整个运行时中摊销-通常会跳过,因为程序在需要收集所有垃圾之前就终止了。

有成百上千的非常有才华的开发人员可以使GC / JVM高效。认为您可以“比所有人都编码更好”是愚蠢的。从本质上讲,这是一个人的自我问题-人们很难接受在有才能的人的适当培训下,计算机的性能要比对其进行编程的人更好。

顺便说一句,如果您不使用OO功能,那么C ++的速度可以与C一样快,但是从开始时就非常接近使用C进行编程。

最重要的是,这些测试中的“速度差异”通常毫无意义。IO成本要比性能差异高出几个数量级,因此即使将其解释为语言,也可以将IO成本降到最低的正确设计总会获胜。很少有系统受CPU限制。

最后,人们将“计算机语言基准测试”称为“科学措施”。测试是完全有缺陷的,例如,如果您查看nbody的Java测试。当我在相同的操作系统/硬件上运行测试时,对于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一样快,特别是当您使用OOP时,这就是他的出生证明书。在没有任何运行时性能下降的情况下进行更多抽象(如在类中),并且具有零字节外字节存储器。如果您不知道,请继续使用java,c#,go,python等进行嬉戏

//顺便说一句,如果您不使用C和OO功能,C ++的速度可以与C一样快,但是从那时开始,您就非常接近使用C //进行编程。如果您这样说,那么您对c ++的了解很少,请不要使用它。c和c ++讨厌魔术和中世纪思想,本质上是迷信的,就像哦,我听说过,可以通过互联网阅读它,这一定是真的...远离c和c ++,他们会把你骗回来给我的朋友(诚实的建议)

c是c ++的祖先。许多人仍然使用它。c ++是更好的c,您可以在不付出代价的情况下做更多的事情。java,c#和go的作者没有得到它,当然,他们做到了,但是他们能做什么?!?与现有(c)代码兼容相同。现实生活中的C代码海洋!python是一个不错的玩具,请尽情享受,我希望他们做对了,但是,不,禅宗的python应该以“编译器是你的朋友”开始了……

>>显示Java程序的CPU利用率为30%<<否-“ 1%0%0%100%”。
igouy

1
@igouy我承认那是我可能犯的一个错误-当我看到负载时,我用“系统负载”术语来解释,并假设用户/系统/ io /空闲是我的错误,那是一个严重的错误。
罗伯特·恩格尔斯(Robert Engel)

1

我认为一个经常被忽视的事实是,JIT编译可以是>静态编译,尤其是对于(运行时)后期绑定的函数或方法。热点JIT在RUNTIME决定内联哪些方法,它甚至可能根据当前正在运行的CPU的缓存大小/体系结构调整数据布局。通常,通过直接访问硬件,C / C ++可以组成(并且总体上仍会表现得更好)。对于Go而言,与C相比,它看起来更高级,但现在缺少运行时优化系统/编译器。我的直觉告诉我,Go 可能比Java更快,因为Go不会强制执行过多的指针追逐,而是鼓励更好的数据结构局部性+需要更少的分配。


1

实际上,Go在设计时不仅优雅高效,而且在运行时也具有出色的性能。关键是使用正确的操作系统,即LINUX。由于缺乏更好的说法,Windows和Mac OS下的性能分析结果要差一个或两个数量级。


0

在linux下,go运行时非常快,可以与c / c ++完美媲美。Windows和Unix下的go运行时不在同一个联盟中

与Java的比较不是那么重要,转到系统和应用程序开发中去(因为Java更像是仅用于应用程序开发的蓝领)。不会输入详细信息,但是当您编写诸如kubernetes之类的东西时,您会意识到这不是企业顾问友好的玩具

我什至不记得Google曾经提到过您提到的折衷方案。go是精心设计的,简单,优雅且高效的设计系统和应用程序级程序,具有指针,有效的内存分配和释放,避免了因易于继承的实现继承而带来的复杂性,从而为您提供了协同例程和其他现代方法时间和预算编写高性能应用程序的方法。再说一次,go在linux下超级快,这正是它的设计目的(对此非常高兴)


-4

Java和C的数据和方法(函数)定义都更加明确。C是静态类型的,而Java的继承模型则较少。这意味着在编译过程中几乎定义了处理数据的方式。

Go的数据和函数定义更加隐含。内置函数本质上是更通用的,缺少类型层次结构(如Java或C ++)使Go在速度上处于劣势。

请记住,Google的Go语言目标是在执行速度和编码速度之间取得可接受的折衷。我认为他们在早期尝试中就达到了一个很好的目标,随着更多的工作要做,情况只会有所改善。

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

无论如何,更好的问题是在编程的易用性和执行速度上取得良好的折衷吗?我的回答是,应该会更好。


20
“缺乏类型层次结构(如Java或C ++)使Go在速度方面处于劣势” –会吗?
埃里克·卡普伦

6
“ Go的数据和函数定义更加隐含。” 不正确 您是说类型如何实现方法而又无需明确说明吗?编译器检测类型-接口成员资格。很快 “内置函数本质上更通用”,不像其他所有东西一样,编译内置函数。C ++模板也会发生同样的事情。“缺乏类型层次结构(如Java或C ++)使Go的速度处于劣势” –不正确,类型层次结构与运行时执行无关。
马尔科姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.