为Python中的列表保留内存?
Posted
技术标签:
【中文标题】为Python中的列表保留内存?【英文标题】:Reserve memory for list in Python? 【发布时间】:2010-10-06 22:19:06 【问题描述】:在 Python 中编程时,是否可以为将填充已知数量项目的列表保留内存,以便在构建时不会多次重新分配列表?我查看了 Python 列表类型的文档,但没有找到任何似乎可以做到这一点的东西。但是,这种类型的列表构建会出现在我的代码的一些热点中,所以我想尽可能提高它的效率。
编辑:另外,用 Python 这样的语言做这样的事情是否有意义?我是一个相当有经验的程序员,但对 Python 很陌生,并且仍然对它的做事方式有所了解。 Python 是否在内部将 所有 对象分配到单独的堆空间中,从而违背了尝试最小化分配的目的,还是将 int、float 等原语直接存储在列表中?
【问题讨论】:
@ironfroggy:关键是这个出现在热点中。在这些地方,列表构建造成了现实世界的重大瓶颈,您应该优化这种瓶颈。 Python - Create a list with initial capacity 的可能重复项 【参考方案1】:您可以像这样创建已知长度的列表:
>>> [None] * known_number
【讨论】:
【参考方案2】:在大多数日常代码中,您不需要这种优化。
但是,当列表效率成为问题时,您应该做的第一件事是将通用列表替换为来自array
module 的类型列表,这样效率更高。
创建 400 万个浮点数列表的方法如下:
import array
lst = array.array('f', [0.0]*4000*1000)
【讨论】:
您所说的“效率更高”是什么意思?array.array
可能需要更少的内存,但在大多数情况下(即我尝试过的情况),Python 列表更快。
在这种情况下,它甚至首先创建一个列表,然后从列表中创建一个数组。这效率不高。【参考方案3】:
在 Python 中,所有对象都分配在堆上。
但是 Python 使用了一个特殊的内存分配器,所以 malloc
不会在你每次需要新对象时被调用。
对于缓存的小整数(等)也有一些优化;但是,哪些类型以及如何实现取决于实现。
【讨论】:
【参考方案4】:如果您想在 Python 中有效地处理数字,请查看 NumPy ( http://numpy.scipy.org/)。它让您可以非常快速地做事,同时仍然可以使用 Python。
要完成你在 NumPy 中的要求,你会做类似的事情
import numpy as np
myarray = np.zeros(4000)
这会给你一个初始化为零的浮点数数组。然后你可以做一些非常酷的事情,比如将整个数组乘以一个因子或其他数组和其他东西(有点像在 Matlab 中,如果你曾经使用过),这非常快(大部分实际工作都发生在NumPy 库的高度优化的 C 部分)。
如果它不是你之后的数字数组,那么你可能不会找到在 Python 中做你想做的事情的方法。 Python 对象列表是内部对象的点列表(无论如何我认为是这样,我不是 Python 内部的专家),因此它仍然会在您创建它们时分配其每个成员。
【讨论】:
正如我在@Mikhail Korobov 的回答中所说,np.empty
更可取,除非你真的需要你的数组从零开始,从而使我的计算机速度提高三倍。【参考方案5】:
这里有四种变体:
增量列表创建 “预分配”列表 array.array() numpy.zeros()
python -mtimeit -s"N=10**6" "a = []; app = a.append;"\
"for i in xrange(N): app(i);"
10 loops, best of 3: 390 msec per loop
python -mtimeit -s"N=10**6" "a = [None]*N; app = a.append;"\
"for i in xrange(N): a[i] = i"
10 loops, best of 3: 245 msec per loop
python -mtimeit -s"from array import array; N=10**6" "a = array('i', [0]*N)"\
"for i in xrange(N):" " a[i] = i"
10 loops, best of 3: 541 msec per loop
python -mtimeit -s"from numpy import zeros; N=10**6" "a = zeros(N,dtype='i')"\
"for i in xrange(N):" " a[i] = i"
10 loops, best of 3: 353 msec per loop
这表明在这种情况下[None]*N
是最快的,array.array
是最慢的。
【讨论】:
我认为array.array
在这里的使用不是最理想的,请看我的回答。
@MikhailKorobov:很好的发现。 array('i', [0])*n
along 比 array('i', [0]*n)
快 10 倍,但如果添加初始化循环,它仍然比 [0]*n
变体慢。答案的重点:先测量。代码示例来自当时的其他答案。
这似乎对 numpy 和 array 有点不公平,因为您包括了导入时间,这可能会在很多调用中分摊。 @MikhailKorobov 的结果似乎表明,一旦导入 numpy,速度就会快很多。
@MattKrause:import
不包括在内,请注意-s
@J.F.Sebastian using a = [0]*n 所有元素都有相同的引用,即如果我执行 a[0] = 1,我将得到 a = [1]*n。我想知道的是分配新内存时结果是否相同?还是我做错了什么?【参考方案6】:
看看这个:
In [7]: %timeit array.array('f', [0.0]*4000*1000)
1 loops, best of 3: 306 ms per loop
In [8]: %timeit array.array('f', [0.0])*4000*1000
100 loops, best of 3: 5.96 ms per loop
In [11]: %timeit np.zeros(4000*1000, dtype='f')
100 loops, best of 3: 6.04 ms per loop
In [9]: %timeit [0.0]*4000*1000
10 loops, best of 3: 32.4 ms per loop
所以永远不要使用array.array('f', [0.0]*N)
,使用array.array('f', [0.0])*N
或numpy.zeros
。
【讨论】:
如果您要设置数组元素而不是添加它们,您可能不需要零,只需要为每个元素保留一些空间。在这种情况下,要走的路是np.empty
代替np.zeros
。通过您的测试,在我的计算机上速度提高了三倍。【参考方案7】:
对于 Python3:
import timeit
from numpy import zeros
from array import array
def func1():
N=10**6
a = []
app = a.append
for i in range(N):
app(i)
def func2():
N=10**6
a = [None]*N
app = a.append
for i in range(N):
a[i] = i
def func3():
N=10**6
a = array('i', [0]*N)
for i in range(N):
a[i] = i
def func4():
N=10**6
a = zeros(N,dtype='i')
for i in range(N):
a[i] = i
start_time = timeit.default_timer()
func1()
print(timeit.default_timer() - start_time)
start_time = timeit.default_timer()
func2()
print(timeit.default_timer() - start_time)
start_time = timeit.default_timer()
func3()
print(timeit.default_timer() - start_time)
start_time = timeit.default_timer()
func4()
print(timeit.default_timer() - start_time)
结果:
0.1655518
0.10920069999999998
0.1935983
0.15213890000000002
-
追加()
[无]*N
使用模块数组
使用模块 numpy
【讨论】:
以上是关于为Python中的列表保留内存?的主要内容,如果未能解决你的问题,请参考以下文章