문득 코딩을 하다가, 꽤나 큰 시간차가 발생되게 하는 원인이 list에 관련된 함수들 때문이라는 것을 알고, 이것에 대한 분석을 해야겠다는 생각이 들었습니다.
물론 다행히도 심각한 성능의 결함까지는 아니지만, 그래도 1.5배에서 10배 정도까지 성능 차이가 나기 때문에 꽤 중요한 부분이라고 생각됩니다.
제가 성능을 분석해 본 방법은, 0부터 N-1까지의 정수 list를 만들어 return하는 함수를 세 가지 방법으로 만들어 본 것입니다.
<방법 1> : 0부터 N-1까지 for문을 돌면서 계속 append해 나가는 방법입니다.
def makeListUsingAppend(N):
result = []
for i in range(N):
result.append(i)
return result
>>> from time import time
>>> t0 = time()
>>> makeListUsingAppend(1000000)
>>> print "걸린시간 : %.2e" % (time()-t0)
걸린시간 : 1.57e-01
append라는 함수 자체가 기존에 할당된 공간을 확장해야 하기 때문에, 만약에 연속된 메모리의 자리가 없을 경우 새롭게 큰 공간을 만들어 모든 값들을 이사시켜야 하는 단점이 있습니다. 여기서 바로 꽤나 큰 overhead가 발생하게 되서 상대적으로 느립니다.
<방법 2> : 이번 방법은 조금 더 smart합니다. 이미 우리는 얼마나 큰 공간을 만들어야 하는지 알기 때문에, 미리 N개의 공간을 만들어 놓고, 값만 바꿔줍니다. 실제로 메모리 할당에 드는 overhead가 가장 무지막지하기 때문에 이 방법은 1.5배 정도까지 시간을 줄여주네요.
def makeListUsingBulkAlloc(N):
result = [0]*N #한번에 N개의 공간을 0으로 채우는 방법입니다.
for i in range(N):
result[i] = i
return result
>>> t0 = time()
>>> makeListUsingBulkAlloc(1000000)
>>> print "걸린시간 : %.2e" % (time()-t0)
걸린시간 : 1.01e-01
<방법 3> : 이번 방법은 최종 보스몹.. 바로 Built-in(내장된) 함수를 사용하는 방법입니다. 바로 range()
란 함수인데요, Built-in함수들은 C언어로 최적화 되어있기 때문에, 굉장히 빠른 속도를 자랑하죠. 물론 기능은 제한적이지만, 꽤나 쏠쏠하게 사용되곤 합니다.
def makeListUsingBuiltin(N):
return range(N)
>>> t0 = time()
>>> makeListUsingBuiltin(1000000)
>>> print "걸린시간 : %.2e" % (time()-t0)
걸린시간 : 1.68e-02
이런 구조들까지 신경쓰면 코드짜는게 너무 어렵게 느껴지지만, 막상 엄청난 코드를 돌리는데 굉장히 느려지면 최적화에 대한 욕구를 느끼실 겁니다 :)