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