ANSI C를 사용하여 시간을 밀리초 단위로 측정하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/361363

  •  21-08-2019
  •  | 
  •  

문제

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 내 기계에.

다른 팁

#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);

나는 항상 clock_gettime () 함수를 사용하여 clock_monotonic clock에서 시간을 반환합니다. 반환 된 시간은 과거의 시스템 시작과 같이 과거의 일부가 지정되지 않은 시점이기 때문에 몇 초와 나노초의 시간입니다.

#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);
}

휴대용 솔루션 구현

시간 측정 문제에 대해 충분한 정밀도를 갖춘 적절한 ANSI 솔루션이 없다는 점을 여기서 이미 언급했듯이 휴대용 및 가능하다면 고해상도 시간 측정 솔루션을 얻는 방법에 대해 쓰고 싶습니다.

단조로운 시계 vs.타임스탬프

일반적으로 시간 측정에는 두 가지 방법이 있습니다.

  • 단조시계;
  • 현재(날짜)타임 스탬프입니다.

첫 번째는 미리 정의된 빈도로 틱을 계산하는 단조 시계 카운터(틱 카운터라고도 함)를 사용하므로 틱 값이 있고 빈도가 알려진 경우 틱을 경과 시간으로 쉽게 변환할 수 있습니다.실제로 단조 시계가 어떤 방식으로든 현재 시스템 시간을 반영한다는 보장은 없으며 시스템 시작 이후의 틱을 계산할 수도 있습니다.그러나 이는 시스템 상태에 관계없이 시계가 항상 증가하는 방식으로 실행되도록 보장합니다.일반적으로 주파수는 하드웨어 고해상도 소스에 바인딩되므로 높은 정확도를 제공합니다(하드웨어에 따라 다르지만 대부분의 최신 하드웨어는 고해상도 클럭 소스에 문제가 없습니다).

두 번째 방법은 현재 시스템 시계 값을 기반으로 (날짜)시간 값을 제공합니다.해상도가 높을 수도 있지만 한 가지 큰 단점이 있습니다.이러한 종류의 시간 값은 다양한 시스템 시간 조정에 의해 영향을 받을 수 있습니다.시간대 변경, 일광 절약 시간(DST) 변경, NTP 서버 업데이트, 시스템 최대 절전 모드 등.어떤 상황에서는 정의되지 않은 동작으로 이어질 수 있는 음의 경과 시간 값을 얻을 수 있습니다.실제로 이런 종류의 시간 소스는 첫 번째 것보다 신뢰성이 떨어집니다.

따라서 시간 간격 측정의 첫 번째 규칙은 가능하면 단조 시계를 사용하는 것입니다.일반적으로 정밀도가 높으며 설계상 신뢰성이 높습니다.

대체 전략

이식 가능한 솔루션을 구현할 때 대체 전략을 고려해 보는 것이 좋습니다.가능한 경우 단조 시계를 사용하고 시스템에 단조 시계가 없으면 타임스탬프 접근 방식으로 폴백합니다.

윈도우

이라는 훌륭한 기사가 있습니다. 고해상도 타임스탬프 획득 MSDN에서는 소프트웨어 및 하드웨어 지원에 대해 알아야 할 모든 세부 정보를 설명하는 Windows의 시간 측정에 대해 설명합니다.Windows에서 고정밀 타임스탬프를 얻으려면 다음을 수행해야 합니다.

  • 타이머 빈도(초당 틱)를 쿼리합니다. 쿼리성능빈도:

    LARGE_INTEGER tcounter;
    LARGE_INTEGER freq;    
    
    if (QueryPerformanceFrequency (&tcounter) != 0)
        freq = tcounter.QuadPart;
    

    타이머 주파수는 시스템 부팅 시 고정되어 있으므로 한 번만 가져오면 됩니다.

  • 현재 틱 값을 쿼리합니다. 쿼리성능카운터:

    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 시스템이 시작된 이후 경과된 시간(밀리초)을 제공합니다.49.7일마다 한 번 포장되므로 더 긴 간격을 측정할 때는 주의하세요.
  • GetTickCount64 64비트 버전입니다. GetTickCount, 그러나 Windows Vista 이상부터 사용할 수 있습니다.

OS X(맥OS)

OS X(macOS)에는 단조 시계를 나타내는 자체 마하 절대 시간 단위가 있습니다.시작하는 가장 좋은 방법은 Apple의 기사입니다. 기술 Q&A QA1398:마하 절대 시간 단위 이는 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을 어떻게 사용할 수 있나요?.

리눅스와 유닉스

그만큼 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 아이릭스

아이릭스 가지고있다 clock_gettime 전화했는데 부족해요 CLOCK_MONOTONIC.대신 다음과 같이 정의된 자체 단조 클록 소스를 갖습니다. CLOCK_SGI_CYCLE 대신에 사용해야하는 CLOCK_MONOTONIC ~와 함께 clock_gettime.

솔라리스와 HP-UX

Solaris에는 자체 고해상도 타이머 인터페이스가 있습니다. gethrtime 현재 타이머 값을 나노초 단위로 반환합니다.최신 버전의 Solaris에는 clock_gettime, 당신은 충실 할 수 있습니다 gethrtime 이전 Solaris 버전을 지원해야 하는 경우.

사용법은 간단합니다:

#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

BeOS 자체 고해상도 타이머 인터페이스도 있습니다. system_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;
}

운영체제/2

운영체제/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);
    

구현 예

다음을 살펴보실 수 있습니다. 플립시스 위에서 설명한 모든 전략을 구현하는 라이브러리입니다(자세한 내용은 ptimeprofiler*.c 참조).

timespec_get C11에서

구현의 해상도로 반올림 된 나노 초까지 반환합니다.

Posix의 Ansi Ripoff처럼 보입니다. clock_gettime.

예 : a printf 우분투 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) Struct 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는 a clock_gettime 시스템 호출.

    이것은 실패한 마이크로 벤치 마킹 방법이 아닙니다.

    • man clock_gettime 프로그램이 실행되는 동안 시스템 시간 설정을 변경하면이 측정 값이 불연속성이있을 수 있다고 말합니다. 이것은 물론 드문 사건이어야하며, 당신은 그것을 무시할 수 있습니다.

    • 이로 인해 벽 시간이 측정되므로 스케줄러가 작업을 잊어 버리기로 결정하면 더 오래 실행되는 것으로 보입니다.

    그런 이유로 getrusage() 마이크로 초 최대 정밀도가 낮음에도 불구하고 더 나은 Posix 벤치마킹 도구 일 수 있습니다.

    추가 정보 : Linux에서 시간을 측정 - 시간 대 클럭 대 Getrusage vs Clock_GetTime vs gettimeofday vs 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 초.

Windows 아래 :

SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top