高效的Python数组有1亿个零?

时间:2010-02-06 20:46:40

标签: python arrays performance

在Python中初始化和访问大型数组元素的有效方法是什么?

我想在Python中创建一个包含1亿个条目的数组,无符号的4字节整数,初始化为零。我想要快速数组访问,最好是连续的内存。

奇怪的是,NumPy阵列似乎表现得很慢。我可以尝试其他替代方案吗?

array.array模块,但我没有看到有效分配1亿个条目的方法。

回应评论:

  • 我不能使用稀疏数组。这个算法太慢了,因为阵列变得非常快。
  • 我知道Python已被解释,但肯定有办法进行快速数组操作吗?
  • 我做了一些分析,每秒使用NumPy获得大约160K的数组访问(按索引查找或更新元素)。这似乎很慢。

10 个答案:

答案 0 :(得分:32)

我做了一些分析,结果完全违反直觉。 对于简单的数组访问操作, numpy和array.array比本机Python数组慢10倍

请注意,对于数组访问,我正在执行以下形式的操作:

a[i] += 1

配置文件:

  • [0] * 20000000

    • 访问:2.3M /秒
    • 初始化:0.8s
  • numpy.zeros(shape =(20000000,),dtype = numpy.int32)

    • 访问:160K /秒
    • 初始化:0.2s
  • array.array('L',[0] * 20000000)

    • 访问:175K /秒
    • 初始化:2.0s
  • array.array('L',(0为范围内的i(20000000)))

    • 访问:175K /秒,大概是基于其他array.array
    • 的配置文件
    • 初始化:6.7s

答案 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 < KV (== 256)以上的整数数。在64位系统上,sizeof(PyIntObject) == 24sizeof(PyObject*) == 8,即最坏情况下的内存消耗为3,200,000,000字节。

使用numpy.ndarrayarray.array时,内存消耗在初始化后是不变的,但是您要为透明创建的包装器对象付费,正如Thomas Wouters所说。或许,您应该考虑使用Cythonscipy.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,但系统可能不会出于任何原因而全部给你。

然而,还有一些非常快速的稀疏矩阵(SciPyndsparse)。它们是在低级别C中完成的,也可能是好的。

答案 7 :(得分:1)

如果

  • 您的应用程序可接受array.array的访问速度
  • 紧凑型存储是最重要的
  • 您想使用标准模块(没有NumPy依赖关系)
  • 您使用的是/ dev / zero
  • 的平台

您可能会对以下内容感兴趣。它初始化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的索引位置,只需返回值。

如果要写入尚未初始化的索引位置,请将其初始化并存储输入。