C和python中insert-sort的性能差异

时间:2009-10-13 17:04:50

标签: python c performance profiling

我很好奇使用C和python进行insert-sort的性能,但是我得到的结果让我想到如果我做错了什么。我怀疑C会更快,但不是那么多。

我已经分析了这两个代码,而insert-sort函数是花费时间最多的地方。

这是C函数:

void
insert_sort (vec_t * vec)
{
    int j;
    for (j = 1 ; j < vec->n ; j++){
        int key = vec->v[j];
        int i = j - 1;
        while (i >= 0 && vec->v[i] > key){
            vec->v[i+1] = vec->v[i];
            i--;
        }
        vec->v[i+1] = key;
    }
}

这是python函数:

def insert_sort (ln):
    for j in range(1, len(ln)):
        key = ln[j]
        i = j-1
        while i >= 0 and ln[i] > key:
            ln[i+1] = ln[i]
            i-=1
        ln[i+1] = key

用10000个整数进行测试,每个整数随机生成0到10000之间。

每个函数花费的时间的结果是:

  • C时间:0.13秒
  • python time:8.104秒

我在这里做错了吗?就像我说的,我希望看到C代码更快,但速度不快。

我不想使用内置函数或任何其他函数。我想实现这个算法。是否有 pythonic 方法可以在insert-sort中使用?

5 个答案:

答案 0 :(得分:13)

Python是一种动态语言,标准实现使用解释器来评估代码。这意味着编译后的C代码可以通过单个机器指令进行转义,例如分配给vec-&gt; v [i + 1],Python的解释器必须从本地范围中查找序列变量,查找其类,在类上找到项设置方法,调用该方法。同样的比较,添加。更不用说执行几乎每个字节码都会导致CPU中的间接分支误预测导致管道泡沫。

这种代码可以从JIT编译到本机代码和运行时类型专业化中受益匪浅,就像unladen-swallow和PyPy一样。

否则代码几乎是pythonic,因为如果需要实现插入排序,这就是人们如何在Python中实现它。它也是非常不合理的,因为你应该使用非常有效的内置排序。

答案 1 :(得分:5)

我的第一个想法是,我现在手头的笔记本电脑,一台Macbook Pro,必须与你的机器相当但略胜一筹 - 我没有足够的周围代码来尝试你的C例子(什么是一个vec_t等等,但运行你编码的Python给我:

$ python -mtimeit -s'import inso' 'inso.insort(inso.li)'
10 loops, best of 3: 7.21 msec per loop

vs 8.1秒。这就是您放入insort.py的代码,前面是:

import random
li = [random.randrange(10000) for _ in xrange(10000)]

array没有帮助 - 实际上减慢了一些事情。然后我安装了psyco,Python JIT帮助程序(仅限x86,仅限32位),进一步添加:

import psyco
psyco.full()

得到了:

$ python -mtimeit -s'import inso' 'inso.insort(inso.li)'
10 loops, best of 3: 207 usec per loop

所以加速大约是7.21 / 0.000207 = 34830倍 - 相比之下8.04 / 0.13 = 62倍让你大吃一惊; - )。

当然,问题是在第一次之后,列表已经排序,因此insort必须更快。您没有给我们足够的周围测试工具来确切地知道您测量的 。一个更现实的例子(没有触及实际列表,因此保持无序,只有副本被排序......),没有psyco:

$ python -mtimeit -s'import inso' 'inso.insort(list(inso.li))'
10 loops, best of 3: 13.8 sec per loop

哎呀 - 所以你的机器比Macbook Pro更快(记得,核心不计算:我们在这里只使用一个;-) - 哇...否则,你是错误的测量。无论如何,与psyco:

$ python -mtimeit -s'import inso' 'inso.insort(list(inso.li))'
10 loops, best of 3: 456 msec per loop

因此psyco的加速仅为13.8 / 0.456,30倍 - 大约是纯C编码60倍以上的一半。 IOW,你希望python + psyco的速度是纯C的两倍。这是一个更现实和典型的评估。

如果我们编写合理的高级代码,psyco的速度会从(比如说)30倍降低到更低 - 但C的优势也会超过Python。例如,

$ python -mtimeit -s'import inso' 'sorted(inso.li)'
100 loops, best of 3: 8.72 msec per loop

没有psyco(在这种情况下,psyco实际上是 - 稍微 - 减慢执行;-),这是另一个因素而不是psyco,比非psyco insort总计1582。< / p>

但是,由于某种原因,你必须在python中编写极低级别的算法,而不是使用内置函数和stdlib的大量支持,psyco可以帮助减轻痛苦。

另一点是,当您进行基准测试时,请发布所有代码,以便其他人可以看到完全您正在做什么(并且可能发现陷阱) - 您的“脚手架”非常棘手且很可能隐藏陷阱,就像你想象你正在测量的代码一样! - )

答案 2 :(得分:4)

所以,这里有一些你应该从中汲取的教训:

  • 解释Python的速度很慢。不要试图用Python编写自己的FFT,MPEG编码器等。

  • 即使是缓慢解释的Python,对于小问题也可能足够快。 8秒的运行时间并不可怕,编写和调试C所需的时间比Python要长,所以如果你要写一些东西运行一次,Python就会胜出。

  • 为了提高Python的速度,请尝试依赖内置功能和C模块。让别人的C代码做繁重的工作。我在一个嵌入式设备上工作,我们用Python完成了我们的工作;尽管嵌入式处理器速度很慢,但性能还算不错,因为C库模块正在完成大部分工作。

为了娱乐和教育,请重复您的Python测试,这一次使用列表中的内置.sort()方法;它可能不会像C那么快,但它会很接近。 (虽然对于非常大的数据集,它会击败C,因为插入类很糟糕。如果你重写了C以使用C库qsort()函数,那将是速度冠军。)

常见的Python设计“模式”是:首先,用Python编写应用程序。如果它足够快,停止;你完成了其次,尝试重写以提高速度;例如,查看是否有可以使用的C模块。如果仍然不够快,可以考虑编写自己的C模块;或者,编写一个C程序,使用Python原型代码作为设计的基础。

答案 3 :(得分:2)

您使用什么方法来衡量时间?
做这种事情,我发现python至少比C
慢30倍 C编译器可能能够使用Python甚至不尝试的一些优化

如果尝试psyco可能会很有趣,那么这种类型的代码非常适合它。

基于Alex的回答,我尝试了cython。在他的情况下,cython将for循环和所有内容转换为纯C,所以现在我可以比较C,python和psyco

现在我有这个insort.py


import psyco
import random
li = [random.randrange(10000) for _ in xrange(10000)]

def insort (ln):
    for j in range(1, len(ln)):
        key = ln[j]
        i = j-1
        while i >= 0 and ln[i] > key:
            ln[i+1] = ln[i]
            i-=1
        ln[i+1] = key

#psyco.bind(insort)

import pyximport; pyximport.install()
import pyxinsort

def pyx_setup():
    pyxinsort.setup(li)

def pyx_insort():
    pyxinsort.insort(li)

和这个pyxinsort.pyx


cdef int ln[10000]

def insort(li):
    cdef int i,j,key
    for j in range(1, len(li)):
        key = ln[j]
        i = j-1
        while i >= 0 and ln[i] > key:
            ln[i+1] = ln[i]
            i-=1
        ln[i+1] = key

def setup(li):
    cdef int i
    for i in range(1, len(li)):
        ln[i]=li[i]

insort的代码几乎完全相同。 li传入了它的长度。 ln是已排序并由设置预先填充的数组,因此我可以隔离从排序中构建列表

$ python2.5 -mtimeit -s'import inso' 'list(inso.li)'
10000 loops, best of 3: 84.5 usec per loop
$ python2.5 -mtimeit -s'import inso' 'inso.insort(list(inso.li))'
10 loops, best of 3: 21.9 sec per loop

Psyco的

$ python2.5 -mtimeit -s'import inso' 'list(inso.li)'
10000 loops, best of 3: 85.6 usec per loop
$ python2.5 -mtimeit -s'import inso' 'inso.insort(list(inso.li))'
10 loops, best of 3: 578 msec per loop

cython(这与运行完全相同的算法转换为C并编译)

$ python2.5 -mtimeit -s'import inso' 'inso.pyx_setup()'
10000 loops, best of 3: 141 usec per loop
$ python2.5 -mtimeit -s'import inso' 'inso.pyx_setup();inso.pyx_insort()'
10 loops, best of 3: 46.6 msec per loop

cython以16倍的速度击败psyco,将Python击败470倍!

为了完整起见,我已经包含了cython生成的相应C代码


  for (__pyx_v_j = 1; __pyx_v_j < __pyx_1; __pyx_v_j+=1) {
    __pyx_v_key = (__pyx_v_9pyxinsort_ln[__pyx_v_j]);
    __pyx_v_i = (__pyx_v_j - 1);
    while (1) {
      __pyx_2 = (__pyx_v_i >= 0);
      if (__pyx_2) {
        __pyx_2 = ((__pyx_v_9pyxinsort_ln[__pyx_v_i]) > __pyx_v_key);
      }
      if (!__pyx_2) break;
      (__pyx_v_9pyxinsort_ln[(__pyx_v_i + 1)]) = (__pyx_v_9pyxinsort_ln[__pyx_v_i]);
      __pyx_v_i -= 1;
    }
    (__pyx_v_9pyxinsort_ln[(__pyx_v_i + 1)]) = __pyx_v_key;
  }

答案 4 :(得分:-3)

出了什么问题:

ln.sort()