programing

동일한 두 목록의 메모리 설치 공간이 다른 이유는 무엇입니까?

telebox 2023. 5. 27. 10:37
반응형

동일한 두 목록의 메모리 설치 공간이 다른 이유는 무엇입니까?

나는 두 개의 목록을 만들었습니다.l1그리고.l2하지만 각각 다른 생성 방법을 사용합니다.

import sys

l1 = [None] * 10
l2 = [None for _ in range(10)]

print('Size of l1 =', sys.getsizeof(l1))
print('Size of l2 =', sys.getsizeof(l2))

하지만 그 결과는 저를 놀라게 했습니다.

Size of l1 = 144
Size of l2 = 192

목록 이해로 작성된 목록은 메모리에서 더 큰 크기이지만 Python에서는 두 목록이 동일하지 않습니다.

왜 그런 것일까요?이것은 Cython 내부의 일입니까, 아니면 다른 설명입니까?

당신이 글을 쓸 때[None] * 10Python은 정확히 10개의 객체 목록이 필요하다는 것을 알고 있기 때문에 정확히 할당합니다.

당신이 목록 이해를 사용할 때, 파이썬은 그것이 얼마나 필요한지 모릅니다.따라서 요소가 추가됨에 따라 목록이 점진적으로 증가합니다.각 재할당에 대해 즉시 필요한 공간보다 더 많은 공간을 할당하므로 각 요소에 대해 재할당할 필요가 없습니다.결과 목록은 필요한 것보다 다소 클 수 있습니다.

비슷한 크기로 작성된 목록을 비교할 때 다음 동작을 확인할 수 있습니다.

>>> sys.getsizeof([None]*15)
184
>>> sys.getsizeof([None]*16)
192
>>> sys.getsizeof([None for _ in range(15)])
192
>>> sys.getsizeof([None for _ in range(16)])
192
>>> sys.getsizeof([None for _ in range(17)])
264

첫 번째 방법은 필요한 것만 할당하고 두 번째 방법은 주기적으로 증가하는 것을 볼 수 있습니다.이 예제에서는 16개 요소에 대해 충분한 양을 할당하고 17번째 요소에 도달하면 다시 할당해야 했습니다.

질문에서 언급한 바와 같이 목록 이해는 다음과 같이 사용합니다.list.append후드 아래에, 그래서 그것은 리스트-오프닝 방법을 부를 것이고, 그것은 과도하게 위치합니다.

이것을 자신에게 보여주기 위해, 당신은 실제로 사용할 수 있습니다.dis분해기:

>>> code = compile('[x for x in iterable]', '', 'eval')
>>> import dis
>>> dis.dis(code)
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x10560b810, file "", line 1>)
              2 LOAD_CONST               1 ('<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (iterable)
              8 GET_ITER
             10 CALL_FUNCTION            1
             12 RETURN_VALUE

Disassembly of <code object <listcomp> at 0x10560b810, file "", line 1>:
  1           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 8 (to 14)
              6 STORE_FAST               1 (x)
              8 LOAD_FAST                1 (x)
             10 LIST_APPEND              2
             12 JUMP_ABSOLUTE            4
        >>   14 RETURN_VALUE
>>>

주의:LIST_APPEND의 분해에 있는 opcode.<listcomp>코드 개체입니다.문서에서:

LIST_APPEND(i)

통화list.append(TOS[-i], TOS)목록 이해를 구현하는 데 사용됩니다.

이제 목록 반복 작업의 경우 다음을 고려하면 무슨 일이 일어나고 있는지 힌트를 얻을 수 있습니다.

>>> import sys
>>> sys.getsizeof([])
64
>>> 8*10
80
>>> 64 + 80
144
>>> sys.getsizeof([None]*10)
144

그래서 정확하게 크기를 할당할 수 있을 것으로 보입니다.소스 코드를 보면 다음과 같은 현상이 나타납니다.

static PyObject *
list_repeat(PyListObject *a, Py_ssize_t n)
{
    Py_ssize_t i, j;
    Py_ssize_t size;
    PyListObject *np;
    PyObject **p, **items;
    PyObject *elem;
    if (n < 0)
        n = 0;
    if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n)
        return PyErr_NoMemory();
    size = Py_SIZE(a) * n;
    if (size == 0)
        return PyList_New(0);
    np = (PyListObject *) PyList_New(size);

즉, 다음과 같습니다.size = Py_SIZE(a) * n;나머지 함수는 단순히 배열을 채웁니다.

메모리 블록은 아니지만 미리 지정된 크기는 아닙니다.이 외에도 배열 요소 사이에는 추가적인 간격이 있습니다.다음을 실행하여 직접 확인할 수 있습니다.

for ele in l2:
    print(sys.getsizeof(ele))

>>>>16
16
16
16
16
16
16
16
16
16

이것은 l2의 총 크기가 아니라 오히려 더 작습니다.

print(sys.getsizeof([None]))
72

그리고 이것은 크기의 10분의 1보다 훨씬 큽니다.l1.

사용자의 숫자는 운영 체제의 세부 정보와 운영 체제의 현재 메모리 사용량의 세부 정보에 따라 달라집니다.[None]의 크기는 변수가 저장되도록 설정된 사용 가능한 인접 메모리보다 클 수 없으며, 나중에 변수가 더 커지도록 동적으로 할당되면 변수를 이동해야 할 수 있습니다.

언급URL : https://stackoverflow.com/questions/51526242/why-do-two-identical-lists-have-a-different-memory-footprint

반응형