为什么使用Cython列表比使用Python列表更快?

0 投票
3 回答
2908 浏览
提问于 2025-04-18 17:45

这是我的Python代码:

X = [[0] * 1000] * 100
start = time()
for x in xrange(100):
    for i in xrange(len(X)):
        for j in xrange(len(X[i])):
            X[i][j] += 1
print  time() - start

我的Cython代码是一样的:

X = [[0] * 1000] * 100
start = time()
for x in xrange(100):
    for i in xrange(len(X)):
        for j in xrange(len(X[i])):
            X[i][j] += 1
print  time() - start

输出结果:

  • Python耗时:2.86秒
  • Cython耗时:0.41秒

有没有其他更快的方法可以在Python或Cython中实现上面的功能?

更新:有没有办法创建一个二维数组X,使得它的高度索引性能接近C/C++中的数组int X[][]?

目前我在考虑使用Python C API来完成这个任务。

还有一件事,numpy数组做同样的事情,但在纯Python和Cython中都慢得多(70秒),比列表还慢。

Python:

X = np.zeros((100,1000),dtype=np.int32)
start = time()
for x in xrange(100):
    for i in xrange(len(X)):
        for j in xrange(len(X[i])):
            X[i][j]+=1

如果要频繁访问数字数组,哪种方法是最好的?

3 个回答

0

ajcr的回答可能是最快和最简单的。你应该在cython代码中明确声明变量的数据类型。此外,我建议在外层循环中使用prange,而不是简单的range迭代器。这样可以启用OpenMP多线程,这可能会进一步加快你的代码速度,但我真的怀疑这个方法能超过numpy的实现。

1

有没有更快的方法用Python或Cython实现上面的功能?

相应的、更快的代码如下:

X = [[100 * 100] * 1000] * 100

在你的代码中,你创建了一个长度为1000的零列表,然后又创建了一个长度为100的列表,这个列表里面存的是对前面那个零列表的引用。接着,你对这个100长度的列表进行了100次循环,这样每个位置的值就被增加了100 * 100 = 10000次。

len(set(map(id, X)))
1

如果你想得到一个包含100个列表的列表:

base = [100] * 1000
X = [list(base) for _ in xrange(100)]
len(set(map(id, X)))
100

请注意,列表里面的对象引用仍然是被复制的。

4

关于你标题里的问题,你的Cython代码比Python代码快,原因是虽然没有用cdef来声明变量,但它生成了C语言代码来处理for循环(还有很多额外的C代码来描述Python对象)。为了让你的Cython代码更快,建议用cdef来声明整数ijx,这样它们就不再是Python的整数了,比如可以写成cdef int i。你还可以在Cython中声明C类型的数组,这会进一步提高性能。

如果想用NumPy快速得到相同的结果,可以试试这个方法:

X = np.zeros((100, 1000), dtype=np.int32)
X += 10000

如果可以的话,尽量不要在NumPy数组上使用for循环。因为在内存使用上,它们和列表是完全不同的。

撰写回答