【问题标题】:Why python is much slower than node.js on recursion为什么 python 在递归上比 node.js 慢得多
【发布时间】:2013-10-11 01:25:33
【问题描述】:

我编写了一个简单的斐波那契测试程序来比较 node.js 和 python 的性能。 原来 python 用了 5s 来完成计算,而 node.js 用了 200ms 为什么python在这种情况下表现如此糟糕?

蟒蛇

import time

beg = time.clock()
def fib(n):
    if n <=2:
        return 1
    return fib(n-2) + fib(n-1)

var = fib(35)

end = time.clock()
print var
print end - beg

node.js

var beg = new Date().getTime();

function fib(n)
{
    if (n <= 2)
        return 1;

    return fib(n-2) + fib(n-1);
}

var f = fib(35);
var end = new Date().getTime();

console.log(f);
console.log(end - beg);

【问题讨论】:

  • 您正在比较实现:CPython 和 Node.js。 语言比较是 Python(实现包括 CPython、PyPy、IronPython 和 Jython)与 JavaScript(实现包括 SpiderMonkey、V8 和 Chakra)。
  • 这个问题导致了一些非常有启发性的答案。谢谢大家。

标签: python node.js


【解决方案1】:

真的不可能像这样设置一个人为的基准并获得足够有用的结果来对速度做出笼统的陈述;基准测试极其复杂,在某些情况下,运行时甚至可以完全排除基准测试的一部分,因为它们意识到有一种更快的方法可以完成您告诉它您想做的事情。

但是,最重要的是,您不是在比较 Python 和 node.js,而是比较它们的解释器:CPython 和 V8。 Python 和 Javascript 有相似的语言特性会影响性能(垃圾收集、动态类型,甚至我认为的整数堆分配?)所以当你运行这个基准测试时,它真的是解释器之间的枪战。

在这种情况下,即使像这样的基准测试通常没有价值,“为什么 V8 在这种事情上比 CPython 更快”这个问题确实有一个答案:这是因为 JIT编译器

因此,如果您想直接比较,请尝试在 PyPy 上运行您的 Python 代码,PyPy 是一个带有 JIT 的 Python 解释器。或者尝试在没有 JIT 的运行时运行您的 Javascript 代码。但是,到那时,您可能会发现基准测试太难太复杂,无法使用这样的脚本来判断哪种语言更快。

【讨论】:

    【解决方案2】:

    Node 使用JIT compiler,其设计目的是在相同的代码块以相同类型的输入多次运行并将其编译为机器代码时注意到。有可能 Node 甚至注意到这是一个纯函数并内联了一些结果,但由于这种编译器的本质,从外部很难分辨。

    CPython 是一个幼稚的解释器,它会按照你说的去做。然而,正在尝试编写一个名为PyPy 的 Python JIT(不少于 Python 编写),如您所见,目前的结果很有希望:

    $ time python2 fib.py
    9227465
    python2 fib.py  2.90s user 0.01s system 99% cpu 2.907 total
    $ time pypy fib.py
    9227465
    pypy fib.py  1.73s user 0.03s system 96% cpu 1.816 total
    

    【讨论】:

      【解决方案3】:

      如果你在 Python 中使用记忆斐波那契函数,你会发现它变得更快:

      import time
      
      beg = time.clock()
      
      def memoize(f):
          cache = {}
          def decorated_function(*args):
              if args in cache:
                  return cache[args]
              else:
                  cache[args] = f(*args)
                  return cache[args]
          return decorated_function
      
      @memoize
      def fib(n):
          if n <=2:
              return 1
          return fib(n-2) + fib(n-1)
      
      var = fib(35)
      
      end = time.clock()
      print(var)
      print(end - beg)
      

      你可以在 javascript 中做同样的事情:

      function memoize( fn ) {
          return function () {
              var args = Array.prototype.slice.call(arguments),
                  hash = "",
                  i = args.length;
              currentArg = null;
              while (i--) {
                  currentArg = args[i];
                  hash += (currentArg === Object(currentArg)) ?
                  JSON.stringify(currentArg) : currentArg;
                  fn.memoize || (fn.memoize = {});
              }
              return (hash in fn.memoize) ? fn.memoize[hash] :
              fn.memoize[hash] = fn.apply(this, args);
          };
      }
      
      var beg = new Date().getTime();
      
      function fib(n)
      {
          if (n <= 2)
              return 1;
      
          return fib(n-2) + fib(n-1);
      }
      
      var f = memoize(fib)(35);
      var end = new Date().getTime();
      
      console.log(f);
      console.log(end - beg);
      

      看起来 javascript 方面没有性能提升,这往往表明这里已经内置了某种 memoization 机制。

      学分:http://ujihisa.blogspot.fr/2010/11/memoized-recursive-fibonacci-in-python.html, http://addyosmani.com/blog/faster-javascript-memoization/

      【讨论】:

      • 这是一个测试程序,不是为了实用,我是故意这样写的。
      • 在 memoization 下运行这两个测试可以为正在发生的事情提供一些线索。
      • 问题是关于递归函数在两种环境(Paython、Node.js)中的性能,但你的答案优化了算法。
      • 提示是javascript可能使用JIT,这是@eevee确认的
      【解决方案4】:

      我会写这个作为评论,但由于我还没有足够的分数,所以我添加了另一个答案。

      正如许多答案/cmets 提到的pypy,现在比原始问题的日期晚了几年,我想我会更新最新版本的CPythonpypy运行问题中的python代码:

      Windows 7 64 位,英特尔至强 E5-1607 3GHz。

      U:>python --version
      Python 2.7.5

      U:>python fib.py
      9227465
      3.54921930198

      U:>pypy --version
      Python 2.7.3(2cec7296d7fb,2013 年 11 月 12 日,13:24:40)
      [带有 MSC v.1500 32 位的 PyPy 2.2.0]

      U:>pypy fib.py
      9227465
      0.385597246386

      因此,在这个微基准测试中,pypyCPython9 倍。看起来 node.js 仍然会更快。

      干杯,蒂姆。

      【讨论】:

        【解决方案5】:

        有一种方法可以通过使用 lru_cache() 装饰器来加速 Cpython 的递归。然后,结果会比 NodeJS 更快。在下面的链接中找到有关递归和 lru_cache() 的更多信息。

        https://realpython.com/lru-cache-python/#using-lru_cache-to-implement-an-lru-cache-in-python

        https://realpython.com/python-thinking-recursively/

        from functools import lru_cache
        import time
        beg = time.time()
        
        @lru_cache()
        def fib(n):     
            if n <=2:        
                return 1 
            return fib(n-2) + fib(n-1)  
        var = fib(35)  
        end = time.time() 
        print (var)
        print (end - beg)
        
        C:\Users\steps\Desktop>python dot.py 
        9227465
        0.0
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-11
          • 1970-01-01
          • 1970-01-01
          • 2010-11-24
          • 2013-08-18
          相关资源
          最近更新 更多