Python 与sys.getsizeof()的结果相比,整数的内存占用较大

Python 与sys.getsizeof()的结果相比,整数的内存占用较大,python,python-3.x,performance,cpython,python-internals,Python,Python 3.x,Performance,Cpython,Python Internals,范围[1,2^30)中的Python整数对象需要28字节,如sys.getsizeof()所提供,并在中进行了示例说明 但是,当我使用以下脚本测量内存占用时: #int_list.py: import sys N=int(sys.argv[1]) lst=[0]*N # no overallocation for i in range(N): lst[i]=1000+i # ints not from integer pool 通过 因此,似乎每个整数对

范围
[1,2^30)
中的Python整数对象需要
28
字节,如
sys.getsizeof()
所提供,并在中进行了示例说明

但是,当我使用以下脚本测量内存占用时:

#int_list.py:
import sys

N=int(sys.argv[1])
lst=[0]*N            # no overallocation

for i in range(N):
    lst[i]=1000+i    # ints not from integer pool
通过

因此,似乎每个整数对象需要
40.5
字节,即
12.5
字节比
sys.getsizeof()
产生的字节多

额外的
8
字节很容易解释-列表
lst
不包含整数对象,而是引用它们-这意味着需要额外的指针,即
8
字节

但是,另一个
4.5
字节呢,它们的用途是什么

可以排除以下原因:

  • 整数对象的大小是可变的,但
    10^7
    小于
    2^30
    ,因此所有整数都将
    28
    字节大
  • 列表
    lst
    中没有过度分配,可以通过
    sys.getsizeof(lst)
    轻松检查,它产生
    8倍的元素数,外加非常小的开销

    • int对象只需要28个字节,但Python使用8字节对齐方式:内存分配在大小为8字节倍数的块中。因此每个int对象实际使用的内存为32字节。有关详细信息,请参阅上的这篇优秀文章


      对于剩余的半字节我还没有解释,但如果我找到了,我会更新它。

      @Nathan的建议出乎意料地不是解决方案,因为CPython的
      longint
      -实现有一些微妙的细节

      ...
      lst[i] = (1<<30)+i
      

      尽管事实上,
      sys.sizeof(1实际上是一个很好的猜测!但是情况更微妙。请参阅我的答案(也解释了0.5字节)。
      
         N     Peak memory in Kb        bytes/integer
      -------------------------------------------   
         1            9220              
         1e7        404712                40.50 
         2e7        800612                40.52 
         3e7       1196204                40.52
         4e7       1591948                40.52
      
      ...
      lst[i] = (1<<30)+i
      
      ...
      lst[i] = (1<<60)+i
      
      static PyLongObject *
      x_add(PyLongObject *a, PyLongObject *b)
      {
          Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b));
          PyLongObject *z;
          ...
          /* Ensure a is the larger of the two: */
          ...
          z = _PyLong_New(size_a+1);  
          ...
      
       ...
       return long_normalize(z);
      
      /* Pool for small blocks. */
      struct pool_header {
          union { block *_padding;
                  uint count; } ref;          /* number of allocated blocks    */
          block *freeblock;                   /* pool's free list head         */
          struct pool_header *nextpool;       /* next pool of this size class  */
          struct pool_header *prevpool;       /* previous pool       ""        */
          uint arenaindex;                    /* index into arenas of base adr */
          uint szidx;                         /* block size class index        */
          uint nextoffset;                    /* bytes to virgin block         */
          uint maxnextoffset;                 /* largest valid nextoffset      */
      };
      
      /* Return total number of blocks in pool of size index I, as a uint. */
      #define NUMBLOCKS(I) ((uint)(POOL_SIZE - POOL_OVERHEAD) / INDEX2SIZE(I))