在Python中初始化和访问大型数组元素的有效方法是什么?
我想在Python中创建一个包含1亿个条目的数组,无符号的4字节整数,初始化为零。我想要快速数组访问,最好是连续的内存。
奇怪的是,NumPy阵列似乎表现得很慢。我可以尝试其他替代方案吗?
有array.array模块,但我没有看到有效分配1亿个条目的方法。
回应评论:
答案 0 :(得分:32)
我做了一些分析,结果完全违反直觉。 对于简单的数组访问操作, numpy和array.array比本机Python数组慢10倍。
请注意,对于数组访问,我正在执行以下形式的操作:
a[i] += 1
配置文件:
[0] * 20000000
numpy.zeros(shape =(20000000,),dtype = numpy.int32)
array.array('L',[0] * 20000000)
array.array('L',(0为范围内的i(20000000)))
答案 1 :(得分:13)
只是提醒一下Python的整数是如何工作的:如果你通过说
来分配一个列表a = [0] * K
您需要列表的内存(sizeof(PyListObject) + K * sizeof(PyObject*)
)和单个整数对象0
的内存。只要列表中的数字保持在Python用于缓存的幻数V
之下,你就可以了,因为它们是共享的,即任何指向数字n < V
的名称都指向完全相同的宾语。您可以使用以下代码段找到此值:
>>> i = 0
>>> j = 0
>>> while i is j:
... i += 1
... j += 1
>>> i # on my system!
257
这意味着只要计数超过此数字,您需要的内存为sizeof(PyListObject) + K * sizeof(PyObject*) + d * sizeof(PyIntObject)
,其中d < K
是V (== 256)
以上的整数数。在64位系统上,sizeof(PyIntObject) == 24
和sizeof(PyObject*) == 8
,即最坏情况下的内存消耗为3,200,000,000字节。
使用numpy.ndarray
或array.array
时,内存消耗在初始化后是不变的,但是您要为透明创建的包装器对象付费,正如Thomas Wouters所说。或许,您应该考虑使用Cython或scipy.weave
将更新代码(访问并增加数组中的位置)转换为C代码。
答案 2 :(得分:7)
试试这个:
x = [0] * 100000000
在我的机器上执行只需几秒钟,接近即时访问。
答案 3 :(得分:5)
如果你无法对你的计算进行矢量化,那么Python / Numpy会很慢。 Numpy很快,因为矢量化计算的发生频率低于Python。核心numpy函数都是用C或Fortran编写的。因此sum(a)
不是一个有很多访问的python循环,它只是一个低级别的C调用。
Numpy's Performance Python demo page有不同选项的好例子。通过使用较低级别的编译语言Cython或使用矢量化函数(如果可行),您可以轻松获得100倍的增长。 This blog post显示使用Cython增加了43倍的numpy用例。
答案 4 :(得分:3)
你不可能找到比numpy
array
更快的东西。数组本身的实现与C中的实现一样高效(并且与array.array
基本相同,只是更有用。)
如果你想加速你的代码,你必须这样做。即使数组是有效实现的,从Python代码访问它也有一定的开销;例如,索引数组会生成整数对象,必须动态创建。 numpy
提供了许多在C中高效实施的操作,但没有看到实际代码表现不如您想要的那么难以提出任何具体建议。
答案 5 :(得分:3)
要快速创建,请使用阵列模块。
使用阵列模块的速度比创建速度慢约5倍,但与普通列表相比,访问元素的速度要慢一倍:
# Create array
python -m timeit -s "from array import array" "a = array('I', '\x00'
* 100000000)"
10 loops, best of 3: 204 msec per loop
# Access array
python -m timeit -s "from array import array; a = array('I', '\x00'
* 100000000)" "a[4975563]"
10000000 loops, best of 3: 0.0902 usec per loop
# Create list
python -m timeit "a = [0] * 100000000"
10 loops, best of 3: 949 msec per loop
# Access list
python -m timeit -s "a = [0] * 100000000" "a[4975563]"
10000000 loops, best of 3: 0.0417 usec per loop
答案 6 :(得分:1)
除了其他优秀的解决方案之外,另一种方法是使用dict而不是数组(存在的元素不为零,否则它们为零)。查找时间为O(1)。
您还可以检查您的应用程序是否驻留在RAM中,而不是交换。它只有381 MB,但系统可能不会出于任何原因而全部给你。
答案 7 :(得分:1)
如果
您可能会对以下内容感兴趣。它初始化array.array比array.array('L',[0] * size)快27倍:
myarray = array.array('L')
f = open('/dev/zero', 'rb')
myarray.fromfile(f, size)
f.close()
在How to initialise an integer array.array object with zeros in Python我正在寻找更好的方法。
答案 8 :(得分:0)
NumPy是适用于大型固定大小的同类数组的合适工具。尽管全阵列操作通常可以以类似于C或Fortran的速度进行,但在Python中访问任何内容的各个元素并不会那么快。如果你需要快速单独对数以百万计的元素进行操作,那么你可以从Python中获得很多东西。
您正在实施哪种算法?你怎么知道如果你没有尝试过使用稀疏数组太慢了? “高效”是什么意思?你想快速初始化?那是你代码的瓶颈?
答案 9 :(得分:0)
我只是创建自己的数据类型,不会初始化任何值。
如果要读取尚未初始化的索引位置,则返回零。不过,不要初始化任何存储空间。
如果要读取已初始化HAS的索引位置,只需返回值。
如果要写入尚未初始化的索引位置,请将其初始化并存储输入。