【问题标题】:Reserve memory for list in Python?为Python中的列表保留内存?
【发布时间】:2010-10-06 22:19:06
【问题描述】:

在 Python 中编程时,是否可以为将填充已知数量项目的列表保留内存,以便在构建时不会多次重新分配列表?我查看了 Python 列表类型的文档,但没有找到任何似乎可以做到这一点的东西。但是,这种类型的列表构建会出现在我的代码的一些热点中,所以我想尽可能提高它的效率。

编辑:另外,用 Python 这样的语言做这样的事情是否有意义?我是一个相当有经验的程序员,但对 Python 很陌生,并且仍然对它的做事方式有所了解。 Python 是否在内部将 所有 对象分配到单独的堆空间中,从而违背了尝试最小化分配的目的,还是将 int、float 等原语直接存储在列表中?

【问题讨论】:

标签: python performance arrays memory-management list


【解决方案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])*Nnumpy.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
      
      1. 追加()
      2. [无]*N
      3. 使用模块数组
      4. 使用模块 numpy

      【讨论】:

        猜你喜欢
        • 2019-08-11
        • 1970-01-01
        • 2018-07-05
        • 1970-01-01
        • 2021-06-05
        • 1970-01-01
        • 1970-01-01
        • 2015-06-14
        • 1970-01-01
        相关资源
        最近更新 更多