02PyListObject: 列表对象的扩容机制

丶灬走出姿态 提交于 2020-10-28 14:36:17

list object

  • 可变对象头
  • ob_item: 指向分配的空间,元素是指针
  • allocated: 真实容量.

Objects/listobjects.c

/* Ensure ob_item has room for at least newsize elements, and set
 * ob_size to newsize.  If newsize > ob_size on entry, the content
 * of the new slots at exit is undefined heap trash; it's the caller's
 * responsibility to overwrite them with sane values.
 * The number of allocated elements may grow, shrink, or stay the same.
 * Failure is impossible if newsize <= self.allocated on entry, although
 * that partly relies on an assumption that the system realloc() never
 * fails when passed a number of bytes <= the number of bytes last
 * allocated (the C standard doesn't guarantee this, but it's hard to
 * imagine a realloc implementation where it wouldn't be true).
 * Note that self->ob_item may change, and even if newsize is less
 * than ob_size on entry.
 */
static int
list_resize(PyListObject *self, Py_ssize_t newsize)
{
    PyObject **items;
    size_t new_allocated, num_allocated_bytes;
    Py_ssize_t allocated = self->allocated;

    /* Bypass realloc() when a previous overallocation is large enough
       to accommodate the newsize.  If the newsize falls lower than half
       the allocated size, then proceed with the realloc() to shrink the list.
    */
    if (allocated >= newsize && newsize >= (allocated >> 1)) {
        assert(self->ob_item != NULL || newsize == 0);
        Py_SET_SIZE(self, newsize);
        return 0;
    }

    /* This over-allocates proportional to the list size, making room
     * for additional growth.  The over-allocation is mild, but is
     * enough to give linear-time amortized behavior over a long
     * sequence of appends() in the presence of a poorly-performing
     * system realloc().
     * Add padding to make the allocated size multiple of 4.
     * The growth pattern is:  0, 4, 8, 16, 24, 32, 40, 52, 64, 76, ...
     * Note: new_allocated won't overflow because the largest possible value
     *       is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
     */
    new_allocated = ((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3;
    /* Do not overallocate if the new size is closer to overallocated size
     * than to the old size.
     */
    if (newsize - Py_SIZE(self) > (Py_ssize_t)(new_allocated - newsize))
        new_allocated = ((size_t)newsize + 3) & ~(size_t)3;

    if (newsize == 0)
        new_allocated = 0;
    num_allocated_bytes = new_allocated * sizeof(PyObject *);
    items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);
    if (items == NULL) {
        PyErr_NoMemory();
        return -1;
    }
    self->ob_item = items;
    Py_SET_SIZE(self, newsize);
    self->allocated = new_allocated;
    return 0;
}

list resize

import sys


def print_list_size(nums=None):
    nums_size = sys.getsizeof(nums)
    if nums:
        print(f"id({id(nums)}) v:{nums[-1]} len:{len(nums)} size:{nums_size}")
    else:
        print(f"id({id(nums)}) v:{nums} len:{len(nums)} size:{nums_size}")


def test_0_4():
    data = []
    print_list_size(nums=data)
    for i in range(1, 35):
        data.append(i)
        print_list_size(nums=data)


test_0_4()

def resize_list(x):
    return (x + (x >> 3) + 6) & (~3)

"""

## 3.9
Add padding to make the allocated size multiple of 4
The growth pattern is:  0, 4, 8, 16, 24, 32, 40, 52, 64, 76
https://github.com/python/cpython/blob/3.9/Objects/listobject.c
new_allocated = ((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3;

id(4554271168) v:[] len:0 size:56
id(4554271168) v:1 len:1 size:88
id(4554271168) v:2 len:2 size:88
id(4554271168) v:3 len:3 size:88
id(4554271168) v:4 len:4 size:88
id(4554271168) v:5 len:5 size:120
id(4554271168) v:6 len:6 size:120
id(4554271168) v:7 len:7 size:120
id(4554271168) v:8 len:8 size:120
id(4554271168) v:9 len:9 size:184
id(4554271168) v:10 len:10 size:184
id(4554271168) v:11 len:11 size:184
id(4554271168) v:12 len:12 size:184
id(4554271168) v:13 len:13 size:184
id(4554271168) v:14 len:14 size:184
id(4554271168) v:15 len:15 size:184
id(4554271168) v:16 len:16 size:184
id(4554271168) v:17 len:17 size:248
id(4554271168) v:18 len:18 size:248
id(4554271168) v:19 len:19 size:248
id(4539054528) v:20 len:20 size:248
id(4539054528) v:21 len:21 size:248
id(4539054528) v:22 len:22 size:248
id(4539054528) v:23 len:23 size:248
id(4539054528) v:24 len:24 size:248
id(4506298816) v:25 len:25 size:312
id(4506298816) v:26 len:26 size:312
id(4506298816) v:27 len:27 size:312
id(4506298816) v:28 len:28 size:312
id(4506298816) v:29 len:29 size:312
id(4506298816) v:30 len:30 size:312
id(4506298816) v:31 len:31 size:312
id(4506298816) v:32 len:32 size:312
id(4506298816) v:33 len:33 size:376
id(4506298816) v:34 len:34 size:376


## 3.7/3.8
The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88
https://github.com/python/cpython/blob/3.7/Objects/listobject.c
https://github.com/python/cpython/blob/3.8/Objects/listobject.c

new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

(py3) ➜  stu git:(hyh_debug_01_main) ✗ python --version 
Python 3.7.4
(py3) ➜  stu git:(hyh_debug_01_main) ✗ python test_list_resize.py 
id(4538606048) v:[] len:0 size:72
id(4538606048) v:1 len:1 size:104
id(4538606048) v:2 len:2 size:104
id(4538606048) v:3 len:3 size:104
id(4538606048) v:4 len:4 size:104       4个空间使用完. 增加4
id(4538606048) v:5 len:5 size:136
id(4538606048) v:6 len:6 size:136
id(4538606048) v:7 len:7 size:136
id(4538606048) v:8 len:8 size:136       8个空间使用完. 增加4
id(4538606048) v:9 len:9 size:200
id(4538606048) v:10 len:10 size:200
id(4538606048) v:11 len:11 size:200
id(4538606048) v:12 len:12 size:200
id(4538606048) v:13 len:13 size:200
id(4538606048) v:14 len:14 size:200
id(4538606048) v:15 len:15 size:200
id(4538606048) v:16 len:16 size:200    16个空间使用完. 增加8
id(4538606048) v:17 len:17 size:272
id(4538606048) v:18 len:18 size:272
id(4538606048) v:19 len:19 size:272
id(4538606048) v:20 len:20 size:272
id(4538606048) v:21 len:21 size:272
id(4538606048) v:22 len:22 size:272
id(4538606048) v:23 len:23 size:272
id(4538606048) v:24 len:24 size:272
id(4538606048) v:25 len:25 size:272    25个空间使用完. 增加9
id(4538606048) v:26 len:26 size:352
id(4538606048) v:27 len:27 size:352
id(4538606048) v:28 len:28 size:352
id(4538606048) v:29 len:29 size:352
id(4538606048) v:30 len:30 size:352
id(4538606048) v:31 len:31 size:352
id(4538606048) v:32 len:32 size:352
id(4538606048) v:33 len:33 size:352
id(4538606048) v:34 len:34 size:352
# 关于size不一致问题,_typeobject 类型对象结构体在py3.9中删除了部分元素比3.8更小
"""
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!