示例
1 2 3 4 5 6 7 8 9 |
>>> a = () >>> b = () >>> id(a) == id(b) True >>> a = (1, ) >>> b = (1, ) >>> id(a) == id(b) False |
原始碼位置 Include/tupleobject.h |
Objects/tupleobject.c
結構
定義
1 2 3 4 5 |
typedef struct { PyObject_VAR_HEAD PyObject *ob_item[1]; } PyTupleObject; |
說明
1 2 3 4 5 6 7 8 |
1. PyObject_VAR_HEAD PyTupleObject在底層是個變長物件(需要儲存列表元素個數). 雖然, 在python中, tuple是不可變物件 2. PyObject *ob_item[1]; 指向儲存元素的陣列 3. |
結構
構造方法
1 |
PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size); |
構造
看下構造方法定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
PyObject * PyTuple_New(register Py_ssize_t size) { register PyTupleObject *op; Py_ssize_t i; // 大小為負數, return if (size 0) { PyErr_BadInternalCall(); return NULL; } // 如果大小=0, 空元組, 直接取free_list第一個返回 #if PyTuple_MAXSAVESIZE > 0 if (size == 0 & free_list[0]) { op = free_list[0]; Py_INCREF(op); #ifdef COUNT_ALLOCS tuple_zero_allocs++; #endif return (PyObject *) op; } // 如果free_list可分配, 從free_list取一個 if (size PyTuple_MAXSAVESIZE & (op = free_list[size]) != NULL) { // 上面 op = free_list[size] 取得單連結串列頭 // free_list指向單連結串列下一個元素, 對應位置閾值-- free_list[size] = (PyTupleObject *) op->ob_item[0]; numfree[size]--; #ifdef COUNT_ALLOCS fast_tuple_allocs++; #endif // 初始化 ob_size和ob_type /* Inline PyObject_InitVar */ #ifdef Py_TRACE_REFS Py_SIZE(op) = size; Py_TYPE(op) = &PyTuple_Type; #endif _Py_NewReference((PyObject *)op); } else #endif // free_list不可用 { // 計算空間 Py_ssize_t nbytes = size * sizeof(PyObject *); /* Check for overflow */ if (nbytes / sizeof(PyObject *) != (size_t)size || (nbytes > PY_SSIZE_T_MAX - sizeof(PyTupleObject) - sizeof(PyObject *))) { return PyErr_NoMemory(); } // 分配記憶體 op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size); if (op == NULL) return NULL; } // 初始化ob_item每個元素 for (i=0; i size; i++) op->ob_item[i] = NULL; // 第一次分配空陣列, 將其放入free_list第一個位置 #if PyTuple_MAXSAVESIZE > 0 if (size == 0) { free_list[0] = op; ++numfree[0]; Py_INCREF(op); /* extra INCREF so that this is never freed */ } #endif #ifdef SHOW_TRACK_COUNT count_tracked++; #endif _PyObject_GC_TRACK(op); // 返回 return (PyObject *) op; } |
簡化步驟
1 2 3 4 5 6 7 |
1. 如果size=0, 從free_list[0]取, 直接返回 2. 否則, 確認free_list[size], 是否可用, 可用獲取 3. 否則, 從記憶體分配新的空間 4. 初始化, 返回 |
回收
定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
static void tupledealloc(register PyTupleObject *op) { register Py_ssize_t i; // 獲取元素個數 register Py_ssize_t len = Py_SIZE(op); PyObject_GC_UnTrack(op); Py_TRASHCAN_SAFE_BEGIN(op) if (len > 0) { i = len; // 遍歷, 析構每個元素 while (--i >= 0) Py_XDECREF(op->ob_item[i]); // 與物件緩衝池相關 #if PyTuple_MAXSAVESIZE > 0 if (len ob_item[0] = (PyObject *) free_list[len]; numfree[len]++; free_list[len] = op; goto done; /* return */ } #endif } // 呼叫回收 Py_TYPE(op)->tp_free((PyObject *)op); done: Py_TRASHCAN_SAFE_END(op) } |
簡化流程
1 2 3 4 5 |
1. 回收ob_item每個元素 2. 如果符合條件, 放入到free_list 3. 否則, 回收 |
tuple物件緩衝池
定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* Speed optimization to avoid frequent malloc/free of small tuples */ #ifndef PyTuple_MAXSAVESIZE #define PyTuple_MAXSAVESIZE 20 #endif #ifndef PyTuple_MAXFREELIST #define PyTuple_MAXFREELIST 2000 #endif #if PyTuple_MAXSAVESIZE > 0 static PyTupleObject *free_list[PyTuple_MAXSAVESIZE]; static int numfree[PyTuple_MAXSAVESIZE]; #endif |
結論
1 2 3 4 |
1. 作用: 優化小tuple的mall/free 2. PyTuple_MAXSAVESIZE = 20 會被快取的tuple長度閾值, 20, 長度 |
free_list的結構
回頭看回收跟物件緩衝池相關的邏輯
條件:
1 2 3 |
if (len < PyTuple_MAXSAVESIZE && // len < 20 numfree[len] < PyTuple_MAXFREELIST && // numfree[len] < 2000 Py_TYPE(op) == &PyTuple_Type) // 是tuple型別 |
操作
1 2 3 4 |
op->ob_item[0] = (PyObject *) free_list[len]; //ob_item指向free_list[len] 單連結串列頭 numfree[len]++; // len位置計數+1 free_list[len] = op; // op變成單連結串列的頭 goto done; /* return */ |
即過程
1 2 3 4 5 6 7 8 9 |
1. 如果size=0, 直接從free_list[0]取 2. 如果size!=0, 判斷size 20 走記憶體分配邏輯 ------------------ 回收時 如果size |
注意
1 2 3 |
1. 回收時, ob_item都會被回收, 只是本身物件快取下來 2. 這裡free_list, 複用ob_item作為連結串列指標, 指向下一個位置(通用整數物件池也是複用指標的方式, 不過用的是ob_type) |
changelog
1 |
2014-08-10 first version |
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式