高性能Python-内存利用(元组vs列表)

586 查看

从内存利用和CPU利用开始了解List和Tuple的优缺点

定义

List:动态数组,元素可变,可改变大小(append,resize)
Tuple:静态数组,不可变,数据一旦创建后不可改变

List的内存利用

  • 当创建N个元素的List时,Python的动态内存分配长N+1个元素的内存,第一个元素存储列表长度,和列表的元信息。
  • 当Append一个元素时,Python将创建一个足够大的列表,来容纳N个元素和将要被追加的元素。重新创建的列表长度大于N+1(虽然我们只触发一次append操作),实际上,为了未来的Append操作,M个元素长度(M>N+1)的内存将会被额外分配,然后,旧列表中的数据被copy到新列表中,旧列表销毁。
  • 额外内存的分配,只会发生在第一次Append操作时,当我们创建普通列表时,不会额外分配内存。
  • 这里的哲学是,一个Append操作很可能是很多Append操作的开始,通过额外分配内存来减少可能的内存分配和内存copy的次数。
  • 那么,对于一个具有N个元素的列表,当一次Append操作发生时,新列表要分配多少内存(额外M个元素,需多分配一个元素存储长度)呢?答案是:

M = (N >> 3) + (N <9 ? 3 : 6) + 1

举例:
1)当一个具有100,000,000个元素的列表发生一次Append后,我们会实际分配一个长为112,500,007个元素长度的新列表。
2)当一个列表 l = [1, 2], l.append(3)后,M = (2 >> 3) + 3 + 1= 4, 新列表长度为4,l = [1,2,3, -]


Tuple的内存利用

  • 虽然Tuple不支持resize,但是我们可以粘贴两个元祖组成一个新的元组,这个操作类似于List的append,但是又不会额外的分配内存。但我们不能把它当成append,因为每次都会进行一个分配内存和内存copy操作。
  • 另一个Tuple的静态本质带来的好处是,resource caching。Python是garbage collected,当一个变量不用了,内存会被回收并交回给OS。但是,对于一个20个元素的Tuple,当它不再被用时,内存不会立即返还给OS,而是为了以后应用而暂缓保留,当一个新的Tuple被创建时,我们不会向OS重新申请分配内存,而是用现有reserved的free memory。
  • 也就是,Tuple的创建很简单并且避免频繁与OS申请内存,创建一个具有10个元素的Tuple比创建一个List要快不少,55ns VS 280 ns。