실험을 하다 보면 퍼포먼스 비교, 코드 개선, 단순 호기심 등 여러 가지 이유로 내가 작성한 코드의 실행 시간을 측정해야 할 때가 있다. 측정이 필요한 함수가 1개~2개뿐이라면 단순히 해당 함수의 시작, 끝부분에 시간 측정 코드를 작성해도 되지만, 아무래도 보기에 좋지 않다. 측정이 필요한 함수의 개수가 늘어나면 동일한 동작을 하는 코드가 여기저기 중복되어 들어가고 유지보수가 힘들어지는 건 덤이다.
이럴 때 decorator를 이용하면 편하다. Decorator는 @wrapper
syntax를 이용한 함수 변환을 통해 다른 함수를 반환하는 함수이다. 먼저 함수의 실행 시간을 측정해주는 decorator를 작성하자.
import time def elapsed(f): def wrap(*args): start_r = time.perf_counter() start_p = time.process_time() ret = f(*args) end_r = time.perf_counter() end_p = time.process_time() elapsed_r = end_r - start_r elapsed_p = end_p - start_p print(f'{f.__name__}... elapsed: {elapsed_r:.3f}sec (real) / {elapsed_p:.3f}sec (cpu)') return ret return wrap
elapsed
라는 이름의 decorator를 작성했다. elapsed는 line 7
에서 함수를 호출하기 전/후로 time.perf_counter()와 time.process_time() 을 호출하여 함수의 수행 시간을 측정한다. 사용 예시는 아래와 같다.
@elapsed def ftn1(): sum = 0 for i in range(int(1e7)): sum += i return @elapsed def ftn2(): time.sleep(1) return
>>> ftn1() ftn1... elapsed: 0.718sec (real) / 0.719sec (cpu) >>> ftn2() ftn2... elapsed: 1.001sec (real) / 0.000sec (cpu)
각 함수에 실행 시간 측정 코드를 직접 작성하는 것보다 훨씬 간단하고 유지보수도 쉬운 코드가 작성되었다.