development

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

big-blog 2020. 7. 19. 00:08
반응형

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


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 시계에서 시간을 반환합니다. 반환 된 시간은 에포크 (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);
}

휴대용 솔루션 구현

여기에서 이미 언급했듯이 시간 측정 문제에 대해 충분한 정밀도를 가진 적절한 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 는 시스템이 시작된 이후 경과 된 시간 (밀리 초)을 제공합니다. 49.7 일마다 랩핑되므로 더 긴 간격을 측정 할 때주의하십시오.
  • GetTickCount64 는의 64 비트 버전 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을 어떻게 사용합니까?를 읽어보십시오 . .

리눅스와 유닉스

clock_gettime전화는 POSIX 친화적 인 시스템에 대한 최선의 방법입니다. 다른 클럭 소스에서 시간을 쿼리 할 수 ​​있으며 필요한 것은 CLOCK_MONOTONIC입니다. clock_gettime지원 하는 모든 시스템을 지원 CLOCK_MONOTONIC하는 것은 아니므로 가장 먼저 수행해야 할 작업은 가용성을 확인하는 것입니다.

  • 만약 _POSIX_MONOTONIC_CLOCK값으로 정의되고 >= 0그 뜻 CLOCK_MONOTONICavaiable이다이고;
  • 경우 _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

IRIXclock_gettime전화를했지만 부족 CLOCK_MONOTONIC합니다. 대신 그것으로 자신의 단조 클럭 소스 정의가 CLOCK_SGI_CYCLE당신이 대신 사용해야하는 CLOCK_MONOTONICclock_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없지만 gethrtimeSolaris와 동일한 방식으로 사용해야 하는 기능 지원합니다 .

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

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 C11에서

구현 해상도로 반올림하여 최대 나노초를 반환합니다.

Looks like an ANSI ripoff from POSIX' clock_gettime.

Example: a printf is done every 100ms on Ubuntu 15.10:

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

The C11 N1570 standard draft 7.27.2.5 "The timespec_get function says":

If base is TIME_UTC, the tv_sec member is set to the number of seconds since an implementation defined epoch, truncated to a whole value and the tv_nsec member is set to the integral number of nanoseconds, rounded to the resolution of the system clock. (321)

321) Although a struct timespec object describes times with nanosecond resolution, the available resolution is system dependent and may even be greater than 1 second.

C++11 also got std::chrono::high_resolution_clock: C++ Cross-Platform High-Resolution Timer

glibc 2.21 implementation

Can be found under sysdeps/posix/timespec_get.c as:

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

so clearly:

  • only TIME_UTC is currently supported

  • it forwards to __clock_gettime (CLOCK_REALTIME, ts), which is a POSIX API: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html

    Linux x86-64 has a clock_gettime system call.

    Note that this is not a fail-proof micro-benchmarking method because:

    • man clock_gettime says that this measure may have discontinuities if you change some system time setting while your program runs. This should be a rare event of course, and you might be able to ignore it.

    • this measures wall time, so if the scheduler decides to forget about your task, it will appear to run for longer.

    For those reasons getrusage() might be a better better POSIX benchmarking tool, despite it's lower microsecond maximum precision.

    More information at: Measure time in Linux - time vs clock vs getrusage vs clock_gettime vs gettimeofday vs timespec_get?


The best precision you can possibly get is through the use of the x86-only "rdtsc" instruction, which can provide clock-level resolution (ne must of course take into account the cost of the rdtsc call itself, which can be measured easily on application startup).

The main catch here is measuring the number of clocks per second, which shouldn't be too hard.


The accepted answer is good enough.But my solution is more simple.I just test in Linux, use gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.

Alse use gettimeofday, the tv_sec is the part of second, and the tv_usec is microseconds, not milliseconds.

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

It print:

1522139691342 1522139692342, exactly a second.


Under windows:

SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);

참고URL : https://stackoverflow.com/questions/361363/how-to-measure-time-in-milliseconds-using-ansi-c

반응형