timeit — 작은 코드 조각의 실행 시간 측정

timeit — 작은 코드 조각의 실행 시간 측정

이 모듈은 작은 Python 코드 조각의 실행 시간을 측정하는 간단한 방법을 제공해요. 명령줄 인터페이스(Command-Line Interface)와 호출 가능한 인터페이스(callable interface)를 모두 갖추고 있으며, 실행 시간을 측정할 때 빠지기 쉬운 여러 함정을 피할 수 있게 해줘요. O'Reilly에서 출판한 Python Cookbook 2판의 "Algorithms" 장에 실린 Tim Peters의 소개도 참고하세요.

출처: Python documentation

본문

기본 예제 (Basic Examples)

다음 예제는 명령줄 인터페이스를 사용해 세 가지 다른 표현식을 비교하는 방법을 보여줘요:

$ python -m timeit "'-'.join(str(n) for n in range(100))"
10000 loops, best of 5: 30.2 usec per loop
$ python -m timeit "'-'.join([str(n) for n in range(100)])"
10000 loops, best of 5: 27.5 usec per loop
$ python -m timeit "'-'.join(map(str, range(100)))"
10000 loops, best of 5: 23.2 usec per loop

이것은 Python 인터페이스로는 다음과 같이 할 수 있어요:

>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.3018611848820001

호출 가능(callable) 객체도 Python 인터페이스로 전달할 수 있어요:

>>> timeit.timeit(lambda: "-".join(map(str, range(100))), number=10000)
0.19665591977536678

다만 timeit()은 명령줄 인터페이스를 사용할 때만 반복 횟수를 자동으로 결정한다는 점에 주의하세요.

Python 인터페이스

이 모듈은 세 가지 편의 함수와 하나의 공개 클래스를 정의해요:

timeit.timeit(stmt='pass', setup='pass', timer=, number=1000000, globals=None)

주어진 문장, setup 코드, 타이머 함수로 Timer 인스턴스를 만들고, 그 timeit() 메서드를 number번 실행해요. 선택적 globals 인자는 코드를 실행할 네임스페이스를 지정해요.

timeit.repeat(stmt='pass', setup='pass', timer=, repeat=5, number=1000000, globals=None)

주어진 문장, setup 코드, 타이머 함수로 Timer 인스턴스를 만들고, 주어진 repeat 횟수와 number 실행으로 그 repeat() 메서드를 실행해요.

timeit.default_timer()

항상 time.perf_counter()인 기본 타이머로, float 초를 반환해요. 대안으로 time.perf_counter_ns는 정수 나노초를 반환해요.

classtimeit.Timer(stmt='pass', setup='pass', timer=, globals=None)

작은 코드 조각의 실행 속도를 측정하는 클래스예요.

생성자는 측정할 문장, setup에 사용할 추가 문장, 타이머 함수를 받아요. 두 문장은 모두 기본적으로 'pass'이고, 타이머 함수는 플랫폼에 따라 달라져요. stmtsetup은 여러 줄 문자열 리터럴을 포함하지 않는 한 ;나 개행으로 구분된 여러 문장을 포함할 수도 있어요. 문장은 기본적으로 timeit의 네임스페이스 안에서 실행되며, 이 동작은 globals에 네임스페이스를 전달해 제어할 수 있어요.

첫 번째 문장의 실행 시간을 측정하려면 timeit() 메서드를 사용해요. repeat()autorange() 메서드는 timeit()을 여러 번 호출하는 편의 메서드예요.

setup의 실행 시간은 전체 타이밍 실행에서 제외돼요. stmtsetup 매개변수는 인자 없이 호출 가능한 객체도 받을 수 있어요. 이 경우 타이머 함수에 이들 호출을 포함시켜 timeit()이 실행해요. 이때 추가 함수 호출 때문에 타이밍 오버헤드가 약간 더 커진다는 점에 주의하세요.

timeit(number=1000000)

주요 문장을 number번 실행하는 시간을 측정해요. setup 문장을 한 번 실행한 다음, 주요 문장을 number번 실행하는 데 걸리는 시간을 반환해요. 기본 타이머는 초를 float로 반환해요.

기본적으로 timeit()은 타이밍 동안 가비지 컬렉션(GC)을 일시적으로 끄는데, 이렇게 하면 독립적인 타이밍을 더 잘 비교할 수 있어요. 다만 측정 대상 함수의 성능에서 GC가 중요한 요소라면, setup 문자열의 첫 문장으로 gc.enable()을 넣어 GC를 다시 켤 수 있어요.

autorange(callback=None)

timeit()을 몇 번 호출할지 자동으로 결정해요. 총 시간이 0.2초 이상이 될 때까지 timeit()을 반복 호출해 최종 (loop 수, 그 loop 수에 걸린 시간)을 반환해요. callback이 주어지면 각 시도 후 callback(number, time_taken)으로 호출돼요.

repeat(repeat=5, number=1000000)

timeit()을 몇 번 호출해요. 결과 목록을 반환하며, 첫 번째 인자는 timeit()을 호출할 횟수, 두 번째 인자는 timeit()에 전달할 number 인자예요.

결과 벡터에서 평균과 표준편차를 계산해 보고하는 것이 유혹적일 수 있지만, 그리 유용하지 않아요. 일반적으로 가장 낮은 값이 주어진 코드 조각을 머신이 실행할 수 있는 하한을 주며, 결과 벡터의 높은 값은 보통 Python 속도의 변동 때문이 아니라 다른 프로세스가 타이밍 정확도를 방해해서 생겨요. 따라서 결과의 min()만 관심을 가지는 편이 좋아요.

print_exc(file=None)

측정된 코드의 traceback을 출력하는 헬퍼예요. 표준 traceback에 비해 컴파일된 템플릿의 소스 줄이 표시된다는 장점이 있어요. file 인자는 traceback을 보낼 위치를 지정하며 기본값은 sys.stderr이에요.

명령줄 인터페이스 (Command-Line Interface)

명령줄에서 프로그램으로 호출하면 다음 형태를 사용해요:

python -m timeit [-n N] [-r N] [-u U] [-s S] [-p] [-v] [-h] [statement ...]

지원되는 옵션은 다음과 같아요:

  • -n N, --number=N — 'statement'를 실행할 횟수
  • -r N, --repeat=N — 타이머를 반복할 횟수 (기본값 5)
  • -s S, --setup=S — 처음에 한 번 실행할 문장 (기본값 pass)
  • -p, --processtime.process_time()를 사용해 프로세스 시간 측정 (기본은 time.perf_counter())
  • -u, --unit=U — 타이머 출력의 시간 단위 지정; nsec, usec, msec, sec 중 선택
  • -v, --verbose — 원시 타이밍 결과 출력; 반복하면 정밀도 더 많은 자리수
  • -h, --help — 짧은 사용법 메시지 출력 후 종료

-n이 주어지지 않으면 총 시간이 0.2초 이상이 될 때까지 1, 2, 5, 10, 20, 50, … 시퀀스에서 늘어나는 숫자를 시도해 적절한 루프 수를 계산해요.

default_timer() 측정값은 같은 머신에서 실행되는 다른 프로그램의 영향을 받을 수 있으므로, 정확한 타이밍이 필요할 때는 타이밍을 몇 번 반복하고 가장 좋은 시간을 사용하는 것이 좋아요. -r 옵션이 여기에 적합하며 기본 5회 반복이 대부분 충분해요.

pass 문장 실행에는 기본 오버헤드가 약간 있다는 점에 주의하세요.

더 알아보기 (Learn more)