Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么等效Python代码要慢得多_Python_Ruby_Python 3.x - Fatal编程技术网

为什么等效Python代码要慢得多

为什么等效Python代码要慢得多,python,ruby,python-3.x,Python,Ruby,Python 3.x,有人能解释一下为什么下面这段琐碎的代码(实现欧几里德算法以找到最大公分母)比Ruby中的等效代码慢3倍 iter_gcd.py的内容: from sys import argv,stderr def gcd(m, n): if n > m: m, n = n, m while n != 0: rem = m % n m = n n = rem return m # in Python3 code t

有人能解释一下为什么下面这段琐碎的代码(实现欧几里德算法以找到最大公分母)比Ruby中的等效代码慢3倍

iter_gcd.py的内容:

from sys import argv,stderr

def gcd(m, n):
    if n > m:
        m, n = n, m
    while n != 0:
        rem = m % n
        m = n
        n = rem
    return m

# in Python3 code there is xrange replaced with range function
def main(a1, a2):
    comp = 0
    for j in xrange(a1, 1, -1):
        for i in xrange(1, a2):
            comp += gcd(i,j)

    print(comp)

if __name__ == '__main__':
    if len(argv) != 3:
        stderr.write('usage: {0:s} num1 num2\n'.format(argv[0]))
        exit(1)
    else:
        main(int(argv[1]), int(argv[2]))
iter_gcd.rb的内容:

def gcd(m, n)
    while n != 0
        rem = m % n
        m = n
        n = rem
    end
    return m
end

def main(a1, a2)
    comp = 0
    a1.downto 2 do
        |j|
        1.upto (a2 - 1) do
            |i|
            comp += gcd(i,j)
        end
    end
    puts comp
end

 if __FILE__ == $0
    if ARGV.length != 2
        $stderr.puts('usage: %s num1 num2' % $0)
        exit(1)
    else
        main(ARGV[0].to_i, ARGV[1].to_i)
    end
end
执行时间测量:

$ time python iter_gcd.py 4000 3000
61356305

real    0m22.890s
user    0m22.867s
sys     0m0.006s

$ python -V
Python 2.6.4


$ time python3 iter_gcd.py 4000 3000
61356305

real    0m18.634s
user    0m18.615s
sys     0m0.009s

$ python3 -V
Python 3.1.2


$ time ruby iter_gcd.rb 4000 3000
61356305

real    0m7.619s
user    0m7.616s
sys     0m0.003s

$ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-linux]
只是好奇为什么我会得到这样的结果。我认为CPython在大多数情况下都比MRI更快,甚至比YARV上的新Ruby 1.9更快,但这个“微基准”确实让我感到惊讶

顺便说一句,我知道我可以使用专门的库函数,比如fracts.gcd,但我想比较一下这些基本和琐碎的语言结构的实现


我是否错过了一些东西,或者下一代Ruby的实现在速度方面有了很大的改进?

我似乎记得Ruby处理整数的方式与Python不同,所以我猜测可能只是Python在分配内存方面花费了大量时间,而Ruby只是在适当的地方对整数进行了变异


值得一提的是,在我的系统上使用Pypy 1.4可以将Python版本的运行时间从大约15秒减少到3秒以下。

我可以确认ruby1.9比CPython在以下“微基准”上的运行速度更快:

Profiler(
python-mcProfile iter\u gcd.py 4000 3000
)显示,调用
gcd()
函数所花费的时间有80%,因此实际上差异在于
gcd()
函数

我使用cython为Python编写了
cython\u gcd
扩展,
cython\u gcd.pyx

def gcd(m, n):
    while n:
        n, m = m % n, n
    return m

def gcd_int(int m, int n):
    while n:
        n, m = m % n, n
    return m
它在iter\u gcd.py中使用,如下所示,来自cython\u gcd import gcd,gcd\u int

要尝试扩展,请运行:
python setup.py build\u ext--inplace
,其中:


要全局安装扩展,请运行
python setup.py install

我无法复制您的结果。python代码似乎比ruby代码快4倍:

2010-12-07 13:49:55:~/tmp$ time python  iter_gcd.py 4000 3000
61356305

real    0m14.655s
user    0m14.633s
sys 0m0.012s

2010-12-07 13:43:26:~/tmp$ time ruby iter_gcd.rb 4000 3000
iter_gcd.rb:14: warning: don't put space before argument parentheses
61356305

real    0m54.298s
user    0m53.955s
sys 0m0.028s
版本:

2010-12-07 13:50:12:~/tmp$ ruby --version
ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]
2010-12-07 13:51:52:~/tmp$ python --version
Python 2.6.6
此外,python代码的速度可以提高8%:

def gcd(m, n):
    if n > m:
        m, n = n, m
    while n:
        n, m = m % n, n
    return m

def main(a1, a2):
    print sum(
        gcd(i,j)
        for j in xrange(a1, 1, -1)
        for i in xrange(1, a2)
    )

if __name__ == '__main__':
    from sys import argv
    main(int(argv[1]), int(argv[2]))

稍后:当我安装并使用ruby 1.9.1时,ruby代码要快得多:

2010-12-07 14:01:08:~/tmp$ ruby1.9.1 --version
ruby 1.9.2p0 (2010-08-18 revision 29036) [i686-linux]
2010-12-07 14:01:30:~/tmp$ time ruby1.9.1 iter_gcd.rb 4000 3000
61356305

real    0m12.137s
user    0m12.037s
sys 0m0.020s
我想你的问题是,“为什么Ruby1.9.x比Ruby1.8.x快那么多?”

Summary “因为Python中的函数调用开销比Ruby中大得多。”

细节 作为一个微基准,这并不能说明这两种语言在正确使用中的表现。您可能希望重写程序以利用Python和Ruby的优势,但这确实说明了Python目前的一个弱点。速度差异的根本原因来自函数调用开销。我做了一些测试来说明。请参阅下面的代码和更多详细信息。对于Python测试,我使用2000作为两个gcd参数

Interpreter: Python 2.6.6
Program type: gcd using function call
Total CPU time: 29.336 seconds

Interpreter: Python 2.6.6
Program type: gcd using inline code
Total CPU time: 13.194 seconds

Interpreter: Python 2.6.6
Program type: gcd using inline code, with dummy function call
Total CPU  time: 30.672 seconds
这告诉我们,对时差贡献最大的不是gcd函数所做的计算,而是函数调用本身。对于Python 3.1,区别类似:

Interpreter: Python 3.1.3rc1
Program type: gcd using function call
Total CPU time: 30.920 seconds

Interpreter: Python 3.1.3rc1
Program type: gcd using inline code
Total CPU time: 15.185 seconds

Interpreter: Python 3.1.3rc1
Program type: gcd using inline code, with dummy function call
Total CPU time: 33.739 seconds
同样,实际计算并不是最大的贡献者,而是函数调用本身。在Ruby中,函数调用开销要小得多。(注意:对于Ruby版本的程序,我不得不使用较小的参数(200),因为Ruby profiler确实会降低实时性能。但这不会影响CPU时间性能。)

请注意,Ruby 1.8和1.9都没有受到gcd函数调用的严重影响–函数调用和内联版本或多或少是相同的。Ruby1.9似乎更好一些,函数调用和内联版本之间的差异更小

因此,问题的答案是:“因为Python中的函数调用开销比Ruby中大得多”

代码


试运行 最后,用于运行Python和Ruby并进行评测以获得数字进行比较的命令是:Python的
pythonX.X-mcprofile iter\u gcdX.py 2000
,Ruby的
rubyX.X-rprofile iter\u gcdX.rb 200
。造成这种差异的原因是Ruby分析器增加了很多开销。结果仍然有效,因为我比较的是函数调用和内联代码之间的差异,而不是Python和Ruby之间的差异

另见


请正确格式化/突出显示您的代码示例。无论您的代码值多少,在我的机器上,python(2.6)比ruby(1.8)快得多。。。python为11.7秒,ruby为46.7秒。当然,我是在比较ruby 1.8而不是1.9,这不是你的问题……Python
gcd
swaps
m
n
if
n>m
。Ruby
gcd
没有。这可能并不能解释所有的差异,但最好将苹果与苹果进行比较。@EOL:Ruby似乎可以透明地处理bignum。我相信这与数字存储/处理的实现有关。另一方面,对于上面的计算,integer和longint的大小足够了,我不希望Python将它们无效地存储为任意数字,f.e.OT:我确实看过Pypy,它看起来很有希望,尽管并非在所有情况下都比CPython快。我们能期待它在哪里“生产就绪”吗?@David Unric:它现在通过了各种各样的测试。如果它现在对你来说还不够好,那么它可能永远也不会好。它还不太好。这是pypy兼容性的最新版本。一些东西,比如重新计数,不会改变很好的答案,还有cython很酷的例子。然而,我认为你误解了基准(或者你的话不准确;)。当然,程序的大部分时间都花在
gcd()
函数上,因为即使参数很小,它也会被调用数千次。所以区别不在于函数,而是函数被调用。也许这就是你的意思?@Fabian Fagerholm:如果
gcd=lambda,b:0
t的7%
Interpreter: Python 2.6.6
Program type: gcd using function call
Total CPU time: 29.336 seconds

Interpreter: Python 2.6.6
Program type: gcd using inline code
Total CPU time: 13.194 seconds

Interpreter: Python 2.6.6
Program type: gcd using inline code, with dummy function call
Total CPU  time: 30.672 seconds
Interpreter: Python 3.1.3rc1
Program type: gcd using function call
Total CPU time: 30.920 seconds

Interpreter: Python 3.1.3rc1
Program type: gcd using inline code
Total CPU time: 15.185 seconds

Interpreter: Python 3.1.3rc1
Program type: gcd using inline code, with dummy function call
Total CPU time: 33.739 seconds
Interpreter: ruby 1.9.2p0 (2010-08-18 revision 29036) [i486-linux]
Program type: gcd using function call
Total CPU time: 21.66 seconds

Interpreter: ruby 1.9.2p0 (2010-08-18 revision 29036) [i486-linux]
Program type: gcd using inline code
Total CPU time: 21.31 seconds

Interpreter: ruby 1.8.7 (2010-08-16 patchlevel 302) [i486-linux]
Program type: gcd using function call
Total CPU time: 27.00 seconds

Interpreter: ruby 1.8.7 (2010-08-16 patchlevel 302) [i486-linux]
Program type: gcd using inline code
Total CPU time: 24.83 seconds
# iter_gcd -- Python 2.x version, with gcd function call
#             Python 3.x version uses range instead of xrange
from sys import argv,stderr

def gcd(m, n):
    if n > m:
        m, n = n, m
    while n != 0:
        rem = m % n
        m = n
        n = rem
    return m

def main(a1, a2):
    comp = 0
    for j in xrange(a1, 1, -1):
        for i in xrange(1, a2):
            comp += gcd(i,j)
    print(comp)

if __name__ == '__main__':
    if len(argv) != 3:
        stderr.write('usage: {0:s} num1 num2\n'.format(argv[0]))
        exit(1)
    else:
        main(int(argv[1]), int(argv[2]))
# iter_gcd -- Python 2.x version, inline calculation
#             Python 3.x version uses range instead of xrange
from sys import argv,stderr

def main(a1, a2):
    comp = 0
    for j in xrange(a1, 1, -1):
        for i in xrange(1, a2):
            if i < j:
                m, n = j, i
            else:
                m, n = i, j
            while n != 0:
                rem = m % n
                m = n
                n = rem
            comp += m
    print(comp)

if __name__ == '__main__':
    if len(argv) != 3:
        stderr.write('usage: {0:s} num1 num2\n'.format(argv[0]))
        exit(1)
    else:
        main(int(argv[1]), int(argv[2]))
# iter_gcd -- Python 2.x version, inline calculation, dummy function call
#             Python 3.x version uses range instead of xrange
from sys import argv,stderr

def dummyfunc(n, m):
    a = n + m

def main(a1, a2):
    comp = 0
    for j in xrange(a1, 1, -1):
        for i in xrange(1, a2):
            if i < j:
                m, n = j, i
            else:
                m, n = i, j
            while n != 0:
                rem = m % n
                m = n
                n = rem
            comp += m
            dummyfunc(i, j)
    print(comp)

if __name__ == '__main__':
    if len(argv) != 3:
        stderr.write('usage: {0:s} num1 num2\n'.format(argv[0]))
        exit(1)
    else:
        main(int(argv[1]), int(argv[2]))
# iter_gcd -- Ruby version, with gcd function call

def gcd(m, n)
    if n > m
        m, n = n, m
    end
    while n != 0
        rem = m % n
        m = n
        n = rem
    end
    return m
end

def main(a1, a2)
    comp = 0
    a1.downto 2 do
        |j|
        1.upto a2-1 do
            |i|
            comp += gcd(i,j)
        end
    end
    puts comp
end

 if __FILE__ == $0
    if ARGV.length != 2
        $stderr.puts('usage: %s num1 num2' % $0)
        exit(1)
    else
        main(ARGV[0].to_i, ARGV[1].to_i)
    end
end
# iter_gcd -- Ruby version, with inline gcd

def main(a1, a2)
    comp = 0
    a1.downto 2 do |j|
        1.upto a2-1 do |i|
            m, n = i, j
            if n > m
                m, n = n, m
            end
            while n != 0
                rem = m % n
                m = n
                n = rem
            end
            comp += m
        end
    end
    puts comp
end

 if __FILE__ == $0
    if ARGV.length != 2
        $stderr.puts('usage: %s num1 num2' % $0)
        exit(1)
    else
        main(ARGV[0].to_i, ARGV[1].to_i)
    end
end