코딩을 어느 정도 하다보면, “코드 자체를 생산하는 것”은 꽤 쉬운 일이라는 생각이 들때가 있습니다. 그 즈음에 드는 생각은 “어떻게 해야 더 효율적이고 빠른 코드를 만들 수 있을까?“죠.
원론적으로 이를 해결하기 위해서는, 프로그래밍 기본에 대한 공부들을 하는 것이 좋습니다. 인간컴파일러가 되어서, 어떻게 돌아가는지를 이해한다면 좋겠지만, 보통 그런 경우는 없고, ’일단 이 코드가 느린지 빠른지’를 먼저 체크하는 것이 필요하죠.
이를 위해서는 기본적인 성능을 비교하는 것이 필요합니다. 그리고 그 성능이라는 것은 보통 ’시간’과 ’RAM 사용량’으로 결정되죠.
이 글에서는, 상대적으로 사용하기 쉬운 시간 비교는 무시하고, RAM 사용량을 체크해보려고 합니다. 같은 데이터를 담더라도, 어떤 자료 구조를 선택하느냐에 따라서, RAM 사용량이 다른 경우들이 존재하니까요.
Check RAM Usage.
sys.getsizeof
간단하게, 각 변수에 할당된 데이터의 크기가 어느 정도인지를 체크하기 위해서는 sys.getsizeof를 사용할 수 있습니다. 하지만, object 안에 object가 또 있는 경우, 특히 string으로 구성된 list에 대해서는 정확하게 비교해주지 못합니다. 즉, 그냥 list of pointer와 유사하게, shallow size를 출력해준다고 봐도 되겠네요.
#################################### sys.getsizeof()# Return the size of an object in bytes.# 이를 사용하면, 간단하게 크기를 비교할 수 있다.import sysN = 1000print("== sys.getsizeof")# 그러나, 아래와 같이 string이 container의 내부 요소로 포함된 경우에는 정확한 크기를 측정하지 못한다.# 이는 python에서 string은 object이며, 위치 정보(가령 pointer)만 저장하고 전체 크기까지 함께 저장하고 있지 않기 때문이다# 만약 정확한 크기를 파악하고 싶다면, 내부 요소의 타입까지 고려하여 계산해야 한다.assert sys.getsizeof(['aaaa', 1])==sys.getsizeof(['aaaa', 'asdfsdafdsfasf'])test_lst_int = [i for i in range(0, N)]test_lst_tuple = [(f"k{i}", i) for i in range(0, N)]test_dict = {f"k{i}":i for i in range(0, N)}# 내부 요소가 tuple로 되어 있는 경우인 test_lst_int 와 test_lst_tuple의 크기가 같다.print(f"sys.getsizeof(test_lst): {sys.getsizeof(test_lst_int)}")print(f"sys.getsizeof(test_lst): {sys.getsizeof(test_lst_tuple)}")print(f"sys.getsizeof(test_dict): {sys.getsizeof(test_dict)}")
이제 Ram의 사용량을 비교해봅시다. 기본적으로는 pstuil이라는 라이브러리를 사용하며, 현재 프로세스의 메모리 사용량을 체크하는 경우와, 제너럴한 메모리 사용량을 체크하는 경우로 구분되며, 사실상 큰 차이는 없습니다(성능을 비교할 때 다른 짓을 거의 하지 않는다는 전제 하에서)
코드 적으로 어려운 부분은 없으니, 자세한 설명은 제외하겠습니다.
#################################### memory check.# 1) 그냥 현재 memory usage 정보를 그대로 가져오는 경우# 2) 현재 process id를 통해 해당 프로세스의 memory usage를 정확하게 비교하는 경우import psutilimport osprint("=="*20)print("== memory usage check")for exec_num in range(0, 2): # BEFORE code print(f"== {exec_num:2d} exec") # general RAM usage memory_usage_dict = dict(psutil.virtual_memory()._asdict()) memory_usage_percent = memory_usage_dict['percent'] print(f"BEFORE CODE: memory_usage_percent: {memory_usage_percent}%") # current process RAM usage pid = os.getpid() current_process = psutil.Process(pid) current_process_memory_usage_as_KB = current_process.memory_info()[0] / 2.**20 print(f"BEFORE CODE: Current memory KB : {current_process_memory_usage_as_KB: 9.3f} KB") X = [i for i in range(0, 9000000)] # AFTER code memory_usage_dict = dict(psutil.virtual_memory()._asdict()) memory_usage_percent = memory_usage_dict['percent'] print(f"AFTER CODE: memory_usage_percent: {memory_usage_percent}%") # current process RAM usage pid = os.getpid() current_process = psutil.Process(pid) current_process_memory_usage_as_KB = current_process.memory_info()[0] / 2.**20 print(f"AFTER CODE: Current memory KB : {current_process_memory_usage_as_KB: 9.3f} KB") del X print("--"*30)