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

소스 코드: Lib/timeit.py


이 모듈은 파이썬 코드의 작은 조각의 시간을 측정하는 간단한 방법을 제공합니다. 명령 줄 인터페이스뿐만 아니라 콜러블도 있습니다. 실행 시간을 측정에 따르는 흔한 함정들을 피할 수 있습니다. O’Reilly가 출판한 Python Cookbook에 있는 Tim Peters의 “Algorithms” 장의 개요도 참조하십시오.

기본 예제

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

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

이것은 파이썬 인터페이스로는 다음과 같이 할 수 있습니다:

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

콜러블을 파이썬 인터페이스로 전달할 수도 있습니다:

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

그러나 timeit()은 명령 줄 인터페이스가 사용될 때만 반복 횟수를 자동으로 결정합니다. 예제 절에서 고급 예제를 찾을 수 있습니다.

파이썬 인터페이스

이 모듈은 세 개의 편리 함수와 하나의 공용 클래스를 정의합니다:

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

지정된 문장, setup 코드 및 timer 함수로 Timer 인스턴스를 만들고, number 실행으로 timeit() 메서드를 실행합니다. 선택적 globals 인자는 코드를 실행할 이름 공간을 지정합니다.

버전 3.5에서 변경: 선택적 globals 매개 변수가 추가되었습니다.

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

주어진 문장, setup 코드 및 timer 함수로 Timer 인스턴스를 생성하고, 주어진 repeat 카운트와 number 실행으로 repeat() 메서드를 실행합니다. 선택적 globals 인자는 코드를 실행할 이름 공간을 지정합니다.

버전 3.5에서 변경: 선택적 globals 매개 변수가 추가되었습니다.

버전 3.7에서 변경: repeat의 기본값이 3에서 5로 변경되었습니다.

timeit.default_timer()

기본 타이머, 항상 time.perf_counter()입니다.

버전 3.3에서 변경: 이제 time.perf_counter()가 기본 타이머입니다.

class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)

작은 코드 조각의 실행 속도를 측정하기 위한 클래스.

생성자는 시간 측정될 문장, 설정에 사용되는 추가 문장 및 타이머 함수를 받아들입니다. 두 문장의 기본값은 'pass'입니다; 타이머 함수는 플랫폼에 따라 다릅니다 (모듈 독스트링을 참조하십시오). stmtsetup은 여러 줄에 걸친 문자열 리터럴을 포함하지 않는 한 ;나 줄 바꿈으로 구분된 여러 개의 문장을 포함 할 수도 있습니다. 문장은 기본적으로 timeit의 이름 공간 내에서 실행됩니다; 이 동작은 globals에 이름 공간을 전달하여 제어 할 수 있습니다.

첫 번째 문장의 실행 시간을 측정하려면, timeit() 메서드를 사용하십시오. repeat()autorange() 메서드는 timeit()을 여러 번 호출하는 편리 메서드입니다.

setup의 실행 시간은 전체 측정 실행 시간에서 제외됩니다.

stmtsetup 매개 변수는 인자 없이 호출 할 수 있는 객체를 받아들일 수도 있습니다. 이렇게 하면 timeit()에 의해 실행될 타이머 함수에 그들에 대한 호출을 포함시킵니다. 이때는 여분의 함수 호출로 인해 타이밍 오버헤드가 약간 더 커집니다.

버전 3.5에서 변경: 선택적 globals 매개 변수가 추가되었습니다.

timeit(number=1000000)

주 문장의 number 실행의 시간을 측정합니다. setup 문장을 한 번 실행한 다음, 주 문장을 여러 번 실행하는 데 걸리는 시간을 초 단위로 float로 반환합니다. 인자는 루프를 통과하는 횟수이며, 기본값은 백만입니다. 주 문장, setup 문장 및 사용할 타이머 함수는 생성자에 전달됩니다.

참고

기본적으로, timeit()은 시간 측정 중에 가비지 수거를 일시적으로 끕니다. 이 방법의 장점은 독립적인 시간 측정이 더 잘 비교될 수 있다는 것입니다. 단점은 GC가 측정되는 함수의 성능에서 중요한 요소가 될 수 있다는 것입니다. 그렇다면, GC를 setup 문자열의 첫 번째 문장에서 다시 활성화할 수 있습니다. 예를 들면:

timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()
autorange(callback=None)

timeit()를 호출하는 횟수를 자동으로 결정합니다.

이 함수는 총 시간이 0.2초 이상이 될 때까지 timeit()을 반복적으로 호출하고, 최종 (루프 수, 해당 루프 수에 소요된 시간)을 반환하는 편리 함수입니다. 실행 시간이 적어도 0.2초가 될 때까지 1, 2, 5, 10, 20, 50 … 로 루프 수를 증가시키면서 timeit()을 호출합니다.

callback이 주어지고 None이 아니면, 각 시도 다음에 두 개의 인자로 호출합니다: callback(number, time_taken).

버전 3.6에 추가.

repeat(repeat=5, number=1000000)

timeit()을 몇 번 호출합니다.

이것은 반복적으로 timeit()을 호출하여 결과 리스트를 반환하는 편리 함수입니다. 첫 번째 인자는 timeit()을 호출할 횟수를 지정합니다. 두 번째 인자는 timeit()에 대한 number 인자를 지정합니다.

참고

결과 벡터로부터 평균과 표준 편차를 계산하고 이를 보고하고 싶을 수 있습니다. 하지만, 이것은 별로 유용하지 않습니다. 일반적으로, 가장 낮은 값이 여러분의 기계가 주어진 코드 조각을 얼마나 빨리 실행할 수 있는지에 대한 하한값을 제공합니다; 결과 벡터의 더 높은 값은 일반적으로 파이썬의 속도 변동성 때문이 아니라, 시간 측정의 정확성을 방해하는 다른 프로세스에 의해 발생합니다. 따라서 결과의 min()이 여러분이 관심을 기울여야 할 유일한 숫자일 것입니다. 그 후에, 전체 벡터를 살펴보고 통계보다는 상식을 적용해야 합니다.

버전 3.7에서 변경: repeat의 기본값이 3에서 5로 변경되었습니다.

print_exc(file=None)

측정되는 코드로부터의 트레이스백을 인쇄하는 도우미.

일반적인 사용:

t = Timer(...)       # outside the try/except
try:
    t.timeit(...)    # or t.repeat(...)
except Exception:
    t.print_exc()

표준 트레이스백에 비해 장점은 컴파일된 템플릿의 소스 행이 표시된다는 것입니다. 선택적 file 인자는 트레이스백을 보내야 할 곳을 지시합니다; 기본값은 sys.stderr입니다.

명령 줄 인터페이스

명령 줄에서 프로그램으로 호출할 때, 다음 형식이 사용됩니다:

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

이때 다음 옵션을 지원합니다:

-n N, --number=N

‘statement’를 실행하는 횟수

-r N, --repeat=N

타이머 반복 횟수 (기본값 5)

-s S, --setup=S

초기에 한 번 실행될 문장 (기본값 pass)

-p, --process

벽시계 시간이 아니라 프로세스 시간을 측정합니다, 기본값인 time.perf_counter() 대신 time.process_time()을 사용합니다

버전 3.3에 추가.

-u, --unit=U

타이머 출력의 시간 단위를 지정합니다; nsec, usec, msec 또는 sec 중에서 선택할 수 있습니다.

버전 3.5에 추가.

-v, --verbose

날 시간 측정 결과를 인쇄합니다; 더 많은 자릿수 정밀도를 위해서는 반복하십시오

-h, --help

짧은 사용법 메시지를 출력하고 종료합니다

여러 줄의 문장은 각 줄을 별도의 statement 인자로 지정하여 제공할 수 있습니다; 들여쓰기 된 줄은 인자를 따옴표로 묶고 선행 공백을 사용하면 됩니다. 여러 개의 -s 옵션도 비슷하게 취급됩니다.

-n이 주어지지 않으면, 총 시간이 최소 0.2초가 될 때까지 시퀀스 1, 2, 5, 10, 20, 50, … 에서 증가하는 숫자를 시도하여 적절한 루프 수가 계산됩니다.

default_timer() 측정은 같은 기계에서 실행되는 다른 프로그램의 영향을 받을 수 있으므로, 정확한 시간 측정이 필요할 때 가장 좋은 방법은 시간 측정을 몇 번 반복하고 가장 좋은 시간을 사용하는 것입니다. 이 작업에는 -r 옵션이 좋습니다; 대부분의 경우 기본값인 5번 반복으로 충분할 것입니다. time.process_time()을 사용하여 CPU 시간을 측정 할 수 있습니다.

참고

pass 문을 실행하는 것과 관련된 어떤 기준 오버헤드가 있습니다. 여기에 있는 코드는 그것을 숨기려고 시도하지는 않지만, 여러분은 신경 써야 합니다. 기준 오버헤드는 인자 없이 프로그램을 호출하여 측정 할 수 있으며, 파이썬 버전마다 다를 수 있습니다.

예제

처음에 한 번만 실행되는 setup 문을 제공 할 수 있습니다:

$ python -m timeit -s 'text = "sample string"; char = "g"'  'char in text'
5000000 loops, best of 5: 0.0877 usec per loop
$ python -m timeit -s 'text = "sample string"; char = "g"'  'text.find(char)'
1000000 loops, best of 5: 0.342 usec per loop
>>> import timeit
>>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"')
0.41440500499993504
>>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"')
1.7246671520006203

Timer 클래스와 그 메서드를 사용하여 같은 작업을 수행 할 수 있습니다:

>>> import timeit
>>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"')
>>> t.timeit()
0.3955516149999312
>>> t.repeat()
[0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]

다음 예제는 여러 줄을 포함하는 표현식의 시간을 측정하는 방법을 보여줍니다. 여기서 우리는 누락되었거나 존재하는 객체 어트리뷰트를 검사하는데 hasattr()try/except를 사용하는 비용을 비교합니다:

$ python -m timeit 'try:' '  str.__bool__' 'except AttributeError:' '  pass'
20000 loops, best of 5: 15.7 usec per loop
$ python -m timeit 'if hasattr(str, "__bool__"): pass'
50000 loops, best of 5: 4.26 usec per loop

$ python -m timeit 'try:' '  int.__bool__' 'except AttributeError:' '  pass'
200000 loops, best of 5: 1.43 usec per loop
$ python -m timeit 'if hasattr(int, "__bool__"): pass'
100000 loops, best of 5: 2.23 usec per loop
>>> import timeit
>>> # attribute is missing
>>> s = """\
... try:
...     str.__bool__
... except AttributeError:
...     pass
... """
>>> timeit.timeit(stmt=s, number=100000)
0.9138244460009446
>>> s = "if hasattr(str, '__bool__'): pass"
>>> timeit.timeit(stmt=s, number=100000)
0.5829014980008651
>>>
>>> # attribute is present
>>> s = """\
... try:
...     int.__bool__
... except AttributeError:
...     pass
... """
>>> timeit.timeit(stmt=s, number=100000)
0.04215312199994514
>>> s = "if hasattr(int, '__bool__'): pass"
>>> timeit.timeit(stmt=s, number=100000)
0.08588060699912603

timeit 모듈이 여러분이 정의한 함수에 액세스하도록 하려면, import 문이 포함된 setup 매개 변수를 전달하면 됩니다:

def test():
    """Stupid test function"""
    L = [i for i in range(100)]

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))

또 다른 옵션은 globals()globals 매개 변수로 전달해서, 여러분의 현재 전역 이름 공간에서 코드가 실행되도록 하는 것입니다. 임포트를 개별적으로 지정하는 것보다 편리 할 수 있습니다:

def f(x):
    return x**2
def g(x):
    return x**4
def h(x):
    return x**8

import timeit
print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))