Can Go真的比python快得多吗?

bab*_*bab 44 python performance go

我想我可能已经错误地实现了这个,因为结果没有意义.我有一个计数到1000000000的计划

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 1000000000; i++ {}
    fmt.Println("Done") 
}
Run Code Online (Sandbox Code Playgroud)

它在不到一秒钟内完成.另一方面,我有一个python脚本

x = 0
while x < 1000000000:
    x+=1
print 'Done'
Run Code Online (Sandbox Code Playgroud)

它在几分钟内完成.

为什么Go版本要快得多.他们都数到10亿还是我错过了什么?

hob*_*bbs 81

十亿不是一个很大的数字.任何合理的现代机器应该能够在几秒钟内完成,如果它能够使用本机类型进行工作.我通过编写一个等效的C程序来验证这一点,读取程序集以确保它实际上正在添加,并对它进行计时(它在我的机器上完成大约1.8秒).

但是,Python没有本机类型变量的概念(或者根本没有有意义的类型注释),因此在这种情况下它必须完成数百倍的工作.简而言之,您的标题问题的答案是"是".Go真的可以比Python快得多,即使没有任何类似的编译技巧,比如优化掉一个无副作用的循环.


Joh*_*ooy 66

pypy实际上在加速这个循环方面做得非常出色

def main():
    x = 0
    while x < 1000000000:
        x+=1

if __name__ == "__main__":
    s=time.time()
    main()
    print time.time() - s
Run Code Online (Sandbox Code Playgroud)
$ python count.py 
44.221405983
$ pypy count.py 
1.03511095047
Run Code Online (Sandbox Code Playgroud)

加速率达到97%!

澄清了3个没有"得到它"的人.Python语言本身并不慢.CPython实现是一种运行代码的相对简单的方法.Pypy是该语言的另一种实现,它可以解决许多棘手的问题(特别是JIT).直接回答标题中的问题 - Go并不比Python那么"快" ,Go比CPython快得多.

话虽如此,代码示例并没有真正做同样的事情.Python需要实例化其1000000000个int对象.Go只是递增一个内存位置.


小智 19

这种情况非常有利于体面的本地编译的静态类型语言.本地编译的静态类型语言能够发出一个非常简单的循环,例如4-6个CPU操作码,它利用简单的检查条件进行终止.这个循环实际上有零分支预测未命中,并且可以有效地被认为是在每个CPU周期执行增量(这不完全正确,但是......)

Python实现必须做显著更多的工作,主要是由于动态类型.Python必须进行几个不同的调用(内部和外部)才能将两个ints一起添加.在Python中它必须调用__add__(它是有效的i = i.__add__(1),但这种语法只能在Python 3.x中工作),而这又需要检查传递的值的类型(以确保它是一个int),然后它添加整数值(从两个对象中提取它们),然后将新的整数值再次包装在新对象中.最后,它将新对象重新分配给局部变量.这要多得多 而不是单个操作码递增,甚至不解决循环本身 - 相比之下,Go/native版本可能只是通过副作用递增寄存器.

Java将公平在这样一个平凡的基准更好的将可能是相当接近的地方; 计数器变量的JIT和静态类型可以确保这一点(它使用特殊的整数添加JVM指令).再一次,Python没有这样的优势.现在,有一些像PyPy/RPython这样的实现,它们运行一个静态输入阶段,应该比CPython好得多.

  • 我并不是故意将此作为基准(对不起,如果我没有说清楚).我只是想知道为什么python版本这么慢. (7认同)

Sea*_*ing 8

你有两件事在这里工作.第一个是将Go编译为机器代码并直接在CPU上运行,而Python被编译为针对(特别慢)VM运行的字节码.

影响性能的第二个也是更重要的事情是两个程序的语义实际上有很大不同.Go版本创建一个名为"x"的"盒子",它保存一个数字,并在每次通过程序时递增1.Python版本实际上必须在每个循环中创建一个新的"box"(int对象)(并且最终必须抛弃它们).我们可以通过稍微修改您的程序来证明这一点

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d %p\n", i, &i)
    }
}
Run Code Online (Sandbox Code Playgroud)

...和:

x = 0;
while x < 10:
    x += 1
    print x, id(x)
Run Code Online (Sandbox Code Playgroud)

这是因为Go由于它的C根而采用变量名称来引用一个地方,其中Python采用变量名称来引用事物.由于整数被认为是python中唯一的,不可变的实体,我们必须不断创建新的实体.Python应该比Go慢,但你已经选择了最糟糕的情况 - 在基准游戏中,我们看到平均来说,速度提高约25倍(在最坏的情况下为100倍).

您可能已经读过,如果您的Python程序太慢,您可以通过将事物移动到C来加速它们.幸运的是,在这种情况下,有人已经为您完成了这项工作.如果你重写你的空循环使用xrange(),如下所示:

for x in xrange(1000000000):
    pass
print "Done."
Run Code Online (Sandbox Code Playgroud)

......你会发现它的运行速度快了两倍.如果您发现循环计数器实际上是程序中的主要瓶颈,那么可能是时候研究解决问题的新方法了.