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

소스 코드: Lib/timeit.py

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

## 기본 예제

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

    $ 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

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

    >>> 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() 이고, 초 단위의 float를 반환합니다. 대안인 time.perf_counter_ns 는 나노초 단위의 정수를 반환합니다.

  버전 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)`.

    Added in version 3.6.

  repeat(repeat=5, number=1000000)

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

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

    참고

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

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

  print_exc(file=None)

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

    일반적인 사용:

        t = Timer(...)       # try/except 바깥
        try:
            t.timeit(...)    # 또는 t.repeat(...)
        except Exception:
            t.print_exc()

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

## 명령 줄 인터페이스

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

    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, --process

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

  Added in version 3.3.

-u, --unit=U

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

  Added in version 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

In the output, there are three fields. The loop count, which tells you how many times the statement body was run per timing loop repetition. The repetition count (‘best of 5’) which tells you how many times the timing loop was repeated, and finally the time the statement body took on average within the best repetition of the timing loop. That is, the time the fastest repetition took divided by the loop count.

    >>> 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
    >>> # 어트리뷰트가 없습니다
    >>> 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
    >>>
    >>> # 어트리뷰트가 있습니다
    >>> 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():
        """멍청한 테스트 함수"""
        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()))