# `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'`입니다; 타이머 함수는 플랫폼에 따라 다릅니다 (모듈 독스트링을 참조하십시오). stmt와 setup은 여러 줄에 걸친 문자열 리터럴을 포함하지 않는 한 `;`나 줄 바꿈으로 구분된 여러 개의 문장을 포함 할 수도 있습니다. 문장은 기본적으로 timeit의 이름 공간 내에서 실행됩니다; 이 동작은 globals에 이름 공간을 전달하여 제어 할 수 있습니다.

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

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

  stmt와 setup 매개 변수는 인자 없이 호출 할 수 있는 객체를 받아들일 수도 있습니다. 이렇게 하면 `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()))