如何高效创建包含一亿零的Python数组?

30 投票
10 回答
41219 浏览
提问于 2025-04-15 18:58

在Python中,有什么高效的方法来初始化和访问一个大数组的元素呢?

我想在Python里创建一个包含1亿个条目的数组,这些条目是无符号的4字节整数,初始值都设为零。我希望能快速访问这个数组,最好是使用连续的内存。

奇怪的是,NumPy数组的表现似乎很慢。我还有其他可以尝试的替代方案吗?

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

对评论的回复:

  • 我不能使用稀疏数组。因为这个算法会让数组很快变得密集,这样会太慢。
  • 我知道Python是解释型语言,但肯定有办法进行快速的数组操作吧?
  • 我做了一些性能测试,发现使用NumPy时,每秒大约能进行16万次数组访问(通过索引查找或更新一个元素)。这似乎很慢。

10 个回答

8

试试这个:

x = [0] * 100000000

在我这台电脑上执行只需要几秒钟,而且访问几乎是瞬间的。

14

这里提醒一下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.ndarrayarray.array时,初始化后内存消耗是固定的,但你需要为那些透明创建的包装对象付出代价,正如Thomas Wouters所说的那样。也许你应该考虑把更新代码(访问并增加数组中位置的代码)转换成C代码,可以使用Cython或者scipy.weave来实现。

36

我做了一些性能测试,结果让我感到非常意外。对于简单的数组访问操作,numpy和array.array的速度比原生Python数组慢了10倍

需要注意的是,在数组访问时,我进行的操作是这样的:

a[i] += 1

性能测试结果:

  • [0] * 20000000

    • 访问速度:每秒2.3百万次
    • 初始化时间:0.8秒
  • numpy.zeros(shape=(20000000,), dtype=numpy.int32)

    • 访问速度:每秒16万次
    • 初始化时间:0.2秒
  • array.array('L', [0] * 20000000)

    • 访问速度:每秒17.5万次
    • 初始化时间:2.0秒
  • array.array('L', (0 for i in range(20000000)))

    • 访问速度:每秒17.5万次,推测是基于其他array.array的性能测试结果
    • 初始化时间:6.7秒

撰写回答