IT TIP

list ()는 list comprehension보다 약간 더 많은 메모리를 사용합니다.

itqueen 2020. 10. 12. 21:17
반응형

list ()는 list comprehension보다 약간 더 많은 메모리를 사용합니다.


그래서 나는 list물건 을 가지고 놀고 있었고 그것 list으로 만들어 지면 list()목록 이해보다 더 많은 메모리를 사용 한다는 이상한 점을 발견 했습니까? Python 3.5.2를 사용하고 있습니다.

In [1]: import sys
In [2]: a = list(range(100))
In [3]: sys.getsizeof(a)
Out[3]: 1008
In [4]: b = [i for i in range(100)]
In [5]: sys.getsizeof(b)
Out[5]: 912
In [6]: type(a) == type(b)
Out[6]: True
In [7]: a == b
Out[7]: True
In [8]: sys.getsizeof(list(b))
Out[8]: 1008

로부터 문서 :

목록은 여러 가지 방법으로 구성 될 수 있습니다.

  • 한 쌍의 대괄호를 사용하여 빈 목록을 나타냅니다. []
  • 대괄호를 사용하여 항목을 쉼표로 구분 : [a],[a, b, c]
  • 목록 이해력 사용 : [x for x in iterable]
  • 유형 생성자 사용 : list()또는list(iterable)

그러나 그것을 사용 list()하면 더 많은 메모리 를 사용하는 것 같습니다 .

그리고 list많을수록 격차가 커집니다.

기억의 차이

왜 이런 일이 발생합니까?

업데이트 # 1

Python 3.6.0b2로 테스트합니다.

Python 3.6.0b2 (default, Oct 11 2016, 11:52:53) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getsizeof(list(range(100)))
1008
>>> sys.getsizeof([i for i in range(100)])
912

업데이트 # 2

Python 2.7.12로 테스트 :

Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getsizeof(list(xrange(100)))
1016
>>> sys.getsizeof([i for i in xrange(100)])
920

나는 당신이 초과 할당 패턴을보고 있다고 생각합니다. 이것은 소스샘플입니다 .

/* 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().
 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
 */

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

길이 0-88의 목록 내포 크기를 인쇄하면 패턴 일치를 볼 수 있습니다.

# create comprehensions for sizes 0-88
comprehensions = [sys.getsizeof([1 for _ in range(l)]) for l in range(90)]

# only take those that resulted in growth compared to previous length
steps = zip(comprehensions, comprehensions[1:])
growths = [x for x in list(enumerate(steps)) if x[1][0] != x[1][1]]

# print the results:
for growth in growths:
    print(growth)

결과 (형식은 (list length, (old total size, new total size))) :

(0, (64, 96)) 
(4, (96, 128))
(8, (128, 192))
(16, (192, 264))
(25, (264, 344))
(35, (344, 432))
(46, (432, 528))
(58, (528, 640))
(72, (640, 768))
(88, (768, 912))

초과 할당은 성능상의 이유로 수행되므로 증가 할 때마다 더 많은 메모리를 할당하지 않고도 목록이 증가 할 수 있습니다 (더 나은 분할 성능).

목록 이해력을 사용하는 것과 다른 가능한 이유는 목록 이해력이 생성 된 목록의 크기를 결정적으로 계산할 수는 없지만 계산할 list()수 있기 때문입니다. 즉, 최종적으로 채울 때까지 초과 할당을 사용하여 채울 때 이해력이 목록을 지속적으로 늘릴 것입니다.

일단 완료되면 사용되지 않은 할당 된 노드로 초과 할당 버퍼가 증가하지 않을 수 있습니다 (사실 대부분의 경우 초과 할당 목적을 무효화 할 것입니다).

list()그러나 최종 목록 크기를 미리 알고 있으므로 목록 크기에 관계없이 버퍼를 추가 할 수 있습니다.


소스의 또 다른 뒷받침 증거는을 호출하는 목록 내포를LIST_APPEND 볼 수 있다는 것 입니다 list.resize. 이는의 사용을 나타내며 , 이는 얼마나 많이 채워질 지 모른 채 사전 할당 버퍼를 소비 함을 나타냅니다. 이것은 당신이보고있는 행동과 일치합니다.


결론적으로 list()목록 크기의 함수로 더 많은 노드를 미리 할당합니다.

>>> sys.getsizeof(list([1,2,3]))
60
>>> sys.getsizeof(list([1,2,3,4]))
64

List comprehension은 목록 크기를 알지 못하므로 증가함에 따라 추가 작업을 사용하여 사전 할당 버퍼를 고갈시킵니다.

# one item before filling pre-allocation buffer completely
>>> sys.getsizeof([i for i in [1,2,3]]) 
52
# fills pre-allocation buffer completely
# note that size did not change, we still have buffered unused nodes
>>> sys.getsizeof([i for i in [1,2,3,4]]) 
52
# grows pre-allocation buffer
>>> sys.getsizeof([i for i in [1,2,3,4,5]])
68

Thanks everyone for helping me to understand that awesome Python.

I don't want to make question that massive(that why i'm posting answer), just want to show and share my thoughts.

As @ReutSharabani noted correctly: "list() deterministically determines list size". You can see it from that graph.

크기 그래프

When you append or using list comprehension you always have some sort of boundaries that extends when you reach some point. And with list() you have almost the same boundaries, but they are floating.

UPDATE

So thanks to @ReutSharabani, @tavo, @SvenFestersen

요약하면 : list()목록 크기에 따라 메모리를 미리 할당하고 목록 이해는이를 수행 할 수 없습니다 (예 : 필요할 때 더 많은 메모리를 요청합니다 .append()). 이것이 list()더 많은 메모리를 저장하는 이유 입니다.

list()메모리를 미리 할당 하는 그래프가 하나 더 있습니다. 따라서 녹색 선은 list(range(830))요소별로 추가되고 잠시 동안 메모리가 변경되지 않음을 보여줍니다 .

list ()는 메모리를 미리 할당합니다.

업데이트 2

@Barmar으로, 아래 설명에서 언급 list()내가 실행되도록 빠른 지능형리스트보다 더 저를해야한다 timeit()으로 number=1000의 길이 list에서 4**0까지 4**10그 결과는

시간 측정

참고 URL : https://stackoverflow.com/questions/40018398/list-uses-slightly-more-memory-than-list-comprehension

반응형