사전 대결-어느 것이 더 처음이라고?
메모리 사용 및 CPU 소비에서 더 많은 것이 있습니까?
배경 : 엄청난 양의 데이터를로드해야합니다. 필드 컨테이너 인 객체를 만들었습니다. 4M 인스턴스를 만들어 사전에 넣는 데 약 10 분과 ~ 6GB의 메모리가 필요했습니다. 사전이 준비되면 액세스가 눈을 깜박입니다.
예 : 성능을 확인하기 위해 동일한 두 가지 간단한 프로그램을 작성했습니다. 하나는 사전을 사용하고 다른 하나는 사용합니다.
대상 (실행 시간 ~ 18 초) :
class Obj(object):
def __init__(self, i):
self.i = i
self.l = []
all = {}
for i in range(1000000):
all[i] = Obj(i)
사전 (실행 시간 ~ 12 초) :
all = {}
for i in range(1000000):
o = {}
o['i'] = i
o['l'] = []
all[i] = o
질문 : 내가 잘못하고 있거나 사전이 객체보다 빠 사용. 어떤 사람이 왜 그 이유를 설명 할 수 있습니까?
를 많이 보며 __slots__
?
로부터 문서 :
기본적으로 이전 클래스와 새 스타일 클래스의 인스턴스에는 속성 저장을 사전에 있습니다. 이 인스턴스 변수는 거의없는 객체를위한 공간을 제공합니다. 많은 수의 인스턴스를 만들 때 공간 소비가 급격하게 될 수 있습니다.
__slots__
새 스타일 클래스 정의에서 정의를 사용 하여 대체 할 수 있습니다 .__slots__
선언 각 변수에 대한 값을 유지하도록 할당의 인스턴스 인스턴스 변수 보유 충분한 공간의 시퀀스 다.__dict__
인스턴스마다 각 공간이 생성되지 않아 공간이 절약 됩니다.
아니라 메모리 아니라 시간도 절약?
내 컴퓨터의 세 가지 접근 방식 비교 :
test_slots.py :
class Obj(object):
__slots__ = ('i', 'l')
def __init__(self, i):
self.i = i
self.l = []
all = {}
for i in range(1000000):
all[i] = Obj(i)
test_obj.py :
class Obj(object):
def __init__(self, i):
self.i = i
self.l = []
all = {}
for i in range(1000000):
all[i] = Obj(i)
test_dict.py :
all = {}
for i in range(1000000):
o = {}
o['i'] = i
o['l'] = []
all[i] = o
test_namedtuple.py (2.6에서 지원) :
import collections
Obj = collections.namedtuple('Obj', 'i l')
all = {}
for i in range(1000000):
all[i] = Obj(i, [])
CPython 2.5를 사용하여 벤치 마크를 실행하십시오.
$ lshw | grep product | head -n 1
product: Intel(R) Pentium(R) M processor 1.60GHz
$ python --version
Python 2.5
$ time python test_obj.py && time python test_dict.py && time python test_slots.py
real 0m27.398s (using 'normal' object)
real 0m16.747s (using __dict__)
real 0m11.777s (using __slots__)
명명 된 튜플 테스트를 포함하여 CPython 2.6.2 사용 :
$ python --version
Python 2.6.2
$ time python test_obj.py && time python test_dict.py && time python test_slots.py && time python test_namedtuple.py
real 0m27.197s (using 'normal' object)
real 0m17.657s (using __dict__)
real 0m12.249s (using __slots__)
real 0m12.262s (using namedtuple)
그렇습니다 (실제로 놀랍지는 언어), 사용 __slots__
은 성능 최적화입니다. 명명 된 튜플을 사용하면 성능이 발생합니다 __slots__
.
클래스의 속성 액세스는 장면 뒤의 사전 액세스를 사용하여 오버 속성 헤드 액세스를 사용하면 추가로 발생하는 발생합니다. 또한 발생하는 경우 추가 헤드 할당 메모리 할당 및 코드 실행 (예 : __init__
메서드)으로 인해 추가 오버가 발생 합니다.
경우 코드에서 o
입니다 Obj
인스턴스 o.attr
에 해당하는 o.__dict__['attr']
추가 오버 헤드의 소량.
명명 된 튜플 사용을 고려 했습니까 ? ( python 2.4 / 2.5 링크 )
튜플의 성능과 클래스의 편의성을 제공하는 구조화 된 데이터를 다양한 새로운 표준 방법입니다.
사전과 선호 할 때 생성 유일한 단점은 튜플과 같이 후 속성을 설명 수있는 기능을 제공하지는 것입니다.
from datetime import datetime
ITER_COUNT = 1000 * 1000
def timeit(method):
def timed(*args, **kw):
s = datetime.now()
result = method(*args, **kw)
e = datetime.now()
print method.__name__, '(%r, %r)' % (args, kw), e - s
return result
return timed
class Obj(object):
def __init__(self, i):
self.i = i
self.l = []
class SlotObj(object):
__slots__ = ('i', 'l')
def __init__(self, i):
self.i = i
self.l = []
@timeit
def profile_dict_of_dict():
return dict((i, {'i': i, 'l': []}) for i in xrange(ITER_COUNT))
@timeit
def profile_list_of_dict():
return [{'i': i, 'l': []} for i in xrange(ITER_COUNT)]
@timeit
def profile_dict_of_obj():
return dict((i, Obj(i)) for i in xrange(ITER_COUNT))
@timeit
def profile_list_of_obj():
return [Obj(i) for i in xrange(ITER_COUNT)]
@timeit
def profile_dict_of_slotobj():
return dict((i, SlotObj(i)) for i in xrange(ITER_COUNT))
@timeit
def profile_list_of_slotobj():
return [SlotObj(i) for i in xrange(ITER_COUNT)]
if __name__ == '__main__':
profile_dict_of_dict()
profile_list_of_dict()
profile_dict_of_obj()
profile_list_of_obj()
profile_dict_of_slotobj()
profile_list_of_slotobj()
결과 :
hbrown@hbrown-lpt:~$ python ~/Dropbox/src/StackOverflow/1336791.py
profile_dict_of_dict ((), {}) 0:00:08.228094
profile_list_of_dict ((), {}) 0:00:06.040870
profile_dict_of_obj ((), {}) 0:00:11.481681
profile_list_of_obj ((), {}) 0:00:10.893125
profile_dict_of_slotobj ((), {}) 0:00:06.381897
profile_list_of_slotobj ((), {}) 0:00:05.860749
다음은 전문 3.6.1에 대한 @ hughdbrown 답변의 사본입니다. 발행을 5 배로 늘리고 각 실행이 끝날 때마다 기술 프로세스의 메모리 풋 프린트를 테스트하는 코드를 추가했습니다.
다운 보터가 수행하기 위해 수행하기, 크기를 계산하는 방법이 수행되지 않습니다.
from datetime import datetime
import os
import psutil
process = psutil.Process(os.getpid())
ITER_COUNT = 1000 * 1000 * 5
RESULT=None
def makeL(i):
# Use this line to negate the effect of the strings on the test
# return "Python is smart and will only create one string with this line"
# Use this if you want to see the difference with 5 million unique strings
return "This is a sample string %s" % i
def timeit(method):
def timed(*args, **kw):
global RESULT
s = datetime.now()
RESULT = method(*args, **kw)
e = datetime.now()
sizeMb = process.memory_info().rss / 1024 / 1024
sizeMbStr = "{0:,}".format(round(sizeMb, 2))
print('Time Taken = %s, \t%s, \tSize = %s' % (e - s, method.__name__, sizeMbStr))
return timed
class Obj(object):
def __init__(self, i):
self.i = i
self.l = makeL(i)
class SlotObj(object):
__slots__ = ('i', 'l')
def __init__(self, i):
self.i = i
self.l = makeL(i)
from collections import namedtuple
NT = namedtuple("NT", ["i", 'l'])
@timeit
def profile_dict_of_nt():
return [NT(i=i, l=makeL(i)) for i in range(ITER_COUNT)]
@timeit
def profile_list_of_nt():
return dict((i, NT(i=i, l=makeL(i))) for i in range(ITER_COUNT))
@timeit
def profile_dict_of_dict():
return dict((i, {'i': i, 'l': makeL(i)}) for i in range(ITER_COUNT))
@timeit
def profile_list_of_dict():
return [{'i': i, 'l': makeL(i)} for i in range(ITER_COUNT)]
@timeit
def profile_dict_of_obj():
return dict((i, Obj(i)) for i in range(ITER_COUNT))
@timeit
def profile_list_of_obj():
return [Obj(i) for i in range(ITER_COUNT)]
@timeit
def profile_dict_of_slot():
return dict((i, SlotObj(i)) for i in range(ITER_COUNT))
@timeit
def profile_list_of_slot():
return [SlotObj(i) for i in range(ITER_COUNT)]
profile_dict_of_nt()
profile_list_of_nt()
profile_dict_of_dict()
profile_list_of_dict()
profile_dict_of_obj()
profile_list_of_obj()
profile_dict_of_slot()
profile_list_of_slot()
그리고 이것은 내 결과입니다
Time Taken = 0:00:07.018720, provile_dict_of_nt, Size = 951.83
Time Taken = 0:00:07.716197, provile_list_of_nt, Size = 1,084.75
Time Taken = 0:00:03.237139, profile_dict_of_dict, Size = 1,926.29
Time Taken = 0:00:02.770469, profile_list_of_dict, Size = 1,778.58
Time Taken = 0:00:07.961045, profile_dict_of_obj, Size = 1,537.64
Time Taken = 0:00:05.899573, profile_list_of_obj, Size = 1,458.05
Time Taken = 0:00:06.567684, profile_dict_of_slot, Size = 1,035.65
Time Taken = 0:00:04.925101, profile_list_of_slot, Size = 887.49
내 결론은 :
- 더 높고 속도가 합리적입니다.
- dicts가 가장 빠르지 만 가장 많은 메모리를 사용하십시오.
의문의 여지가 없습니다.
다른 속성이없는 데이터가 있습니다 (메서드도, 아무것도 없음). 따라서 데이터 컨테이너 (이 경우 사전)가 있습니다.
나는 일반적으로 데이터 모델링의 관점에서 생각하는 것을 선호합니다 . 큰 성능 문제가 있다면 추상화에서 무언가를 포기할 수 있지만 아주 좋은 이유가 있어야합니다.
프로그래밍은 복잡성을 관리하고 올바른 추상화를 것은 이러한 결과를 얻는 가장 유용한 방법 중 하나입니다.
[정보 이유 객체가 느립니다, 당신의 측정이 정확하지라고 생각합니다.
for 루프 내에서 너무 적은 할당을 수행하고 있으므로 dict (내재 객체)와 "사용자 정의"객체를 인스턴스화하는 데 필요한 시간이 다릅니다. 언어 관점에서 볼 때 동일하지만 구현이 상당히 다릅니다.
그 후에는 최종 구성원이 사전 내에 유지되므로 할당 시간은 둘 다 거의 동일해야합니다.
데이터 구조에 참조주기가 포함되어 있지 않은 경우 메모리 사용량을 줄이는 또 다른 방법이 있습니다.
두 클래스를 비교해 보겠습니다.
class DataItem:
__slots__ = ('name', 'age', 'address')
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
과
$ pip install recordclass
>>> from recordclass import structclass
>>> DataItem2 = structclass('DataItem', 'name age address')
>>> inst = DataItem('Mike', 10, 'Cherry Street 15')
>>> inst2 = DataItem2('Mike', 10, 'Cherry Street 15')
>>> print(inst2)
>>> print(sys.getsizeof(inst), sys.getsizeof(inst2))
DataItem(name='Mike', age=10, address='Cherry Street 15')
64 40
그 이후로 가능해졌습니다. structclass
기반 클래스는 이러한 경우 필요하지 않은 순환 가비지 수집을 지원하지 않기 .
__slots__
기반 클래스에 비해 한 가지 장점 이 있습니다. 추가 속성을 추가 할 수 있습니다.
>>> DataItem3 = structclass('DataItem', 'name age address', usedict=True)
>>> inst3 = DataItem3('Mike', 10, 'Cherry Street 15')
>>> inst3.hobby = ['drawing', 'singing']
>>> print(inst3)
>>> print(sizeof(inst3), 'has dict:', bool(inst3.__dict__))
DataItem(name='Mike', age=10, address='Cherry Street 15', **{'hobby': ['drawing', 'singing']})
48 has dict: True
참고 URL : https://stackoverflow.com/questions/1336791/dictionary-vs-object-which-is-more-efficient-and-why
'IT' 카테고리의 다른 글
인라인 스타일의 CSS 의사 클래스 (0) | 2020.07.19 |
---|---|
NHibernate와 LINQ to SQL (0) | 2020.07.19 |
Xcode 4 체계의 이름을 바꾸는 방법이 있습니까? (0) | 2020.07.18 |
PostgreSQL-DB에 대한 자동 연결로 인해 데이터베이스를 수 없습니다. (0) | 2020.07.18 |
자바 모든에서 공용 서비스 대시 뒤의 모든 것을 가져옵니다. (0) | 2020.07.18 |