ANSI C 만 사용하여 밀리 초 이상의 정밀도로 시간을 측정 할 수있는 방법이 있습니까? time.h를 검색했지만 두 번째 정밀도 함수 만 찾았습니다.
ANSI C 만 사용하여 밀리 초 이상의 정밀도로 시간을 측정 할 수있는 방법이 있습니까? time.h를 검색했지만 두 번째 정밀도 함수 만 찾았습니다.
답변:
1 초 이상의 시간 분해능을 제공하는 ANSI C 기능은 없지만 POSIX 기능 gettimeofday
은 마이크로 초 분해능을 제공합니다. 시계 기능은 프로세스가 실행하는 데 소요 된 시간만을 측정하며 많은 시스템에서 정확하지 않습니다.
이 기능을 다음과 같이 사용할 수 있습니다.
struct timeval tval_before, tval_after, tval_result;
gettimeofday(&tval_before, NULL);
// Some code you want to time, for example:
sleep(1);
gettimeofday(&tval_after, NULL);
timersub(&tval_after, &tval_before, &tval_result);
printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);
이것은 Time elapsed: 1.000870
내 컴퓨터에 반환 됩니다.
timeval::tv_usec
항상 1 초 미만이며 반복 된다는 점 은 주목할 가치가 있습니다. 즉 1 초보다 큰 시간 차이를 이용하려면, 다음을 수행해야합니다long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
timersub
함수 내부에 캡슐화 됩니다. tval_result
값 (tv_sec 및 tv_usec)을있는 그대로 사용할 수 있습니다 .
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
CLOCKS_PER_SEC / 1000
이 정확하지 않아 최종 결과에 영향을 미칠 수 있습니다 (내 경험상 CLOCKS_PER_SEC
항상 1000의 배수 였음에도 불구하고 ). 수행 (1000 * clock()) / CLOCKS_PER_SEC
은 부정확 한 분할에 덜 취약하지만 반면에 오버플로에 더 취약합니다. 고려해야 할 몇 가지 문제입니다.
나는 항상 clock_gettime () 함수를 사용하여 CLOCK_MONOTONIC 시계에서 시간을 반환합니다. 반환 된 시간은 epoch의 시스템 시작과 같이 과거에 일부 지정되지 않은 시점 이후의 시간 (초 및 나노초)입니다.
#include <stdio.h>
#include <stdint.h>
#include <time.h>
int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}
int main(int argc, char **argv)
{
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// Some code I am interested in measuring
clock_gettime(CLOCK_MONOTONIC, &end);
uint64_t timeElapsed = timespecDiff(&end, &start);
}
clock_gettime(CLOCK_MONOTONIC, ...)
기능 테스트 매크로도 _POSIX_MONOTONIC_CLOCK
있습니다.
휴대용 솔루션 구현
시간 측정 문제에 대해 충분한 정밀도를 가진 적절한 ANSI 솔루션이 없다고 여기에서 이미 언급했듯이, 휴대용 및 가능하면 고해상도 시간 측정 솔루션을 얻는 방법에 대해 작성하고 싶습니다.
단조로운 시계 대 타임 스탬프
일반적으로 시간 측정에는 두 가지 방법이 있습니다.
첫 번째는 미리 정의 된 주파수로 틱을 계산하는 단조 시계 카운터 (틱 카운터라고도 함)를 사용하므로 틱 값이 있고 주파수를 알고 있다면 틱을 경과 시간으로 쉽게 변환 할 수 있습니다. 실제로 단조로운 시계가 어떤 식 으로든 현재 시스템 시간을 반영한다는 보장은 없으며 시스템 시작 이후 틱을 계산할 수도 있습니다. 그러나 시스템 상태에 관계없이 시계가 항상 증가하는 방식으로 실행되도록 보장합니다. 일반적으로 주파수는 하드웨어 고해상도 소스에 바인딩되어 있기 때문에 높은 정확도를 제공합니다 (하드웨어에 따라 다르지만 대부분의 최신 하드웨어는 고해상도 클럭 소스에 문제가 없습니다).
두 번째 방법은 현재 시스템 시계 값을 기반으로 (날짜) 시간 값을 제공합니다. 해상도가 높을 수도 있지만 한 가지 큰 단점이 있습니다. 이러한 종류의 시간 값은 시간대 변경, 일광 절약 시간 (DST) 변경, NTP 서버 업데이트, 시스템 최대 절전 모드 등과 같은 다른 시스템 시간 조정의 영향을받을 수 있습니다. 의 위에. 경우에 따라 음의 경과 시간 값을 가져와 정의되지 않은 동작을 유발할 수 있습니다. 실제로 이러한 종류의 시간 원본은 첫 번째 시간 원본보다 신뢰성이 떨어집니다.
따라서 시간 간격 측정의 첫 번째 규칙은 가능하면 단조로운 시계를 사용하는 것입니다. 일반적으로 정밀도가 높고 설계 상 신뢰할 수 있습니다.
대체 전략
휴대용 솔루션을 구현할 때 폴백 전략을 고려할 가치가 있습니다. 가능한 경우 단조로운 클록을 사용하고 시스템에 단조로운 클록이없는 경우 타임 스탬프 방식으로 폴백합니다.
윈도우
소프트웨어 및 하드웨어 지원에 대해 알아야 할 모든 세부 정보를 설명하는 Windows의 시간 측정에 대한 MSDN의 고해상도 타임 스탬프 획득 이라는 훌륭한 기사가 있습니다 . Windows에서 고정밀 타임 스탬프를 얻으려면 다음을 수행해야합니다.
QueryPerformanceFrequency를 사용하여 타이머 빈도 (초당 틱)를 쿼리합니다 .
LARGE_INTEGER tcounter;
LARGE_INTEGER freq;
if (QueryPerformanceFrequency (&tcounter) != 0)
freq = tcounter.QuadPart;
타이머 빈도는 시스템 부팅시 고정되므로 한 번만 가져 오면됩니다.
QueryPerformanceCounter를 사용하여 현재 틱 값을 쿼리합니다 .
LARGE_INTEGER tcounter;
LARGE_INTEGER tick_value;
if (QueryPerformanceCounter (&tcounter) != 0)
tick_value = tcounter.QuadPart;
경과 시간, 즉 마이크로 초로 눈금을 조정합니다.
LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
Microsoft에 따르면 대부분의 경우 Windows XP 이상 버전에서이 방법에 문제가 없어야합니다. 그러나 Windows에서 두 가지 대체 솔루션을 사용할 수도 있습니다.
GetTickCount
이지만 Windows Vista 이상부터 사용할 수 있습니다.OS X (macOS)
OS X (macOS)에는 단조 시계를 나타내는 자체 마하 절대 시간 단위가 있습니다. 시작하는 가장 좋은 방법은 Apple의 기사 Technical Q & A QA1398 : Mach Absolute Time Units (코드 예제와 함께)에서 Mach 특정 API를 사용하여 단조로운 틱을 얻는 방법을 설명합니다. 또한 카운터 주파수가 분자와 분모의 형태로 사용되기 때문에 Mac OS X 에서는 clock_gettime 대안 이라는 로컬 질문 이 있습니다. 따라서 경과 시간을 얻는 방법에 대한 간단한 예 :
클럭 주파수 분자와 분모를 얻으십시오.
#include <mach/mach_time.h>
#include <stdint.h>
static uint64_t freq_num = 0;
static uint64_t freq_denom = 0;
void init_clock_frequency ()
{
mach_timebase_info_data_t tb;
if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) {
freq_num = (uint64_t) tb.numer;
freq_denom = (uint64_t) tb.denom;
}
}
한 번만 수행하면됩니다.
다음을 사용하여 현재 눈금 값을 쿼리합니다 mach_absolute_time
.
uint64_t tick_value = mach_absolute_time ();
이전에 쿼리 한 분자와 분모를 사용하여 틱을 경과 시간, 즉 마이크로 초로 조정합니다.
uint64_t value_diff = tick_value - prev_tick_value;
/* To prevent overflow */
value_diff /= 1000;
value_diff *= freq_num;
value_diff /= freq_denom;
오버플로를 방지하는 주요 아이디어는 분자와 분모를 사용하기 전에 눈금을 원하는 정확도로 축소하는 것입니다. 초기 타이머 분해능은 나노초이므로 1000
마이크로 초를 얻기 위해로 나눕니다 . Chromium의 time_mac.c 에서 사용 된 것과 동일한 접근 방식을 찾을 수 있습니다 . 나노초 정확도가 정말로 필요한 경우 오버플로없이 mach_absolute_time을 어떻게 사용할 수 있습니까? .
Linux 및 UNIX
이 clock_gettime
통화는 POSIX 친화적 인 시스템에서 가장 좋은 방법입니다. 다른 클럭 소스에서 시간을 쿼리 할 수 있으며 필요한 것은 CLOCK_MONOTONIC
. 모든 시스템이 clock_gettime
지원 CLOCK_MONOTONIC
하는 것은 아니므로 가장 먼저해야 할 일은 가용성을 확인하는 것입니다.
_POSIX_MONOTONIC_CLOCK
값으로 정의 되면 사용 가능함을 >= 0
의미합니다 CLOCK_MONOTONIC
.경우 _POSIX_MONOTONIC_CLOCK
에 정의 0
가 런타임시 작동하는 경우 추가로 확인해야한다는 것을 의미, 내가 사용 제안 sysconf
:
#include <unistd.h>
#ifdef _SC_MONOTONIC_CLOCK
if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
/* A monotonic clock presents */
}
#endif
의 사용법 clock_gettime
은 매우 간단합니다.
시간 가치를 얻으십시오 :
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
uint64_t get_posix_clock_time ()
{
struct timespec ts;
if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000);
else
return 0;
}
여기서 시간을 마이크로 초로 줄였습니다.
같은 방법으로받은 이전 시간 값과의 차이를 계산합니다.
uint64_t prev_time_value, time_value;
uint64_t time_diff;
/* Initial time */
prev_time_value = get_posix_clock_time ();
/* Do some work here */
/* Final time */
time_value = get_posix_clock_time ();
/* Time difference */
time_diff = time_value - prev_time_value;
최선의 대체 전략은 gettimeofday
호출 을 사용하는 것입니다. 단조롭지는 않지만 상당히 좋은 해상도를 제공합니다. 아이디어는와 동일 clock_gettime
하지만 시간 값을 얻으려면 다음을 수행해야합니다.
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
uint64_t get_gtod_clock_time ()
{
struct timeval tv;
if (gettimeofday (&tv, NULL) == 0)
return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec);
else
return 0;
}
다시 말하지만, 시간 값은 마이크로 초로 축소됩니다.
SGI IRIX
IRIX 에 clock_gettime
전화가 있지만 CLOCK_MONOTONIC
. 대신 with CLOCK_SGI_CYCLE
대신 사용해야하는 고유 한 단조로운 클럭 소스가 정의되어 있습니다 .CLOCK_MONOTONIC
clock_gettime
Solaris 및 HP-UX
Solaris에는 gethrtime
현재 타이머 값을 나노초 단위로 반환하는 자체 고해상도 타이머 인터페이스 가 있습니다. 최신 버전의 Solaris에는이있을 수 있지만 이전 Solaris 버전을 지원해야하는 경우 계속 clock_gettime
사용할 수 있습니다 gethrtime
.
사용법은 간단합니다.
#include <sys/time.h>
void time_measure_example ()
{
hrtime_t prev_time_value, time_value;
hrtime_t time_diff;
/* Initial time */
prev_time_value = gethrtime ();
/* Do some work here */
/* Final time */
time_value = gethrtime ();
/* Time difference */
time_diff = time_value - prev_time_value;
}
HP-UX에는는 clock_gettime
없지만 gethrtime
Solaris에서와 같은 방식으로 사용해야하는 것을 지원합니다 .
BeOS
BeOSsystem_time
에는 컴퓨터가 부팅 된 이후 경과 한 마이크로 초 수를 반환하는 자체 고해상도 타이머 인터페이스도 있습니다.
사용 예 :
#include <kernel/OS.h>
void time_measure_example ()
{
bigtime_t prev_time_value, time_value;
bigtime_t time_diff;
/* Initial time */
prev_time_value = system_time ();
/* Do some work here */
/* Final time */
time_value = system_time ();
/* Time difference */
time_diff = time_value - prev_time_value;
}
OS / 2
OS / 2 에는 고정밀 타임 스탬프를 검색하는 자체 API가 있습니다.
DosTmrQueryFreq
(GCC 컴파일러의 경우)를 사용하여 타이머 주파수 (단위당 틱)를 쿼리합니다 .
#define INCL_DOSPROFILE
#define INCL_DOSERRORS
#include <os2.h>
#include <stdint.h>
ULONG freq;
DosTmrQueryFreq (&freq);
다음을 사용하여 현재 틱 값을 쿼리합니다 DosTmrQueryTime
.
QWORD tcounter;
unit64_t time_low;
unit64_t time_high;
unit64_t timestamp;
if (DosTmrQueryTime (&tcounter) == NO_ERROR) {
time_low = (unit64_t) tcounter.ulLo;
time_high = (unit64_t) tcounter.ulHi;
timestamp = (time_high << 32) | time_low;
}
경과 시간, 즉 마이크로 초로 눈금을 조정합니다.
uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
구현 예
위에서 설명한 모든 전략을 구현 하는 plibsys 라이브러리를 살펴볼 수 있습니다 (자세한 내용은 ptimeprofiler * .c 참조).
timespec_get
. stackoverflow.com/a/36095407/895245
timespec_get
단조롭지 않습니다.
timespec_get
C11에서
구현 해상도로 반올림하여 최대 나노초를 반환합니다.
POSIX '에서 ANSI ripoff처럼 보입니다 clock_gettime
.
예 : a printf
는 Ubuntu 15.10에서 100ms마다 수행됩니다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static long get_nanos(void) {
struct timespec ts;
timespec_get(&ts, TIME_UTC);
return (long)ts.tv_sec * 1000000000L + ts.tv_nsec;
}
int main(void) {
long nanos;
long last_nanos;
long start;
nanos = get_nanos();
last_nanos = nanos;
start = nanos;
while (1) {
nanos = get_nanos();
if (nanos - last_nanos > 100000000L) {
printf("current nanos: %ld\n", nanos - start);
last_nanos = nanos;
}
}
return EXIT_SUCCESS;
}
C11 N1570 표준 초안 7.27.2.5는 "timespec_get 기능을 말한다"
base가 TIME_UTC 인 경우 tv_sec 멤버는 구현이 정의 된 epoch 이후의 시간 (초)으로 설정되고 전체 값으로 잘리고 tv_nsec 멤버는 시스템 클록의 해상도로 반올림 된 정수 나노초로 설정됩니다. (321)
321) 구조체 timespec 객체가 나노초 해상도로 시간을 설명하지만 사용 가능한 해상도는 시스템에 따라 다르며 1 초보다 클 수도 있습니다.
C ++ 11에 추가 된 기능 std::chrono::high_resolution_clock
: C ++ 크로스 플랫폼 고해상도 타이머
glibc 2.21 구현
다음에서 찾을 수 있습니다 sysdeps/posix/timespec_get.c
.
int
timespec_get (struct timespec *ts, int base)
{
switch (base)
{
case TIME_UTC:
if (__clock_gettime (CLOCK_REALTIME, ts) < 0)
return 0;
break;
default:
return 0;
}
return base;
}
너무 명확하게 :
TIME_UTC
현재 만 지원됩니다.
__clock_gettime (CLOCK_REALTIME, ts)
POSIX API 인으로 전달됩니다 . http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
Linux x86-64에는 clock_gettime
시스템 호출이 있습니다.
이는 다음과 같은 이유로 오류 방지 마이크로 벤치마킹 방법이 아닙니다.
man clock_gettime
프로그램이 실행되는 동안 일부 시스템 시간 설정을 변경하면이 측정이 불연속 될 수 있음을 나타냅니다. 물론 이것은 드문 이벤트이며 무시할 수 있습니다.
이것은 벽 시간을 측정하므로 스케줄러가 작업을 잊어 버리기로 결정하면 더 오래 실행되는 것처럼 보입니다.
이러한 이유 때문에 getrusage()
더 나은 POSIX 벤치마킹 도구가 될 수 있습니다.
자세한 정보 : Linux에서 시간 측정-시간 대 시계 대 getrusage 대 clock_gettime 대 gettimeofday 대 timespec_get?
얻을 수있는 최고의 정밀도는 x86 전용 "rdtsc"명령어를 사용하는 것입니다.이 명령어는 클럭 수준 해상도를 제공 할 수 있습니다 (ne은 물론 쉽게 측정 할 수있는 rdtsc 호출 자체의 비용을 고려해야합니다. 응용 프로그램 시작).
여기서 가장 중요한 점은 초당 클럭 수를 측정하는 것인데 너무 어렵지 않아야합니다.
대답은 충분하지만 내 솔루션은 더 간단합니다. Linux에서 테스트하고 gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0을 사용합니다.
합니다 alse 사용 gettimeofday
은이 tv_sec
두 번째 부분이고,는 tv_usec
인 마이크로 하지 밀리 .
long currentTimeMillis() {
struct timeval time;
gettimeofday(&time, NULL);
return time.tv_sec * 1000 + time.tv_usec / 1000;
}
int main() {
printf("%ld\n", currentTimeMillis());
// wait 1 second
sleep(1);
printf("%ld\n", currentTimeMillis());
return 0;
}
인쇄 :
1522139691342
1522139692342
, 정확히 1 초.