为什么使用Cython列表比使用Python列表更快?
这是我的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 个回答
ajcr的回答可能是最快和最简单的。你应该在cython代码中明确声明变量的数据类型。此外,我建议在外层循环中使用prange
,而不是简单的range
迭代器。这样可以启用OpenMP多线程,这可能会进一步加快你的代码速度,但我真的怀疑这个方法能超过numpy的实现。
有没有更快的方法用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
请注意,列表里面的对象引用仍然是被复制的。
关于你标题里的问题,你的Cython代码比Python代码快,原因是虽然没有用cdef
来声明变量,但它生成了C语言代码来处理for
循环(还有很多额外的C代码来描述Python对象)。为了让你的Cython代码更快,建议用cdef
来声明整数i
、j
和x
,这样它们就不再是Python的整数了,比如可以写成cdef int i
。你还可以在Cython中声明C类型的数组,这会进一步提高性能。
如果想用NumPy快速得到相同的结果,可以试试这个方法:
X = np.zeros((100, 1000), dtype=np.int32)
X += 10000
如果可以的话,尽量不要在NumPy数组上使用for
循环。因为在内存使用上,它们和列表是完全不同的。