"timeit" --- 작은 코드 조각의 실행 시간 측정
********************************************

**소스 코드:** Lib/timeit.py

======================================================================

This module provides a simple way to time small bits of Python code.
It has both a 명령 줄 인터페이스 as well as a callable one.  It avoids
a number of common traps for measuring execution times. See also Tim
Peters' introduction to the "Algorithms" chapter in the second edition
of *Python Cookbook*, published by O'Reilly.


기본 예제
=========

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

   $ 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

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
   >>> # 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()))
