PEP 418 – 단조 시간, 성능 카운터, 프로세스 시간 함수 추가
- Author:
- Cameron Simpson <cs at cskk.id.au>, Jim J. Jewett <jimjjewett at gmail.com>, Stephen J. Turnbull <stephen at xemacs.org>, Victor Stinner <vstinner at python.org>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 26-Mar-2012
- Python-Version:
- 3.3
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 Python 3.3에 time.get_clock_info(name), time.monotonic(), time.perf_counter(), time.process_time() 함수를 추가할 것을 제안합니다.
근거
프로그램이 이벤트를 예약하거나 타임아웃을 구현하는 데 시스템 시간을 사용하면, 시스템 시간이 수동으로 변경되거나 NTP에 의해 자동으로 조정될 때 이벤트를 적절한 순간에 실행하지 못하거나 타임아웃을 너무 이르게 또는 너무 늦게 끝낼 수 있습니다. 시스템 시간 갱신의 영향을 받지 않으려면 대신 단조 시계를 사용해야 합니다: time.monotonic().
함수의 성능을 측정하기 위해 time.clock()을 사용할 수 있지만, 이 함수는 Windows와 Unix에서 매우 다르게 동작합니다. Windows에서 time.clock()은 sleep 동안 경과한 시간을 포함하지만, Unix에서는 포함하지 않습니다. time.clock()의 해상도는 Windows에서는 매우 좋지만, Unix에서는 매우 나쁩니다. 이식성 있는 동작(예: 슬립 중에 소요된 시간 포함)으로 항상 가장 정밀한 성능 카운터를 얻으려면 대신 새 time.perf_counter() 함수를 사용해야 합니다.
지금까지 Python은 CPU 시간을 측정하는 이식 가능한 함수를 직접 제공하지 않았습니다. Unix에서는 time.clock()을 사용할 수 있지만 해상도가 나쁩니다. Unix에서는 resource.getrusage()나 os.times()도 사용할 수 있지만, 커널 공간과 사용자 공간에서 소비한 시간의 합을 계산해야 합니다. 새 time.process_time() 함수는 항상 CPU 시간(슬립 중에 경과한 시간 제외)을 측정하며 사용 가능한 최고의 해상도를 갖는 이식성 있는 카운터 역할을 합니다.
운영 체제마다 시계와 성능 카운터를 다르게 구현하므로, 정확히 어떤 함수가 사용되는지와 해상도 같은 시계의 몇몇 속성을 알면 유용합니다. 새로운 time.get_clock_info() 함수는 각 Python 시간 함수에 대해 사용 가능한 모든 정보에 접근할 수 있게 해 줍니다.
새 함수:
time.monotonic(): 타임아웃과 스케줄링, 시스템 시계 갱신의 영향을 받지 않음time.perf_counter(): 벤치마킹, 짧은 기간에 대해 가장 정밀한 시계time.process_time(): 프로파일링, 프로세스의 CPU 시간
새 함수의 사용처:
- time.monotonic(): concurrent.futures, multiprocessing, queue(큐), subprocess, telnet, threading 모듈에서 타임아웃을 구현하는 데 사용합니다.
- time.perf_counter(): trace 및 timeit 모듈, pybench 프로그램
- time.process_time(): profile 모듈
- time.get_clock_info(): 해상도 같은 타이머 정보를 표시하기 위한 pybench 프로그램
time.clock() 함수는 이식성이 없으므로 사용 중단되었습니다: 운영 체제에 따라 다르게 동작합니다. 요구 사항에 따라 대신 time.perf_counter()나 time.process_time()을 사용해야 합니다. time.clock()은 사용 중단으로 표시되지만 제거할 계획은 없습니다.
제약 사항:
- 시스템 일시 중단 이후의 시계 동작은 새 함수의 설명서에 정의되어 있지 않습니다. 이 동작은 운영 체제에 따라 다릅니다: 아래의 Monotonic Clocks 절을 참조하십시오. 최근의 일부 운영 체제는 두 가지 시계를 제공하는데, 하나는 시스템 일시 중단 동안 경과한 시간을 포함하고 다른 하나는 이 시간을 포함하지 않습니다. 대부분의 운영 체제는 한 종류의 시계만 제공합니다.
- time.monotonic()과 time.perf_counter()는 조정될 수도 있고 조정되지 않을 수도 있습니다. 예를 들어,
CLOCK_MONOTONIC은 Linux에서 점진적으로 조정(slew)되는 반면,GetTickCount()는 Windows에서 조정되지 않습니다. 단조 시계가 조정 가능한지 확인하려면time.get_clock_info('monotonic')['adjustable']를 사용할 수 있습니다. - Python 표준 라이브러리에 필요하지 않고 흔히 요청되는 기능도 아니므로, 이 PEP는 time.thread_time() 함수를 제안하지 않습니다. 그러한 함수는 Windows와 Linux에서만 사용할 수 있을 것입니다. Linux에서는
time.clock_gettime(CLOCK_THREAD_CPUTIME_ID)를 사용할 수 있습니다. Windows에서는 ctypes나 다른 모듈을 사용하여GetThreadTimes()함수를 호출할 수 있습니다.
Python 함수
새 함수
time.get_clock_info(name)
지정한 시계에 관한 정보를 가져옵니다. 지원되는 시계 이름은 다음과 같습니다:
"clock":time.clock()"monotonic":time.monotonic()"perf_counter":time.perf_counter()"process_time":time.process_time()"time":time.time()
다음 속성을 가진 time.clock_info 객체를 반환합니다:
implementation(str): 기반 운영 체제 함수의 이름입니다. 예:"QueryPerformanceCounter()","clock_gettime(CLOCK_REALTIME)".monotonic(bool): 시계가 뒤로 갈 수 없으면 True입니다.adjustable(bool): 시계가 자동으로(예: NTP 데몬에 의해) 또는 시스템 관리자에 의해 수동으로 변경될 수 있으면True이고, 그렇지 않으면False입니다.resolution(float): 시계의 해상도(초 단위)입니다.
time.monotonic()
단조 시계, 즉 뒤로 갈 수 없는 시계입니다. 시스템 시계 갱신의 영향을 받지 않습니다. 반환값의 기준점은 정의되어 있지 않으므로, 연속된 호출 결과 사이의 차이만 유효하며 그 차이는 초 단위의 수입니다.
Vista보다 오래된 Windows 버전에서는 time.monotonic()이 GetTickCount() 정수 오버플로(32비트, 49.7일 후 롤오버)를 감지합니다. 오버플로가 감지될 때마다 내부 에포크(기준 시간)를 232만큼 증가시킵니다. 에포크는 프로세스 로컬 상태에 저장되므로, 49일 넘게 실행되는 두 Python 프로세스에서 time.monotonic()의 값이 서로 다를 수 있습니다. 더 최신 버전의 Windows와 다른 운영 체제에서는 time.monotonic()이 시스템 전역입니다.
가용성: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris. GNU/Hurd에서는 사용할 수 없습니다.
의사 코드 [2]:
if os.name == 'nt':
# GetTickCount64() requires Windows Vista, Server 2008 or later
if hasattr(_time, 'GetTickCount64'):
def monotonic():
return _time.GetTickCount64() * 1e-3
else:
def monotonic():
ticks = _time.GetTickCount()
if ticks < monotonic.last:
# Integer overflow detected
monotonic.delta += 2**32
monotonic.last = ticks
return (ticks + monotonic.delta) * 1e-3
monotonic.last = 0
monotonic.delta = 0
elif sys.platform == 'darwin':
def monotonic():
if monotonic.factor is None:
factor = _time.mach_timebase_info()
monotonic.factor = timebase[0] / timebase[1] * 1e-9
return _time.mach_absolute_time() * monotonic.factor
monotonic.factor = None
elif hasattr(time, "clock_gettime") and hasattr(time, "CLOCK_HIGHRES"):
def monotonic():
return time.clock_gettime(time.CLOCK_HIGHRES)
elif hasattr(time, "clock_gettime") and hasattr(time, "CLOCK_MONOTONIC"):
def monotonic():
return time.clock_gettime(time.CLOCK_MONOTONIC)
Windows에서는 QueryPerformanceCounter()가 GetTickCount()보다 해상도가 더 높지만 사용하지 않습니다. 신뢰할 수 없고 문제가 너무 많기 때문입니다.
time.perf_counter()
짧은 시간을 측정하기 위한, 사용 가능한 가장 높은 해상도의 성능 카운터입니다. 절전(sleep) 중에 경과한 시간을 포함하며 시스템 전역입니다. 반환 값의 기준점은 정의되어 있지 않으므로, 연속된 호출 결과 사이의 차이만 유효하며 그 차이는 초 단위의 수입니다.
모든 플랫폼에서 사용할 수 있습니다.
의사 코드:
if os.name == 'nt':
def _win_perf_counter():
if _win_perf_counter.frequency is None:
_win_perf_counter.frequency = _time.QueryPerformanceFrequency()
return _time.QueryPerformanceCounter() / _win_perf_counter.frequency
_win_perf_counter.frequency = None
def perf_counter():
if perf_counter.use_performance_counter:
try:
return _win_perf_counter()
except OSError:
# QueryPerformanceFrequency() fails if the installed
# hardware does not support a high-resolution performance
# counter
perf_counter.use_performance_counter = False
if perf_counter.use_monotonic:
# The monotonic clock is preferred over the system time
try:
return time.monotonic()
except OSError:
perf_counter.use_monotonic = False
return time.time()
perf_counter.use_performance_counter = (os.name == 'nt')
perf_counter.use_monotonic = hasattr(time, 'monotonic')
time.process_time()
현재 프로세스의 시스템 CPU 시간과 사용자 CPU 시간의 합입니다. 절전(sleep) 중에 경과한 시간은 포함하지 않습니다. 정의상 프로세스 전역입니다. 반환 값의 기준점은 정의되어 있지 않으므로, 연속된 호출 결과 사이의 차이만 유효합니다.
모든 플랫폼에서 사용할 수 있습니다.
의사 코드 [2]:
if os.name == 'nt':
def process_time():
handle = _time.GetCurrentProcess()
process_times = _time.GetProcessTimes(handle)
return (process_times['UserTime'] + process_times['KernelTime']) * 1e-7
else:
try:
import resource
except ImportError:
has_resource = False
else:
has_resource = True
def process_time():
if process_time.clock_id is not None:
try:
return time.clock_gettime(process_time.clock_id)
except OSError:
process_time.clock_id = None
if process_time.use_getrusage:
try:
usage = resource.getrusage(resource.RUSAGE_SELF)
return usage[0] + usage[1]
except OSError:
process_time.use_getrusage = False
if process_time.use_times:
try:
times = _time.times()
cpu_time = times.tms_utime + times.tms_stime
return cpu_time / process_time.ticks_per_seconds
except OSError:
process_time.use_getrusage = False
return _time.clock()
if (hasattr(time, 'clock_gettime')
and hasattr(time, 'CLOCK_PROF')):
process_time.clock_id = time.CLOCK_PROF
elif (hasattr(time, 'clock_gettime')
and hasattr(time, 'CLOCK_PROCESS_CPUTIME_ID')):
process_time.clock_id = time.CLOCK_PROCESS_CPUTIME_ID
else:
process_time.clock_id = None
process_time.use_getrusage = has_resource
process_time.use_times = hasattr(_time, 'times')
if process_time.use_times:
# sysconf("SC_CLK_TCK"), or the HZ constant, or 60
process_time.ticks_per_seconds = _times.ticks_per_seconds
기존 함수
time.time()
일반적으로 상용시(civil time)인 시스템 시간입니다. 정의상 시스템 전역입니다. 시스템 관리자가 수동으로 설정하거나 NTP 데몬이 자동으로 설정할 수 있습니다.
모든 플랫폼에서 사용할 수 있으며 실패하지 않습니다.
의사 코드 [2]:
if os.name == "nt":
def time():
return _time.GetSystemTimeAsFileTime()
else:
def time():
if hasattr(time, "clock_gettime"):
try:
return time.clock_gettime(time.CLOCK_REALTIME)
except OSError:
# CLOCK_REALTIME is not supported (unlikely)
pass
if hasattr(_time, "gettimeofday"):
try:
return _time.gettimeofday()
except OSError:
# gettimeofday() should not fail
pass
if hasattr(_time, "ftime"):
return _time.ftime()
else:
return _time.time()
time.sleep()
주어진 초만큼 실행을 일시 중단합니다. 신호가 포착되면 해당 신호의 포착 루틴이 실행된 뒤 time.sleep()이 종료되므로, 실제 중단 시간은 요청한 시간보다 짧을 수 있습니다. 또한 시스템의 다른 작업 스케줄링 때문에 중단 시간이 요청한 시간보다 임의의 양만큼 길어질 수도 있습니다.
의사 코드 [2]:
try:
import select
except ImportError:
has_select = False
else:
has_select = hasattr(select, "select")
if has_select:
def sleep(seconds):
return select.select([], [], [], seconds)
elif hasattr(_time, "delay"):
def sleep(seconds):
milliseconds = int(seconds * 1000)
_time.delay(milliseconds)
elif os.name == "nt":
def sleep(seconds):
milliseconds = int(seconds * 1000)
win32api.ResetEvent(hInterruptEvent);
win32api.WaitForSingleObject(sleep.sigint_event, milliseconds)
sleep.sigint_event = win32api.CreateEvent(NULL, TRUE, FALSE, FALSE)
# SetEvent(sleep.sigint_event) will be called by the signal handler of SIGINT
elif os.name == "os2":
def sleep(seconds):
milliseconds = int(seconds * 1000)
DosSleep(milliseconds)
else:
def sleep(seconds):
seconds = int(seconds)
_time.sleep(seconds)
폐지 예정 함수
time.clock()
Unix에서는 현재 프로세서 시간을 초 단위의 부동 소수점 수로 반환합니다. 정의상 프로세스 전역입니다. 해상도와, 사실 “프로세서 시간”이라는 의미의 정의 자체도 같은 이름의 C 함수에 따라 달라지지만, 어떤 경우든 Python 벤치마킹이나 알고리즘 시간 측정에는 이 함수를 사용해야 합니다.
Windows에서 이 함수는 Win32 함수 QueryPerformanceCounter()를 기반으로, 이 함수를 처음 호출한 이후 경과한 벽시계(wall-clock) 시간을 초 단위의 부동 소수점 수로 반환합니다. 해상도는 일반적으로 1마이크로초보다 좋습니다. 시스템 전역에 적용됩니다.
의사 코드 [2]:
if os.name == 'nt':
def clock():
try:
return _win_perf_counter()
except OSError:
# QueryPerformanceFrequency() fails if the installed
# hardware does not support a high-resolution performance
# counter
pass
return _time.clock()
else:
clock = _time.clock
대안: API 설계
time.monotonic()의 다른 이름
- time.counter()
- time.metronomic()
- time.seconds()
- time.steady(): “steady”는 모호합니다. 사람마다 다른 의미로 받아들입니다. 예를 들어 Linux에서는 CLOCK_MONOTONIC이 조정됩니다. 실제 시간을 기준 시계로 사용한다면 CLOCK_MONOTONIC이 안정적(steady)이라고 말할 수 있습니다. 하지만 CLOCK_MONOTONIC은 시스템이 일시 중단되면 함께 멈추는 반면, 실제 시간에는 일시 중단 상태로 보낸 시간이 모두 포함됩니다.
- time.timeout_clock()
- time.wallclock(): time.monotonic()은 “벽시계(wall clock)”라고도 하는 시스템 시간이 아니라, 시작 시점이 정해지지 않은 단조 시계입니다.
단조 시계를 사용할 수 없을 때 시스템 시간으로 대체하던 이전 버전의 time.monotonic()에 대해 “time.try_monotonic()”이라는 이름도 제안되었습니다.
time.perf_counter()의 다른 이름
- time.high_precision()
- time.highres()
- time.hires()
- time.performance_counter()
- time.timer()
운영 체제 시계만 노출하기
고수준 시계를 정의하는 것은 어려운 작업이므로, 이를 정의하지 않아도 되도록 운영 체제 시계만 노출하는 더 단순한 접근 방식이 있습니다. 예를 들어 time.clock_gettime()과 관련 시계 식별자는 이미 Python 3.3에 추가되었습니다.
time.monotonic(): 시스템 시간으로 대체
사용할 수 있는 단조 시계가 없으면 time.monotonic()은 시스템 시간으로 대체합니다.
문제점:
- 이러한 함수를 문서에서 올바르게 정의하기 어렵습니다. 단조적입니까? 안정적입니까? 조정됩니까?
- 일부 사용자는 단조 시계를 사용할 수 없을 때 다른 시계를 사용할지, 오류를 표시할지, 아니면 다른 조치를 취할지를 직접 결정하기를 원합니다.
이러한 함수를 정의하기 위해 여러 API가 제안되었습니다.
플래그가 있는 함수 하나: time.monotonic(fallback=True)
- time.monotonic(fallback=True)는 사용할 수 있는 단조 시계가 없거나 단조 시계가 실패하면 시스템 시간으로 대체합니다.
- time.monotonic(fallback=False)는 단조 시계가 실패하면 OSError를 발생시키고, 시스템이 단조 시계를 제공하지 않으면 NotImplementedError를 발생시킵니다
호출자에서 상수로 전달되는 키워드 인자는 대개 좋지 않은 API입니다.
함수에서 NotImplementedError를 발생시키는 것은 Python에서 흔치 않은 일이므로 피해야 합니다.
플래그가 없는 time.monotonic() 함수 하나
time.monotonic()은 (time: float, is_monotonic: bool)을 반환합니다.
다른 방법은 함수 속성 time.monotonic.is_monotonic을 사용하는 것입니다. 이 속성의 값은 time.monotonic()을 처음 호출하기 전에는 None입니다.
제약 조건 목록으로 시계 선택하기
제안된 PEP는 몇 가지 새로운 시계를 제공하지만, 여러 플랫폼에서 유용한 시계를 제공하기 위해 그 보장을 의도적으로 느슨하게 두었습니다. 이 때문에 호출 자체에 정책이 내재되며, 따라서 호출자가 정책을 선택해야 합니다.
“시계 선택” 방식은 대부분의 플랫폼 시계를 사용할 수 있게 하고 호출자가 그중에서 고르도록 하여, 필요한 경우 호출자가 자신만의 정책을 구현할 수 있게 하는 추가 API를 제안합니다. 흔하고 단순한 사용 사례를 위해 PEP에서 제안한 시계는 여전히 제공될 것으로 예상됩니다.
이를 위해서는 두 가지 기능이 필요합니다: 시계의 열거, 그리고 사용자가 시계의 적합성을 평가할 수 있게 해 주는 시계의 메타데이터입니다.
주요 인터페이스는 단순한 선택을 쉽게 만들어 주는 함수입니다: 호출자는 여러 플래그를 조합하여 time.get_clock(*flags)를 사용할 수 있습니다. 여기에는 최소한 다음이 포함됩니다:
- time.MONOTONIC: 시계가 뒤로 갈 수 없음
- time.STEADY: 시계 속도가 일정함
- time.ADJUSTED: 시계가 조정될 수 있음(예: NTP에 의해)
- time.HIGHRES: 해상도가 가장 높은 시계
이 함수는 현재 시간을 반환하는 .now() 메서드를 가진 시계 객체를 반환합니다. 시계 객체에는 시계의 기능 집합을 설명하는 메타데이터가 달려 있으며, 이 객체의 .flags 필드에는 요청한 플래그가 최소한 모두 들어 있습니다.
time.get_clock()은 일치하는 시계를 찾지 못하면 None을 반환하므로, or 연산자로 호출을 연결할 수 있습니다. 단순한 정책 결정의 예:
T = get_clock(MONOTONIC) or get_clock(STEADY) or get_clock()
t = T.now()
사용 가능한 시계에는 항상 최소한 time.time()의 래퍼가 포함되므로, 마지막에 플래그 없이 호출하면 언제나 동작하는 시계를 얻을 수 있습니다.
시스템 시계의 플래그 예:
- QueryPerformanceCounter: MONOTONIC | HIGHRES
- GetTickCount: MONOTONIC | STEADY
- CLOCK_MONOTONIC: MONOTONIC | STEADY (Linux에서는 MONOTONIC만)
- CLOCK_MONOTONIC_RAW: MONOTONIC | STEADY
- gettimeofday(): (플래그 없음)
클록 객체에는 다른 메타데이터도 들어 있습니다. 여기에는 위에 나열한 것 외에 추가 기능 플래그를 포함하는 클록 플래그, 하부 OS 기능의 이름, 클록 정밀도가 포함됩니다.
time.get_clock()은 여전히 하나의 클록만 선택하므로 열거 기능도 필요합니다. 가장 분명한 방법은 time.get_clocks()를 제공하는 것입니다. 이 함수는 time.get_clock()과 시그니처가 같지만, 요청한 플래그와 일치하는 모든 클록의 시퀀스를 반환합니다. 따라서 플래그를 하나도 요청하지 않으면 사용 가능한 모든 클록이 열거되며, 호출자는 메타데이터를 바탕으로 그중 하나를 임의로 선택할 수 있습니다.
부분 구현 예: clockutils.py.
운영 체제 버그를 우회해야 합니까?
Python이 클록 값과 이전 값 중 최댓값을 계산하여 단조 클록이 실제로 단조롭도록 보장해야 합니까?
정적 변수를 사용해 마지막으로 반환한 값을 캐시하는 것은 비교적 간단하므로, 이를 활용해 반환되는 값이 실제로 단조로운지 확인하는 것도 고려해 볼 만합니다.
- 가상 머신이 제공하는 클록은 신뢰성이 더 낮습니다.
- QueryPerformanceCounter()에는 알려진 버그가 있습니다(아직 수정되지 않은 것은 하나뿐입니다)
Python은 알려진 특정 운영 체제 버그 하나만 우회할 수 있습니다: KB274323에 이 버그를 우회하는 코드 예제가 있습니다(GetTickCount()를 사용해 QueryPerformanceCounter()의 도약을 감지합니다).
비단조성을 “보정”할 때의 문제점:
- 클록이 실수로 한 시간 앞당겨졌다가 다시 되돌려지면, 한 시간 동안 쓸모 있는 클록을 사용할 수 없습니다
- 캐시는 프로세스 간에 공유되지 않으므로 서로 다른 프로세스는 같은 클록 값을 보지 못합니다
용어집
- Accuracy:
- 특정 측정 기기로 잰 측정값이 참값에서 벗어난 정도입니다. Accuracy and precision도 참조하십시오. 클록의 부정확성은 정밀도 부족, 드리프트, 클록의 잘못된 초기 설정 때문에 생길 수 있습니다(예를 들어 카운터를 재설정할 때 완벽하게 동기화하기가 매우 어려우므로, 스레드 타이밍은 본질적으로 부정확합니다).
- Adjusted:
- 클록을 올바른 시각으로 재설정하는 것입니다. 이는 <Step>으로 하거나 <Slewing>으로 할 수 있습니다.
- Civil Time:
- 하루 중의 시각이며 시스템 외부의 것입니다. 오전 10:45:13은 상용시(Civil time)이지만 45초는 상용시가 아닙니다. 기존 함수
time.localtime()과time.gmtime()이 제공합니다. 이 PEP로 변경되지 않습니다. - Clock:
- 시간을 측정하는 기기입니다. 클록마다 특성이 다릅니다. 예를 들어 나노초 <precision>을 가진 클록은 몇 분 뒤에 <drift>하기 시작할 수 있지만, 덜 정밀한 클록은 며칠 동안 정확하게 유지되기도 합니다. 이 PEP는 주로 초 단위를 사용하는 클록을 다룹니다.
- Counter:
- 특정 이벤트가 발생할 때마다 증가하는 클록입니다. 카운터는 엄격하게 단조롭지만 단조 클록은 아닙니다. 카운터는 고유하고 순서가 있는 타임스탬프를 생성하는 데 사용할 수 있지만, 이 타임스탬프를 <civil time>에 대응시킬 수는 없습니다. 틱은 몰아서 생성될 수 있어서, 같은 밀리초 안에 여러 번 증가한 뒤 며칠 동안 전혀 증가하지 않을 수도 있습니다.
- CPU Time:
- 특정 작업에 CPU 연산이 얼마나 쓰였는지를 나타내는 척도입니다. CPU 초는 흔히 정규화됩니다(그래서 실제 1초 동안 발생하는 CPU 초의 수가 달라질 수 있습니다). CPU 초는 프로파일링할 때 중요할 수 있지만, 사용자 응답 시간에 직접 대응하지 않으며 (실제 시간의) 초와 직접 비교할 수도 없습니다.
- Drift:
- 시스템 외부에서 정의된 “참” 시간에 대해 누적된 오차입니다. 드리프트는 부정밀함 때문에 생길 수도 있고, 시계 시간이 흐르는 평균 속도와 실제 시간이 흐르는 속도의 차이 때문에 생길 수도 있습니다.
- Epoch:
- 시계의 기준점입니다. <civil time>을 제공하는 시계의 경우, 기준점은 흔히 날짜(와 연도)가 1970년 1월 1일로 넘어가는 자정입니다. <clock_monotonic> 시계의 경우, 에포크가 정의되지 않을 수 있습니다(None으로 표현됩니다).
- Latency:
- 지연입니다. 시계 호출이 반환될 무렵이면 <real time>은 이미 흘러가 있으며, 흘러간 양이 시계의 정밀도보다 클 수도 있습니다.
- Monotonic:
- 단조 시계에 실제로 기대되는 특성입니다. 최대 한 방향으로만 움직입니다. 시계의 경우 그 방향은 앞쪽입니다. 또한 <clock>은 <steady>해야 하며, 초 단위로 변환할 수 있어야 합니다. 흔히 그 대가로 정의된 <epoch>나 <Civil Time>으로의 매핑이 없다는 점을 감수해야 합니다.
- Precision:
- 하나의 계측기로 같은 물리량을 여러 번 측정했을 때 측정값들 사이에 나타나는 편차의 크기입니다. 시계의 부정밀함은 실제 시간에 대해 시계 시간이 흐르는 속도가 변동하여 생길 수 있으며, 슬루잉에 의한 시계 조정도 이러한 변동에 포함됩니다.
- 프로세스 시간:
- 프로세스가 시작된 이후 경과한 시간입니다. 보통 <real time>이 아닌 <CPU time>으로 측정하며, 대개 프로세스가 일시 중단된 동안에는 흐르지 않습니다.
- 실제 시간:
- 현실 세계의 시간입니다. <Civil time>과 달리 <adjusted>되지 않는다는 차이가 있지만, 그 외에는 두 시간이 발맞추어 흘러야 합니다. “실시간 [운영] 체제(Real Time [Operating] Systems)”에서 말하는 “실시간(real time)”과는 관계가 없습니다. 이런 모호함을 피하기 위해 “벽시계 시간(wall clock time)”이라고 부르기도 하지만, 안타깝게도 그 용어는 또 다른 모호함을 불러옵니다.
- Resolution:
- 주어진 계측기에서 서로 다른 측정값을 낳는 두 물리량 사이의 가장 작은 차이입니다.
- Slew:
- 시계 속도를 약간 바꾸는 것으로, 보통 외부 기준에 대한 <drift>를 바로잡기 위해 사용합니다.
- Stability:
- 정확도의 지속성입니다. 예상되는 <drift>의 척도입니다.
- Steady:
- <stability>가 높고 <accuracy>와 <precision>이 비교적 높은 시계입니다. 실제로는 흔히 <clock_monotonic> 시계를 가리키는 데 쓰이지만, 연이은 틱 사이 간격의 일관성을 더 중시합니다.
- Step:
- 표현된 시간이 순간적으로 바뀌는 것입니다. 시계를 빠르게 하거나 느리게 하는(<slew>) 대신, 하나의 오프셋을 영구적으로 더합니다.
- 시스템 시간:
- 운영 체제가 나타내는 시간입니다.
- 스레드 시간:
- 스레드가 시작된 이후 경과한 시간입니다. 일반적으로 <real time>이 아니라 <CPU time>으로 측정하며, 보통 스레드가 유휴 상태인 동안에는 증가하지 않습니다.
- Wallclock:
- 벽에 걸린 시계가 가리키는 시간입니다. 일반적으로 <real time>의 동의어로 사용되지만, 안타깝게도 벽시계 시간이라는 말 자체도 모호합니다.
하드웨어 시계
하드웨어 시계 목록
- HPET: HPET(High Precision Event Timer) 칩은 최소 10 MHz로 증가하는 64비트 상향 카운터(메인 카운터) 하나와 최대 256개(최소 3개)의 비교기 집합으로 구성됩니다. 각 HPET는 최대 32개의 타이머를 가질 수 있습니다. HPET는 하루에 약 3초의 드리프트를 일으킬 수 있습니다.
- TSC(Time Stamp Counter): 과거에는 TSC가 내부 프로세서 클록 사이클마다 증가했지만, 현재는 증가율이 대개 상수이며(프로세서가 주파수를 바꾸더라도) 보통 프로세서의 최대 주파수와 같습니다. 여러 코어는 서로 다른 TSC 값을 가집니다. 시스템이 최대 절전 모드에 들어가면 TSC 값이 초기화됩니다. RDTSC 명령어로 이 카운터를 읽을 수 있습니다. 전력 절약을 위한 CPU 주파수 스케일링입니다.
- ACPI 전원 관리 타이머: 주파수가 3.5 MHz(3,579,545 Hz)인 ACPI 24비트 타이머입니다.
- Cyclone: Cyclone 타이머는 IBM “Summit” 시리즈 칩셋(예: x440)을 사용하는 컴퓨터를 포함하는 IBM EXA(Extended X-Architecture) 칩셋에서 32비트 카운터를 사용합니다. IA32 및 IA64 아키텍처에서 사용할 수 있습니다.
- PIT(programmable interrupt timer): 18.2 Hz ~ 1.2 MHz 범위에서 주파수를 설정할 수 있는 Intel 8253/8254 칩셋입니다. 16비트 카운터를 사용합니다.
- RTC(Real-time clock). 대부분의 RTC는 주파수가 32,768 Hz인 수정 발진기를 사용합니다.
Linux 클럭 소스
Linux 커널에는 시간 구현이 4가지 있었습니다: UTIME(1996), timer wheel(1997), HRT(2001), hrtimers(2007). 마지막 구현은 2001년 George Anzinger가 시작한 “high-res-timers” 프로젝트의 결과물이며, Thomas Gleixner와 Douglas Niehaus가 기여했습니다. hrtimers 구현은 2007년에 릴리스된 Linux 2.6.21에 병합되었습니다.
hrtimers는 다양한 클럭 소스를 지원합니다. 각 소스에 우선순위를 설정하여 어떤 소스를 사용할지 결정합니다. Linux는 다음 클럭 소스를 지원합니다:
- tsc
- hpet
- pit
- pmtmr: ACPI 전원 관리 타이머
- cyclone
고해상도 타이머가 모든 하드웨어 아키텍처에서 지원되는 것은 아닙니다. 적어도 x86/x86_64, ARM, PowerPC에서는 제공됩니다.
clock_getres()는 하부 클럭 소스와 관계없이 CLOCK_REALTIME과 CLOCK_MONOTONIC에 대해 1나노초를 반환합니다. 이에 대한 설명은 Thomas Gleixner가 작성한 Re: clock_getres() and real resolution (2012년 2월 9일)을 읽으십시오.
/sys/devices/system/clocksource/clocksource0 디렉터리에는 유용한 파일 두 개가 있습니다:
available_clocksource: 사용 가능한 클럭 소스 목록current_clocksource: 현재 사용 중인 클럭 소스입니다. 이 파일에 클럭 소스의 이름을 기록하여 현재 클럭 소스를 변경할 수 있습니다.
/proc/timer_list에는 모든 하드웨어 타이머의 목록이 들어 있습니다.
time(7) 매뉴얼 페이지도 읽으십시오: “시간과 타이머 개요”.
FreeBSD timecounter
kern.timecounter.choice는 사용 가능한 하드웨어 클럭을 우선순위와 함께 나열합니다. sysctl 프로그램을 사용하여 timecounter를 변경할 수 있습니다. 예시:
# dmesg | grep Timecounter
Timecounter "i8254" frequency 1193182 Hz quality 0
Timecounter "ACPI-safe" frequency 3579545 Hz quality 850
Timecounter "HPET" frequency 100000000 Hz quality 900
Timecounter "TSC" frequency 3411154800 Hz quality 800
Timecounters tick every 10.000 msec
# sysctl kern.timecounter.choice
kern.timecounter.choice: TSC(800) HPET(900) ACPI-safe(850) i8254(0) dummy(-1000000)
# sysctl kern.timecounter.hardware="ACPI-fast"
kern.timecounter.hardware: HPET -> ACPI-fast
사용 가능한 클럭:
- “TSC”: 프로세서의 타임스탬프 카운터
- “HPET”: 고정밀 이벤트 타이머
- “ACPI-fast”: ACPI 전원 관리 타이머 (고속 모드)
- “ACPI-safe”: ACPI 전원 관리 타이머 (안전 모드)
- “i8254”: Intel 8254 칩셋을 사용하는 PIT
커밋 222222 (2011년 5월)은 ACPI-fast timecounter 품질을 900으로 낮추고 HPET timecounter 품질을 950으로 높였습니다: “최신 플랫폼의 HPET은 대체로 ACPI 타이머보다 해상도가 더 좋고 지연 시간이 더 짧습니다”.
Poul-Henning Kamp가 FreeBSD 프로젝트를 위해 작성한 Timecounters: Efficient and precise timekeeping in SMP kernels (2002)를 읽으십시오.
성능
하드웨어 클럭을 읽는 데에는 비용이 듭니다. 다음 표는 Intel Core i7-2600 3.40GHz (8코어) 환경의 Linux 3.3에서 여러 하드웨어 클럭의 성능을 비교합니다. 이 표들을 채우는 데 bench_time.c 프로그램을 사용했습니다.
| 함수 | TSC | ACPI PM | HPET |
|---|---|---|---|
| time() | 2 ns | 2 ns | 2 ns |
| CLOCK_REALTIME_COARSE | 10 ns | 10 ns | 10 ns |
| CLOCK_MONOTONIC_COARSE | 12 ns | 13 ns | 12 ns |
| CLOCK_THREAD_CPUTIME_ID | 134 ns | 135 ns | 135 ns |
| CLOCK_PROCESS_CPUTIME_ID | 127 ns | 129 ns | 129 ns |
| clock() | 146 ns | 146 ns | 143 ns |
| gettimeofday() | 23 ns | 726 ns | 637 ns |
| CLOCK_MONOTONIC_RAW | 31 ns | 716 ns | 607 ns |
| CLOCK_REALTIME | 27 ns | 707 ns | 629 ns |
| CLOCK_MONOTONIC | 27 ns | 723 ns | 635 ns |
하드웨어 가상화를 사용하는 kvm 위의 FreeBSD 8.0:
| 함수 | TSC | ACPI-Safe | HPET | i8254 |
|---|---|---|---|---|
| time() | 191 ns | 188 ns | 189 ns | 188 ns |
| CLOCK_SECOND | 187 ns | 184 ns | 187 ns | 183 ns |
| CLOCK_REALTIME_FAST | 189 ns | 180 ns | 187 ns | 190 ns |
| CLOCK_UPTIME_FAST | 191 ns | 185 ns | 186 ns | 196 ns |
| CLOCK_MONOTONIC_FAST | 188 ns | 187 ns | 188 ns | 189 ns |
| CLOCK_THREAD_CPUTIME_ID | 208 ns | 206 ns | 207 ns | 220 ns |
| CLOCK_VIRTUAL | 280 ns | 279 ns | 283 ns | 296 ns |
| CLOCK_PROF | 289 ns | 280 ns | 282 ns | 286 ns |
| clock() | 342 ns | 340 ns | 337 ns | 344 ns |
| CLOCK_UPTIME_PRECISE | 197 ns | 10380 ns | 4402 ns | 4097 ns |
| CLOCK_REALTIME | 196 ns | 10376 ns | 4337 ns | 4054 ns |
| CLOCK_MONOTONIC_PRECISE | 198 ns | 10493 ns | 4413 ns | 3958 ns |
| CLOCK_UPTIME | 197 ns | 10523 ns | 4458 ns | 4058 ns |
| gettimeofday() | 202 ns | 10524 ns | 4186 ns | 3962 ns |
| CLOCK_REALTIME_PRECISE | 197 ns | 10599 ns | 4394 ns | 4060 ns |
| CLOCK_MONOTONIC | 201 ns | 10766 ns | 4498 ns | 3943 ns |
각 함수를 100,000번 호출했으며, 호출 전후의 시간을 얻기 위해 CLOCK_MONOTONIC을 사용했습니다. 벤치마크는 5번 실행했으며, 그중 최소 시간을 취했습니다.
NTP 조정
NTP에는 클록을 조정하는 여러 방법이 있습니다:
- “슬루잉(slewing)”: 클록 주파수를 약간 더 빠르게 또는 느리게 변경합니다(
adjtime()함수로 수행합니다). 슬루 속도는 초당 0.5밀리초로 제한되므로, 1초를 조정할 때마다 2000초의 분할 적용 기간이 필요합니다. 따라서 수 초에 달하는 조정은 분할 적용하는 데 몇 시간 또는 며칠이 걸릴 수 있습니다. - “스테핑(stepping)”: 한 번의 불연속적인 단계로 큰 폭을 건너뜁니다(
settimeofday()함수로 수행합니다)
기본적으로 오프셋이 128ms 미만이면 시간을 슬루잉하고, 그렇지 않으면 스테핑합니다.
(CPU 사이클과 같은 시간 유사 객체가 아니라) “실제” 시간을 측정하려는 경우에는 일반적으로 점진적 조정(slewing)이 바람직합니다(즉, CLOCK_MONOTONIC_RAW가 아니라 CLOCK_MONOTONIC을 사용해야 합니다). 이는 NTP 연결의 반대편에 있는 클록이 아마도 여러분의 클록보다 시간을 더 정확하게 유지하기 때문입니다: 어쨌든 3만 5천 달러짜리 세슘 시간 측정 장비라면 여러분 PC의 3달러짜리 수정 진동자보다는 더 나은 일을 해 주기를 기대합니다.
자세한 내용은 NTP 데몬 문서를 참고하십시오.
운영 체제 시간 함수
단조 클록
| 이름 | C 해상도 | 조정됨 | 슬립 포함 | 일시 중단 포함 |
|---|---|---|---|---|
| gethrtime() | 1 ns | 아니요 | 예 | 예 |
| CLOCK_HIGHRES | 1 ns | 아니요 | 예 | 예 |
| CLOCK_MONOTONIC | 1 ns | Linux에서 슬루잉됨 | 예 | 아니요 |
| CLOCK_MONOTONIC_COARSE | 1 ns | Linux에서 점진적으로 조정됨(slew) | 예 | 아니요 |
| CLOCK_MONOTONIC_RAW | 1 ns | 아니요 | 예 | 아니요 |
| CLOCK_BOOTTIME | 1 ns | ? | 예 | 예 |
| CLOCK_UPTIME | 1 ns | 아니요 | 예 | ? |
| mach_absolute_time() | 1 ns | 아니요 | 예 | 아니요 |
| QueryPerformanceCounter() | - | 아니요 | 예 | ? |
| GetTickCount[64]() | 1 ms | 아니요 | 예 | 예 |
| timeGetTime() | 1 ms | 아니요 | 예 | ? |
“C 해상도” 열은 기반 C 구조체의 해상도입니다.
x86_64에서의 클록 해상도 예시
| 이름 | 운영 체제 | OS 해상도 | Python 해결 |
|---|---|---|---|
| QueryPerformanceCounter | Windows Seven | 10 ns | 10 ns |
| CLOCK_HIGHRES | SunOS 5.11 | 2 ns | 265 ns |
| CLOCK_MONOTONIC | Linux 3.0 | 1 ns | 322 ns |
| CLOCK_MONOTONIC_RAW | Linux 3.3 | 1 ns | 628 ns |
| CLOCK_BOOTTIME | Linux 3.3 | 1 ns | 628 ns |
| mach_absolute_time() | Mac OS 10.6 | 1 ns | 3 µs |
| CLOCK_MONOTONIC | FreeBSD 8.2 | 11 ns | 5 µs |
| CLOCK_MONOTONIC | OpenBSD 5.0 | 10 ms | 5 µs |
| CLOCK_UPTIME | FreeBSD 8.2 | 11 ns | 6 µs |
| CLOCK_MONOTONIC_COARSE | Linux 3.3 | 1 ms | 1 ms |
| CLOCK_MONOTONIC_COARSE | Linux 3.0 | 4 ms | 4 ms |
| GetTickCount64() | Windows Seven | 16 ms | 15 ms |
“OS Resolution”은 운영 체제가 알려 주는 해상도입니다. “Python Resolution”은 clock_resolution.py 프로그램을 사용하여 Python에서 계산한 값으로, 시간 함수를 두 번 호출했을 때 두 결과 사이의 가장 작은 차이입니다.
mach_absolute_time
Mac OS X는 단조 시계인 mach_absolute_time()을 제공합니다. 이 시계는 시스템 부팅 이후 경과한 절대 시간을 기반으로 합니다. 이 시계는 조정되지 않으며 설정할 수도 없습니다.
mach_timebase_info()는 시계 값을 나노초 단위의 수로 변환하는 분수를 제공합니다. Technical Q&A QA1398도 참고하십시오.
mach_absolute_time()은 PowerPC CPU에서는 슬립 중에 멈추지만 Intel CPU에서는 멈추지 않습니다: Different behaviour of mach_absolute_time() on i386/ppc.
CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW, CLOCK_BOOTTIME
CLOCK_MONOTONIC과 CLOCK_MONOTONIC_RAW는 지정되지 않은 어떤 시작 시점 이후의 단조 시간을 나타냅니다. 이 시계들은 설정할 수 없습니다. 해상도는 clock_getres()를 사용하여 읽을 수 있습니다.
문서: 사용하는 운영 체제의 매뉴얼 페이지를 참고하십시오. 예시:
CLOCK_MONOTONIC은 적어도 다음 운영 체제에서 사용할 수 있습니다:
- DragonFly BSD, FreeBSD >= 5.0, OpenBSD, NetBSD
- Linux
- Solaris
다음 운영 체제는 CLOCK_MONOTONIC을 지원하지 않습니다:
- GNU/Hurd (open issues/ clock_gettime 참고)
- Mac OS X
- Windows
Linux에서는 NTP가 CLOCK_MONOTONIC의 속도를 조정(slew)할 수 있지만, 시계가 뒤로 건너뛸 수는 없습니다.
CLOCK_MONOTONIC_RAW는 Linux 전용입니다. 이 시계는 CLOCK_MONOTONIC과 비슷하지만, NTP 조정의 영향을 받지 않는 하드웨어 기반의 원시 시간에 접근할 수 있게 해 줍니다. CLOCK_MONOTONIC_RAW를 사용하려면 Linux 2.6.28 이상이 필요합니다.
Linux 2.6.39와 glibc 2.14는 새로운 시계인 CLOCK_BOOTTIME을 도입합니다. CLOCK_BOOTTIME은 서스펜드 상태로 보낸 시간도 포함한다는 점을 제외하면 CLOCK_MONOTONIC과 동일합니다. Waking systems from suspend (2011년 3월)도 읽어 보십시오.
CLOCK_MONOTONIC은 기계가 서스펜드 상태인 동안 멈춥니다.
Linux는 Linux 2.6.32부터 CLOCK_MONOTONIC_COARSE도 제공합니다. 이 시계는 CLOCK_MONOTONIC과 비슷하며, 정밀도는 더 낮지만 더 빠릅니다.
clock_gettime()은 표준 C 라이브러리가 지정된 클록을 지원하더라도 시스템이 이를 지원하지 않으면 실패합니다. 예를 들어 CLOCK_MONOTONIC_RAW에는 커널 버전 2.6.28 이상이 필요합니다.
Windows: QueryPerformanceCounter
고해상도 성능 카운터입니다. 단조적입니다. 카운터의 주파수는 QueryPerformanceFrequency()를 사용하여 읽을 수 있습니다. 해상도는 1 / QueryPerformanceFrequency()입니다.
GetTickCount() 및 timeGetTime() 클록보다 해상도는 훨씬 높지만 장기 정밀도는 더 낮습니다. 예를 들어 저정밀도 클록에 비해 드리프트가 발생합니다.
문서:
QueryPerformanceCounter가 사용하는 하드웨어 클록:
- Windows XP: Intel 프로세서의 RDTSC 명령어이며, 클록 주파수는 프로세서의 주파수입니다(200 MHz에서 3 GHz 사이이며, 요즘은 보통 1 GHz보다 높습니다).
- Windows 2000: ACPI 전원 관리 타이머이며, 주파수 = 3,549,545 Hz입니다. boot.ini의 “/usepmtimer” 플래그를 통해 강제로 사용하게 할 수 있습니다.
QueryPerformanceFrequency()는 한 번만 호출해야 합니다. 시스템이 실행되는 동안에는 주파수가 변하지 않습니다. 설치된 하드웨어가 고해상도 성능 카운터를 지원하지 않으면 실패합니다.
QueryPerformanceCounter()는 조정할 수 없습니다. SetSystemTimeAdjustment()는 시스템 시간만 조정합니다.
버그:
- 하드웨어 버그로 인해 성능 카운터 값이 예기치 않게 앞으로 건너뛸 수 있습니다. KB274323을 참고하십시오.
- VirtualBox에서 QueryPerformanceCounter()는 하위 부분이 오버플로될 때마다 상위 부분을 증가시키지는 않습니다. Monotonic timers (2009)를 참고하십시오.
- VirtualBox의 HPET 가상화 장치에 버그가 있었습니다. QueryPerformanceCounter()가 약 42초 앞으로 건너뛰었습니다(issue #8707).
- Windows XP에는 버그가 있었습니다(KB896256 참고). 멀티프로세서 컴퓨터에서 QueryPerformanceCounter()가 프로세서마다 다른 값을 반환했습니다. 이 버그는 Windows XP SP2에서 수정되었습니다.
- 가변 주파수 프로세서의 문제: 메모리 소비를 줄이기 위해 작업 부하에 따라 주파수가 변경됩니다.
- Chromium은 “QueryPerformanceCounter is unreliable”이라는 이유로 Athlon X2 CPU(모델 15)에서 QueryPerformanceCounter()를 사용하지 않습니다(Chromium 소스 코드의 base/time_win.cc 참고).
Windows: GetTickCount(), GetTickCount64()
GetTickCount()와 GetTickCount64()는 단조적이고, 실패하지 않으며, SetSystemTimeAdjustment()에 의해 조정되지 않습니다. MSDN 문서: GetTickCount(), GetTickCount64(). 해상도는 GetSystemTimeAdjustment()를 사용하여 읽을 수 있습니다.
GetTickCount() 또는 GetTickCount64()로 얻은 경과 시간에는 시스템이 절전 또는 최대 절전 모드에 있었던 시간이 포함됩니다.
GetTickCount64()는 Windows Vista와 Windows Server 2008에 추가되었습니다.
문서화되지 않은 NtSetTimerResolution() 함수를 사용하면 정밀도를 높일 수 있습니다. 이 문서화되지 않은 함수를 사용하는 애플리케이션도 있습니다. 예: Timer Resolution.
WaitForSingleObject()는 GetTickCount()와 같은 타이머를 같은 정밀도로 사용합니다.
Windows: timeGetTime
timeGetTime 함수는 시스템 시간을 밀리초 단위로 가져옵니다. 시스템 시간은 Windows가 시작된 이후 경과한 시간입니다. timeGetTime() 문서를 읽어 보십시오.
timeGetTime()의 반환 타입은 32비트 부호 없는 정수입니다. GetTickCount()와 마찬가지로 timeGetTime()은 2^32밀리초(49.7일)가 지나면 값이 0으로 되돌아갑니다.
timeGetTime()이 가져오는 경과 시간에는 시스템이 절전 상태로 보낸 시간이 포함됩니다.
timeGetTime 함수의 기본 정밀도는 기기에 따라 5밀리초 이상일 수 있습니다.
timeBeginPeriod()를 사용하면 timeGetTime()의 정밀도를 최대 1밀리초까지 높일 수 있지만, 전력 소비에 나쁜 영향을 줍니다. timeBeginPeriod()를 호출하면 CreateWaitableTimer(), WaitForSingleObject(), Sleep() 등 다른 일부 타이밍 호출의 세분성에도 영향을 줍니다.
Note
timeGetTime()과 timeBeginPeriod()는 Windows 멀티미디어 라이브러리의 일부이므로, 프로그램을 winmm과 링크하거나 라이브러리를 동적으로 로드해야 합니다.
Solaris: CLOCK_HIGHRES
Solaris OS에는 최적의 하드웨어 소스를 사용하려고 시도하는 CLOCK_HIGHRES 타이머가 있으며, 이 타이머는 나노초에 가까운 해상도를 제공할 수 있습니다. CLOCK_HIGHRES는 조정할 수 없는 고해상도 시계입니다. clockid_t 값을 CLOCK_HIGHRES로 하여 생성한 타이머에 대해서는 시스템이 최적의 하드웨어 소스를 사용하려고 시도합니다.
CLOCK_HIGHRES의 해상도는 clock_getres()를 사용하여 읽을 수 있습니다.
Solaris: gethrtime
gethrtime() 함수는 현재의 고해상도 실시간을 반환합니다. 시간은 과거의 임의 시점 이후 경과한 나노초로 표현됩니다. 이 시간은 하루 중 시각과 전혀 관련이 없으므로, adjtime()이나 settimeofday()로 인해 재설정되거나 표류하지 않습니다. 고해상도(hires) 타이머는 비용이 적고 정확한 구간 시간 측정이 필요한 성능 측정 작업에 매우 적합합니다.
gethrtime()의 선형성은 일시 중단-재개 주기를 거치면 유지되지 않습니다(Bug 4272663).
Solaris 11의 gethrtime() 매뉴얼 페이지를 읽어 보십시오.
Solaris에서 gethrtime()은 clock_gettime(CLOCK_MONOTONIC)과 같습니다.
시스템 시간
| 이름 | C 해상도 | 슬립 포함 | 일시 중단 포함 |
|---|---|---|---|
| CLOCK_REALTIME | 1 ns | 예 | 예 |
| CLOCK_REALTIME_COARSE | 1 ns | 예 | 예 |
| GetSystemTimeAsFileTime | 100 ns | 예 | 예 |
| gettimeofday() | 1 µs | 예 | 예 |
| ftime() | 1 ms | 예 | 예 |
| time() | 1초 | 예 | 예 |
“C 해상도” 열은 기반이 되는 C 구조체의 해상도입니다.
x86_64에서의 클록 해상도 예시:
| 이름 | 운영 체제 | OS 해상도 | Python 해결 |
|---|---|---|---|
| CLOCK_REALTIME | SunOS 5.11 | 10 ms | 238 ns |
| CLOCK_REALTIME | Linux 3.0 | 1 ns | 238 ns |
| gettimeofday() | Mac OS 10.6 | 1 µs | 4 µs |
| CLOCK_REALTIME | FreeBSD 8.2 | 11 ns | 6 µs |
| CLOCK_REALTIME | OpenBSD 5.0 | 10 ms | 5 µs |
| CLOCK_REALTIME_COARSE | Linux 3.3 | 1 ms | 1 ms |
| CLOCK_REALTIME_COARSE | Linux 3.0 | 4 ms | 4 ms |
| GetSystemTimeAsFileTime() | Windows Seven | 16 ms | 1 ms |
| ftime() | Windows Seven | - | 1 ms |
“OS 해상도”는 운영 체제가 알려 주는 해상도입니다. “Python 해상도”는 clock_resolution.py 프로그램을 사용하여 Python에서 계산한 값으로, 시간 함수를 두 번 호출했을 때 나타나는 가장 작은 차이입니다.
Windows: GetSystemTimeAsFileTime
시스템 시간은 GetSystemTimeAsFileTime(), ftime(), time()을 사용하여 읽을 수 있습니다. 시스템 시간의 해상도는 GetSystemTimeAdjustment()를 사용하여 읽을 수 있습니다.
GetSystemTimeAsFileTime() 문서를 읽어 보십시오.
시스템 시간은 SetSystemTime()을 사용하여 설정할 수 있습니다.
UNIX의 시스템 시간
gettimeofday(), ftime(), time(), clock_gettime(CLOCK_REALTIME)은 시스템 시간을 반환합니다. CLOCK_REALTIME의 해상도는 clock_getres()를 사용하여 읽을 수 있습니다.
시스템 시간은 settimeofday()나 clock_settime(CLOCK_REALTIME)을 사용하여 설정할 수 있습니다.
Linux는 Linux 2.6.32부터 CLOCK_REALTIME_COARSE도 제공합니다. 이는 CLOCK_REALTIME과 비슷하지만, 덜 정밀한 대신 더 빠릅니다.
Alexander Shishkin은 시스템 시계가 변경될 때 Linux가 알림을 받을 수 있도록 하는 API를 제안했습니다: timerfd: add TFD_NOTIFY_CLOCK_SET to watch for clock changes (API 4번째 버전, 2011년 3월). 이 API는 아직 채택되지 않았지만, CLOCK_BOOTTIME이 비슷한 기능을 제공합니다.
프로세스 시간
프로세스 시간은 설정할 수 없습니다. 프로세스 시간은 단조적이지 않습니다: 프로세스가 유휴 상태인 동안에는 시계가 멈춥니다.
| 이름 | C 해상도 | 슬립 포함 | 일시 중단 포함 |
|---|---|---|---|
| GetProcessTimes() | 100 ns | 아니요 | 아니요 |
| CLOCK_PROCESS_CPUTIME_ID | 1 ns | 아니요 | 아니요 |
| getrusage(RUSAGE_SELF) | 1 µs | 아니요 | 아니요 |
| times() | - | 아니요 | 아니요 |
| clock() | - | Windows에서는 예, 그 외에는 아니요 | 아니요 |
“C 해상도” 열은 기반 C 구조체의 해상도입니다.
x86_64에서의 클록 해상도 예시
| 이름 | 운영 체제 | OS 해상도 | Python 해결 |
|---|---|---|---|
| CLOCK_PROCESS_CPUTIME_ID | Linux 3.3 | 1 ns | 1 ns |
| CLOCK_PROF | FreeBSD 8.2 | 10 ms | 1 µs |
| getrusage(RUSAGE_SELF) | FreeBSD 8.2 | - | 1 µs |
| getrusage(RUSAGE_SELF) | SunOS 5.11 | - | 1 µs |
| CLOCK_PROCESS_CPUTIME_ID | Linux 3.0 | 1 ns | 1 µs |
| getrusage(RUSAGE_SELF) | Mac OS 10.6 | - | 5 µs |
| clock() | Mac OS 10.6 | 1 µs | 5 µs |
| CLOCK_PROF | OpenBSD 5.0 | - | 5 µs |
| getrusage(RUSAGE_SELF) | Linux 3.0 | - | 4 ms |
| getrusage(RUSAGE_SELF) | OpenBSD 5.0 | - | 8 ms |
| clock() | FreeBSD 8.2 | 8 ms | 8 ms |
| clock() | Linux 3.0 | 1 µs | 10 ms |
| times() | Linux 3.0 | 10 ms | 10 ms |
| clock() | OpenBSD 5.0 | 10 ms | 10 ms |
| times() | OpenBSD 5.0 | 10 ms | 10 ms |
| times() | Mac OS 10.6 | 10 ms | 10 ms |
| clock() | SunOS 5.11 | 1 µs | 10 ms |
| times() | SunOS 5.11 | 1 µs | 10 ms |
| GetProcessTimes() | Windows Seven | 16 ms | 16 ms |
| clock() | Windows Seven | 1 ms | 1 ms |
“OS 해상도”는 운영 체제가 알려 주는 해상도입니다. “Python 해상도”는 clock_resolution.py 프로그램을 사용하여 Python에서 계산한, 시간 함수를 두 번 호출했을 때 나타나는 가장 작은 차이입니다.
함수
- Windows: GetProcessTimes(). 해상도는 GetSystemTimeAdjustment()를 사용하여 읽을 수 있습니다.
- clock_gettime(CLOCK_PROCESS_CPUTIME_ID): CPU에서 제공하는 프로세스별 고해상도 타이머입니다. 해상도는 clock_getres()를 사용하여 읽을 수 있습니다.
- clock(). 해상도는 1 / CLOCKS_PER_SEC입니다.
- Windows: 프로세스 시작 이후 경과한 벽시계 시간(초 단위 경과 시간에 CLOCKS_PER_SEC를 곱한 값)입니다. sleep 중에 경과한 시간도 포함합니다. 실패할 수 있습니다.
- UNIX: 프로그램이 사용한 프로세서 시간의 근삿값을 반환합니다.
- getrusage(RUSAGE_SELF)는 현재 프로세스의 자원 사용량 구조체를 반환합니다. ru_utime은 사용자 CPU 시간이고 ru_stime은 시스템 CPU 시간입니다.
- times(): 프로세스 시간 구조체입니다. 해상도는 1 / ticks_per_seconds이며, 여기서 ticks_per_seconds는 sysconf(_SC_CLK_TCK) 또는 HZ 상수입니다.
파이썬 소스 코드에는 프로세스 시간(CPU 시간)을 얻기 위한 이식 가능한 라이브러리인 Tools/pybench/systimes.py가 포함되어 있습니다.
QueryProcessCycleTime() 함수 (모든 스레드의 사이클 시간 합계)와 clock_getcpuclockid()도 참고하십시오.
스레드 시간
스레드 시간은 설정할 수 없습니다. 단조적이지 않습니다: 스레드가 유휴 상태인 동안에는 시계가 멈춥니다.
| 이름 | C 해상도 | 슬립 포함 | 일시 중단 포함 |
|---|---|---|---|
| CLOCK_THREAD_CPUTIME_ID | 1 ns | 예 | 에포크 변경 |
| GetThreadTimes() | 100 ns | 아니요 | ? |
“C 해상도” 열은 기반이 되는 C 구조체의 해상도입니다.
x86_64에서의 클록 해상도 예시
| 이름 | 운영 체제 | OS 해상도 | Python 해결 |
|---|---|---|---|
| CLOCK_THREAD_CPUTIME_ID | FreeBSD 8.2 | 1 µs | 1 µs |
| CLOCK_THREAD_CPUTIME_ID | Linux 3.3 | 1 ns | 649 ns |
| GetThreadTimes() | Windows Seven | 16 ms | 16 ms |
“OS 해상도”는 운영 체제가 알려 주는 해상도입니다. “Python 해상도”는 clock_resolution.py 프로그램을 사용해 Python에서 계산한, 시간 함수를 두 번 호출했을 때 나오는 가장 작은 차이입니다.
함수
- Windows: GetThreadTimes(). 해상도는 GetSystemTimeAdjustment()를 사용해 읽을 수 있습니다.
- clock_gettime(CLOCK_THREAD_CPUTIME_ID): 스레드별 CPU 시간 시계입니다. 초 단위가 아니라 CPU 사이클 수를 사용합니다. 해상도는 clock_getres()를 사용해 읽을 수 있습니다.
QueryThreadCycleTime() 함수 (지정한 스레드의 사이클 시간)와 pthread_getcpuclockid()도 참고하십시오.
Windows: QueryUnbiasedInterruptTime
편향된 인터럽트 시간과 현재 절전 편향량으로부터 현재의 편향되지 않은 인터럽트 시간을 가져옵니다. 이 시간은 전원 관리의 절전 전환에 영향을 받지 않습니다.
QueryUnbiasedInterruptTime 함수로 얻은 경과 시간에는 시스템이 작동 상태에 있었던 시간만 포함됩니다. QueryUnbiasedInterruptTime()은 단조적이지 않습니다.
QueryUnbiasedInterruptTime()은 Windows 7에서 도입되었습니다.
QueryIdleProcessorCycleTime() 함수 (각 프로세서의 유휴 스레드에 대한 사이클 시간)도 참고하십시오.
슬립
주어진 초 동안 프로세스의 실행을 일시 중단합니다. 슬립은 시스템 시간 갱신에 영향을 받지 않습니다. 슬립은 시스템이 일시 중단된 동안에는 멈춥니다. 예를 들어 프로세스가 60초 동안 슬립하는 도중에 시스템이 30초 동안 일시 중단되면, 실제 시간으로 슬립 지속 시간은 90초가 됩니다.
Sleep은 시그널에 의해 중단될 수 있습니다: 이 경우 함수는 EINTR로 실패합니다.
| 이름 | C 해상도 |
|---|---|
| nanosleep() | 1 ns |
| clock_nanosleep() | 1 ns |
| usleep() | 1 µs |
| delay() | 1 µs |
| sleep() | 1초 |
기타 함수:
| 이름 | C 해상도 |
|---|---|
| sigtimedwait() | 1 ns |
| pthread_cond_timedwait() | 1 ns |
| sem_timedwait() | 1 ns |
| select() | 1 µs |
| epoll() | 1 ms |
| poll() | 1 ms |
| WaitForSingleObject() | 1 ms |
“C 해상도” 열은 기반이 되는 C 구조체의 해상도입니다.
함수
- sleep(seconds)
- usleep(microseconds)
- nanosleep(나노초, 남은 시간): nanosleep()의 Linux 매뉴얼 페이지
- delay(milliseconds)
clock_nanosleep
clock_nanosleep(clock_id, 플래그, 나노초, 남은 시간): clock_nanosleep()의 Linux 매뉴얼 페이지.
flags 플래그가 TIMER_ABSTIME이면, request는 clock_id 시계로 측정한 절대 시간으로 해석됩니다. request가 클록의 현재 값보다 작거나 같으면 clock_nanosleep()은 호출한 스레드를 일시 중단하지 않고 즉시 반환합니다.
POSIX.1은 clock_settime(2)을 통해 CLOCK_REALTIME 클록의 값을 변경하더라도 상대 clock_nanosleep()에서 블록된 스레드에는 아무런 영향을 주지 않아야 한다고 규정합니다.
select()
select(nfds, readfds, writefds, exceptfs, 타임아웃).
Linux 2.6.28부터 select()는 타임아웃을 처리하는 데 고해상도 타이머를 사용합니다. 프로세스에는 타임아웃의 정밀도를 설정하는 “slack” 속성이 있으며, 기본 slack은 50마이크로초입니다. Linux 2.6.28 이전에는 select()의 타임아웃을 주 타이밍 서브시스템이 jiffy 수준의 해상도로 처리했습니다. High- (but not too high-) resolution timeouts와 Timer slack도 읽어 보십시오.
기타 함수
- poll(), epoll()
- sigtimedwait(). POSIX: “Monotonic Clock 옵션이 지원되는 경우, 타임아웃 인자로 지정된 시간 간격을 측정하는 데 CLOCK_MONOTONIC 시계를 사용해야 합니다.”
- pthread_cond_timedwait(), pthread_condattr_setclock(). “clock 속성의 기본값은 시스템 시간을 가리켜야 합니다.”
- sem_timedwait(): “Timers 옵션이 지원되면 타임아웃은 CLOCK_REALTIME 클록을 기준으로 해야 합니다. Timers 옵션이 지원되지 않으면 타임아웃은 time() 함수가 반환하는 시스템 시간을 기준으로 해야 합니다. 타임아웃의 정밀도는 그 기준이 되는 클록의 정밀도여야 합니다.”
- WaitForSingleObject(): GetTickCount()와 같은 타이머를 같은 정밀도로 사용합니다.
시스템 대기 모드
ACPI 전원 상태 “S3”은 시스템 대기 모드이며, “Suspend to RAM”이라고도 합니다. RAM에는 전원이 계속 공급됩니다.
Windows에서는 전원 관리 이벤트(예: 소유자 상태 변경)를 알리기 위해 Windows 애플리케이션에 WM_POWERBROADCAST 메시지를 보냅니다.
Mac OS X의 경우 Registering and unregistering for sleep and wake notifications (Technical Q&A QA1340)를 읽어 보십시오.
각주
링크
관련 Python 이슈:
- 이슈 #12822: NewGIL은 가능하면 CLOCK_MONOTONIC을 사용해야 합니다.
- 이슈 #14222: 타임아웃 구현에 time.steady() 사용
- 이슈 #14309: time.clock() 폐지
- 이슈 #14397: 단조 시계에 QueryPerformanceCounter 대신 GetTickCount/GetTickCount64 사용
- 이슈 #14428: PEP 418 구현
- 이슈 #14555: clock_gettime/settime/getres: 시계 식별자 추가
단조 시계를 제공하는 라이브러리:
- Java: System.nanoTime
- Qt 라이브러리: QElapsedTimer
- glib 라이브러리: g_get_monotonic_time ()은 Windows에서는 GetTickCount64()/GetTickCount()를, UNIX에서는 clock_gettime(CLOCK_MONOTONIC)을 사용하며, 그렇지 않으면 시스템 시계로 대체합니다.
- python-monotonic-time (github)
- Monoclock.nano_count()는 clock_gettime(CLOCK_MONOTONIC)을 사용하며 나노초 단위의 수를 반환합니다.
- Thomas Habets의 monotonic_clock
- Perl: Time::HiRes는 clock_gettime(CLOCK_MONOTONIC)을 제공합니다.
- Ruby: AbsoluteTime.now: clock_gettime(CLOCK_MONOTONIC), mach_absolute_time() 또는 gettimeofday()를 사용합니다. “AbsoluteTime.monotonic?” 메서드는 AbsoluteTime.now가 단조적인지 여부를 나타냅니다.
- libpthread: Windows용 POSIX 스레드 라이브러리 (clock.c)
- Boost.Chrono는 다음을 사용합니다:
- system_clock:
- mac = gettimeofday()
- posix = clock_gettime(CLOCK_REALTIME)
- win = GetSystemTimeAsFileTime()
- steady_clock:
- mac = mach_absolute_time()
- posix = clock_gettime(CLOCK_MONOTONIC)
- win = QueryPerformanceCounter()
- high_resolution_clock:
- 사용 가능하면 steady_clock, 그렇지 않으면 system_clock
- system_clock:
시간:
- Twisted 이슈 #2424: 단조 시계로 시작하는 reactor 옵션 추가
- Thomas Habets의 시간 측정에 gettimeofday()를 절대 사용해서는 안 됩니다 (2010-09-05)
- hrtimers - 고해상도 커널 타이머를 위한 서브시스템
- C++ 타임아웃 사양, Lawrence Crowl 작성 (2010-08-19)
- Windows: 게임 타이밍과 멀티코어 프로세서, Chuck Walbourn 작성 (2005년 12월)
- Windows용으로 지속적으로 갱신되는 고해상도 시간 공급자 구현하기, Johan Nilsson 작성 (2004년 3월)
- clockspeed는 하드웨어 틱 카운터로 계속 빠르거나 느린 시스템 시간을 보정합니다. D. J. Bernstein 작성 (1998년)
- 시스템 시간 조회는 하드웨어 시계와 시간 함수를 해상도, 에포크 또는 범위와 함께 나열합니다.
- Windows에서 Firefox의 JavaScript 런타임은 더 높은 해상도를 얻기 위해 GetSystemTimeAsFileTime()을 QueryPerformanceCounter()로 보간합니다. Bug 363258 - Windows에서 (new Date).getTime() / Date.now()의 밀리초 해상도가 낮음을 참조하십시오.
- 마이크로초가 중요할 때: IBM 고해상도 타임스탬프 기능이 아주 짧은 시간을 정확하게 측정하는 방법. W. Nathaniel Mills, III 작성 (2002년 4월)
- Win32 성능 측정 옵션, Matthew Wilson 작성 (2003년 5월)
- 피드포워드 기반 동기화를 위한 카운터의 가용성과 특성, Timothy Broomhead, Julien Ridoux, Darryl Veitch 작성 (2009년)
- 시스템 관리 인터럽트(SMI) 문제:
- 시스템 관리 인터럽트가 없는 하드웨어, Keith Mannthey 작성 (2009년)
- IBM 실시간 “SMI Free” 모드 드라이버, Keith Mannthey 작성 (2009년 2월)
- Ubuntu에서 SMI로 인해 발생하는 실시간 문제 해결하기
- [RFC] 간단한 SMI 탐지기, Jon Masters 작성 (2009년 1월)
- [PATCH 2.6.34-rc3] x86용 비간섭 SMI 스니퍼, Joe Korty 작성 (2010-04)
수락
이 PEP는 2012-04-28에 Guido van Rossum이 승인했습니다 [1]. 이후 PEP 구현이 저장소에 커밋되었습니다.
참고 문헌
Copyright
This document has been placed in the public domain.