亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

與 sys.getsizeof() 的結果相比,整數的大內存占用

與 sys.getsizeof() 的結果相比,整數的大內存占用

慕田峪7331174 2022-01-05 10:05:51
范圍內的 Python-Integer-objects[1,2^30)需要28字節,如sys.getsizeof()在此 SO-post 中提供和解釋的那樣。但是,當我使用以下腳本測量內存占用時:#int_list.py:import sysN=int(sys.argv[1])lst=[0]*N            # no overallocationfor i in range(N):    lst[i]=1000+i    # ints not from integer pool通過/usr/bin/time -fpeak_used_memory:%M python3 int_list.py <N>我得到以下峰值內存值(Linux-x64,Python 3.6.2):   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所以看起來好像40.5每個整數對象都需要12.5字節,即字節比產生的多sys.getsizeof()。額外的8字節很容易解釋——列表lst不包含整數對象,而是對它們的引用——這意味著需要一個額外的指針,即8字節。但是,其他4.5字節呢,它們是做什么用的?可以排除以下原因:整數對象的大小是可變的,但10^7小于2^30因此所有整數都將是28字節大。list 中沒有過度分配lst,可以很容易地檢查sys.getsizeof(lst)它,它產生8了元素數量的倍數,加上非常小的開銷。
查看完整描述

2 回答

?
暮色呼如

TA貢獻1853條經驗 獲得超9個贊

longint@Nathan的建議令人驚訝地不是解決方案,因為 CPython 的實現有一些微妙的細節。根據他的解釋,內存占用為


...

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

應該仍然是40.52,因為sys.sizeof(1<<30)是32,但測量結果表明它是48.56。另一方面,對于


...

lst[i] = (1<<60)+i

48.56盡管事實上,足跡仍然sys.sizeof(1<<60)是36。


原因是:sys.getsizeof()不告訴我們真實的內存占用為求和的結果,即a+b是


32 字節 1000+i

36 字節用于 (1<<30)+i

40 字節用于 (1<<60)+i

發生這種情況,因為當兩個整數的相加x_add,所得到的整數,在具有第一個“數字”,即4個字節,超過最大的a和b:


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);

};


即可能的前導零被丟棄,但內存沒有釋放 - 4 個字節不值得,函數的來源可以在這里找到。


現在,我們可以使用@Nathans 的洞察力來解釋為什么(1<<30)+iis48.56和 not的足跡44.xy:使用的py_malloc-allocator 使用內存塊對齊8字節,這意味著36字節將存儲在一個大小的塊中40- 與結果相同(1<<60)+i(記住指針的額外 8 字節)。


為了解釋剩余的0.5字節,我們需要更深入地了解py_malloc-allocator 的細節。一個很好的概述是源代碼本身,我最后一次嘗試描述它可以在這個SO-post 中找到。


簡而言之,分配器管理 arenas 中的內存,每個內存為 256MB。分配 arena 時,會保留內存,但不會提交。我們將內存視為“已使用”,只有當一個所謂的pool被觸及時。池4Kb很大 ( POOL_SIZE) 并且僅用于具有相同大小的內存塊 - 在我們的例子中是32字節。這意味著 的分辨率peak_used_memory為 4Kb,不能對這些0.5字節負責。


但是,必須管理這些池,這會導致額外的開銷:每個池都py_malloc需要一個pool_header:


/* 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      */

};

這個結構的大小在我的 Linux_64 機器上是48(稱為POOL_OVERHEAD)字節。這pool_header是池的一部分(避免通過 cruntime-memory-allocator 進行額外分配的一種非常聰明的方法),并將取代兩個32字節塊,這意味著池有用于126 32字節整數的位置:


/* Return total number of blocks in pool of size index I, as a uint. */

#define NUMBLOCKS(I) ((uint)(POOL_SIZE - POOL_OVERHEAD) / INDEX2SIZE(I))

這導致:


4Kb/126 = 32.51的字節足跡1000+i,加上額外的 8 個字節的指針。

(30<<1)+i需要40字節,這意味著4Kb有102塊的地方,其中一個(16當池劃分為40-bytes塊時還有剩余的字節,它們可以用于pool_header)用于pool_header,這導致4Kb/101=40.55字節(加上8字節指針)。

我們還可以看到,還有一些額外的開銷,負責 ca。0.01每個整數字節 - 不足以讓我關心。


查看完整回答
反對 回復 2022-01-05
?
智慧大石

TA貢獻1946條經驗 獲得超3個贊

int對象只需要 28 個字節,但 Python 使用 8 字節對齊:內存分配在大小為 8 個字節的倍數的塊中。所以每個int對象實際使用的內存是 32 字節。有關更多詳細信息,請參閱這篇關于Python 內存管理的優秀文章。

我還沒有對剩余的半字節做出解釋,但如果我找到了,我會更新它。


查看完整回答
反對 回復 2022-01-05
  • 2 回答
  • 0 關注
  • 266 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號