C ++를 사용하여 나노초 단위로 시간을 제공하는 타이머 기능


101

API가 값을 반환하는 데 걸린 시간을 계산하고 싶습니다. 그러한 행동에 걸리는 시간은 나노초의 공간입니다. API가 C ++ 클래스 / 함수이므로 timer.h를 사용하여 동일하게 계산합니다.

  #include <ctime>
  #include <cstdio>

  using namespace std;

  int main(int argc, char** argv) {

      clock_t start;
      double diff;
      start = clock();
      diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;
      cout<<"printf: "<< diff <<'\n';

      return 0;
  }

위의 코드는 시간을 초 단위로 제공합니다. 나노초 안에 더 정밀하게 동일한 결과를 얻으려면 어떻게해야합니까?


위의 코드는 초 단위로 계산합니다. 나노 초 단위로 답을 얻고 싶습니다.
gagneet

좋은 답변을 얻으려면 질문에 플랫폼을 추가해야합니다 (가급적이면 제목에도 추가).
Patrick Johnmeyer

시간을 확보하는 것 외에도 마이크로 벤치마킹 (매우 복잡한) 문제를 찾아야합니다. 한 번의 실행 만 수행하고 시작과 끝에서 시간을 확보하는 것은 충분한 정밀도를 제공하지 못할 것입니다.
Blaisorblade 2012

@Blaisorblade : 특히 clock()제가 생각했던 것만 큼 빠르지 않은 일부 테스트에서 발견 했기 때문입니다.
Mooing Duck 2012

답변:


83

루프에서 반복적으로 함수를 실행하는 것에 대해 다른 사람들이 게시 한 내용은 정확합니다.

Linux (및 BSD)의 경우 clock_gettime () 을 사용하고 싶습니다 .

#include <sys/time.h>

int main()
{
   timespec ts;
   // clock_gettime(CLOCK_MONOTONIC, &ts); // Works on FreeBSD
   clock_gettime(CLOCK_REALTIME, &ts); // Works on Linux
}

Windows의 경우 QueryPerformanceCounter 를 사용하려고합니다 . 그리고 여기에 QPC 에 대한 자세한 내용이 있습니다.

분명히 일부 칩셋의 QPC에 알려진 문제 가 있으므로 해당 칩셋이 없는지 확인하는 것이 좋습니다. 또한 일부 듀얼 코어 AMD도 문제를 일으킬 수 있습니다 . sebbbi의 두 번째 게시물을 참조하십시오.

QueryPerformanceCounter () 및 QueryPerformanceFrequency ()는 좀 더 나은 해상도를 제공하지만 다른 문제가 있습니다. 예를 들어 Windows XP에서 모든 AMD Athlon X2 듀얼 코어 CPU는 문제를 해결하기 위해 특별히 AMD 듀얼 코어 드라이버 패키지를 설치하지 않는 한 "무작위로"(PC가 약간 뒤로 이동 함) 두 코어 중 하나의 PC를 반환합니다. 비슷한 문제 (p4 dual, p4 ht, core2 dual, core2 quad, phenom quad)가있는 다른 듀얼 + 코어 CPU는 발견되지 않았습니다.

2013/07/16 수정 :

http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx에 명시된 특정 상황에서 QPC의 효과에 대한 논란이있는 것 같습니다 .

... QueryPerformanceCounter 및 QueryPerformanceFrequency는 일반적으로 여러 프로세서에 맞게 조정되지만 BIOS 또는 드라이버의 버그로 인해 스레드가 한 프로세서에서 다른 프로세서로 이동할 때 이러한 루틴이 다른 값을 반환 할 수 있습니다.

그러나이 StackOverflow 답변 https://stackoverflow.com/a/4588605/34329 는 QPC가 Win XP 서비스 팩 2 이후 모든 MS OS에서 제대로 작동해야한다고 말합니다.

이 문서에서는 Windows 7이 프로세서에 고정 TSC가 있는지 확인하고 그렇지 않은 경우 외부 타이머로 대체 할 수 있음을 보여줍니다. http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html 프로세서 간 동기화는 여전히 문제입니다.

타이머와 관련된 기타 정밀 판독 :

자세한 내용은 주석을 참조하십시오.


1
구형 듀얼 Xeon PC에서 TSC 클럭 스큐를 보았습니다.하지만 C1 클럭 램핑이 활성화 된 Athlon X2만큼 나쁘지는 않습니다. C1 클럭 램핑을 사용하면 HLT 명령을 실행하면 클럭이 느려지므로 유휴 코어의 TSC가 활성 코어보다 느리게 증가합니다.
bk1e

6
CLOCK_MONOTONIC은 내가 사용 가능한 Linux 버전에서 작동합니다.
Bernard

1
@Bernard-마지막으로 본 이후로 새로 추가되어야합니다. 알림 주셔서 감사합니다.
슬픔

3
실제로 CLOCK_MONOTONIC_RAWNTP에 의해 조정되지 않은 하드웨어 시간을 얻으려면 사용 가능한 경우을 사용해야 합니다.

여기에서 논의한 바와 같이, QPC의 올바른 구현은 적어도 신뢰할 수없는 것으로 알려진 곳에서 TSC 카운터를 사용하지 않습니다. stackoverflow.com/q/510462/53974
Blaisorblade

69

이 새로운 답변은 C ++ 11의 <chrono>기능을 사용합니다. 사용하는 방법을 보여 다른 답변이 있지만 <chrono>, 그들 중 누구도 사용하는 방법을 보여줍니다 <chrono>RDTSC여기에 다른 답변의 여러에서 언급 한 시설. 그래서 제가 사용하는 방법을 보여 것이라고 생각 RDTSC으로 <chrono>. 또한 , 및 / 또는을 RDTSC기반으로하는 시스템의 내장 클록 기능 사이를 빠르게 전환 할 수 있도록 클록에서 테스트 코드를 템플릿 화하는 방법을 보여줄 것 입니다.clock()clock_gettime()QueryPerformanceCounter

참고 것을 RDTSC명령은 86 별이다. QueryPerformanceCounterWindows 전용입니다. 그리고 clock_gettime()POSIX 전용입니다. 아래에서 두 가지 새로운 시계를 소개합니다. std::chrono::high_resolution_clockstd::chrono::system_clock, C ++ 11을 가정 할 수 있다면 이제 크로스 플랫폼입니다.

먼저, Intel rdtsc어셈블리 명령어 에서 C ++ 11 호환 시계를 만드는 방법이 있습니다 . 나는 그것을 부를 것이다 x::clock:

#include <chrono>

namespace x
{

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2'800'000'000>       period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    {
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    }
};

}  // x

이 클럭이하는 일은 CPU주기를 계산하고 부호없는 64 비트 정수에 저장하는 것입니다. 컴파일러의 어셈블리 언어 구문을 조정해야 할 수도 있습니다. 또는 컴파일러가 대신 사용할 수있는 내장 함수를 제공 할 수 있습니다 (예 :) now() {return __rdtsc();}.

시계를 만들려면 표시 (저장 유형)를 제공해야합니다. 또한 시스템이 다른 전원 모드에서 클럭 속도를 변경할 수 있더라도 컴파일 시간 상수 여야하는 클럭 기간을 제공해야합니다. 또한 이러한 기본 사항에 따라 시계의 "기본"시간 기간과 시점을 쉽게 정의 할 수 있습니다.

당신이 원하는 것이 클럭 틱의 수를 출력하는 것이라면, 클럭 기간 동안 어떤 숫자를 제공하든 상관 없습니다. 이 상수는 클럭 틱 수를 나노초와 같은 실시간 단위로 변환하려는 경우에만 작동합니다. 이 경우 클록 속도를 더 정확하게 제공할수록 나노초 (밀리 초 등) 로의 변환이 더 정확 해집니다.

다음은 사용 방법을 보여주는 예제 코드입니다 x::clock. 실제로 동일한 구문으로 여러 다른 시계를 사용할 수있는 방법을 보여주고 싶기 때문에 시계에 코드를 템플릿으로 작성했습니다. 이 특정 테스트는 루프에서 원하는 시간을 실행할 때 루프 오버 헤드가 무엇인지 보여줍니다.

#include <iostream>

template <class clock>
void
test_empty_loop()
{
    // Define real time units
    typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
    // or:
    // typedef std::chrono::nanoseconds nanoseconds;
    // Define double-based unit of clock tick
    typedef std::chrono::duration<double, typename clock::period> Cycle;
    using std::chrono::duration_cast;
    const int N = 100000000;
    // Do it
    auto t0 = clock::now();
    for (int j = 0; j < N; ++j)
        asm volatile("");
    auto t1 = clock::now();
    // Get the clock ticks per iteration
    auto ticks_per_iter = Cycle(t1-t0)/N;
    std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
    // Convert to real time units
    std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
              << "ps per iteration\n";
}

이 코드가 수행하는 첫 번째 작업은 결과를 표시 할 "실시간"단위를 만드는 것입니다. 피코 초를 선택했지만 정수 또는 부동 소수점 기반 중에서 원하는 단위를 선택할 수 있습니다. 예를 들어 std::chrono::nanoseconds내가 사용할 수 있는 미리 만들어진 장치가 있습니다.

또 다른 예로, 반복 당 평균 클럭 사이클 수를 부동 소수점으로 인쇄하고 싶으므로, 클럭의 틱 ( Cycle코드에서 호출 됨)과 동일한 단위를 갖는 double을 기준으로 다른 기간을 만듭니다 .

루프는 clock::now()양쪽 에 대한 호출로 시간이 설정됩니다. 이 함수에서 반환 된 유형의 이름을 지정하려면 다음과 같습니다.

typename clock::time_point t0 = clock::now();

( x::clock예제에 명확하게 표시되어 있으며 시스템 제공 시계에도 해당됨).

부동 소수점 클록 틱의 관점에서 기간을 얻으려면 1은 단순히 두 시점을 빼고 반복 당 값을 얻으려면 해당 기간을 반복 횟수로 나눕니다.

count()멤버 함수 를 사용하여 기간에 관계없이 개수를 가져올 수 있습니다 . 이것은 내부 표현을 반환합니다. 마지막으로 std::chrono::duration_cast기간 Cycle을 기간으로 변환하여 picoseconds인쇄합니다.

이 코드를 사용하는 방법은 간단합니다.

int main()
{
    std::cout << "\nUsing rdtsc:\n";
    test_empty_loop<x::clock>();

    std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
    test_empty_loop<std::chrono::high_resolution_clock>();

    std::cout << "\nUsing std::chrono::system_clock:\n";
    test_empty_loop<std::chrono::system_clock>();
}

나는 우리 집에서 만든을 사용하여 시험을 행사보다도 x::clock, 시스템이 제공하는 시계 두 개를 사용하여 그 결과를 비교 : std::chrono::high_resolution_clockstd::chrono::system_clock. 나를 위해 이것은 다음과 같이 인쇄됩니다.

Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration

Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration

Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration

이것은 반복 당 틱이 각 클록마다 크게 다르기 때문에 이러한 클록 각각이 다른 틱주기를 가지고 있음을 보여줍니다. 그러나 알려진 시간 단위 (예 : 피코 초)로 변환하면 각 시계에 대해 거의 동일한 결과를 얻습니다 (마일리지는 다를 수 있음).

내 코드에 "매직 변환 상수"가 완전히 없는지 확인하십시오. 실제로 전체 예제에는 두 개의 매직 넘버 만 있습니다.

  1. 정의하기 위해 내 컴퓨터의 클럭 속도 x::clock.
  2. 테스트 할 반복 횟수입니다. 이 숫자를 변경하면 결과가 크게 달라지는 경우 반복 횟수를 늘리거나 테스트하는 동안 컴퓨터에서 경쟁 프로세스를 비워야합니다.

5
"RDTSC는 Intel 전용입니다"라는 말은 x86 아키텍처 및 파생 제품을 실제로 언급하는 것입니다. AMD, Cyrix, Transmeta x86 칩에는 명령어가 있지만 Intel RISC 및 ARM 프로세서에는 없습니다.
Ben Voigt

1
@BenVoigt : +1 네, 수정이 정확합니다. 감사합니다.
Howard Hinnant

1
CPU 스로틀 링이 이에 어떤 영향을 미칩니 까? CPU 부하에 따라 클럭 속도가 변하지 않습니까?
Tejas Kale 2016

@TejasKale : "To build a clock you ..."로 시작하는 연속 된 두 단락의 답변에 설명되어 있습니다. 일반적으로 타이밍 코드는 스레드를 차단하는 작업을 측정하지 않지만 가능합니다. 따라서 일반적으로 CPU는 스로틀 링되지 않습니다. 그러나 sleep, mutex lock, condition_variable wait 등과 관련된 코드를 측정하는 경우 rdtsc시계가 다른 단위로 부정확하게 변환 될 수 있습니다. 시계를 쉽게 변경하고 비교할 수 있도록 측정을 설정하는 것이 좋습니다 (이 답변 참조).
Howard Hinnant

27

정확도 수준이 높으면 clock ()과 같은 시스템 호출보다는 CPU 틱에서 추론하는 것이 좋습니다 . 그리고 명령을 실행하는 데 1 나노초 이상이 걸린다면 ... 나노초 정확도를 갖는 것은 거의 불가능하다는 것을 잊지 마십시오.

그래도 그와 같은 것이 시작입니다.

다음은 CPU가 마지막으로 시작된 이후 통과 된 80x86 CPU 클럭 틱 수를 검색하는 실제 코드입니다. Pentium 이상에서 작동합니다 (386/486은 지원되지 않음). 이 코드는 실제로 MS Visual C ++ 전용이지만 인라인 어셈블리를 지원하는 한 다른 코드로 쉽게 이식 할 수 있습니다.

inline __int64 GetCpuClocks()
{

    // Counter
    struct { int32 low, high; } counter;

    // Use RDTSC instruction to get clocks count
    __asm push EAX
    __asm push EDX
    __asm __emit 0fh __asm __emit 031h // RDTSC
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    // Return result
    return *(__int64 *)(&counter);

}

이 기능은 또한 매우 빠르다는 장점이 있습니다. 일반적으로 실행하는 데 50 CPU주기를 넘지 않습니다.

타이밍 수치 사용 :
클럭 카운트를 실제 경과 시간으로 변환해야하는 경우 결과를 칩의 클럭 속도로 나눕니다. "정격"GHz는 칩의 실제 속도와 약간 다를 수 있습니다. 칩의 실제 속도를 확인하려면 몇 가지 아주 좋은 유틸리티 나 Win32 호출 인 QueryPerformanceFrequency ()를 사용할 수 있습니다.


정보 감사합니다. 이것은 유용합니다. 나는 시간을 계산하기 위해 CPU 사이클을 생각하지 않았다. 나는 그것이 명심해야 할 아주 좋은 점이라고 생각한다 :-)
gagneet

4
QueryPerformanceFrequency ()를 사용하여 TSC 카운트를 경과 시간으로 전환하면 작동하지 않을 수 있습니다. QueryPerformanceCounter ()는 사용 가능한 경우 Vista에서 HPET (고정밀 이벤트 타이머)를 사용합니다. 사용자가 boot.ini에 / USEPMTIMER을 추가하면 ACPI 전원 관리 타이머를 사용합니다.
bk1e

23

이 작업을 올바르게 수행하려면 with RDTSC또는 with 두 가지 방법 중 하나를 사용할 수 있습니다 clock_gettime(). 두 번째는 약 2 배 더 빠르며 적절한 절대 시간을 제공하는 이점이 있습니다. 참고를 위해 RDTSC(이 페이지에 다른 의견에 오류가 있고, 특정 프로세서에서 잘못된 타이밍 값을 얻을 수 있음) 표시된 작업을 올바르게 당신은 그것을 사용할 필요가

inline uint64_t rdtsc()
{
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx" );
    return (uint64_t)hi << 32 | lo;
}

및 clock_gettime : (마이크로 초 해상도를 임의로 선택했습니다)

#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
    timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}

생산 된시기와 가치 :

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

Processing time: (10000000 runs)
rdtsc           = 2292547353
clock_gettime   = 1031119636

22

원하는 결과를 얻기 위해 다음을 사용하고 있습니다.

#include <time.h>
#include <iostream>
using namespace std;

int main (int argc, char** argv)
{
    // reset the clock
    timespec tS;
    tS.tv_sec = 0;
    tS.tv_nsec = 0;
    clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    ...
    ... <code to check for the time to be put here>
    ...
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;

    return 0;
}

2
이 코드를 적용하려고 시도했기 때문에 먼저 timespec이 정의되지 않은 이유를 Google에 검색해야했기 때문에 투표했습니다. 그런 다음 POSIX가 무엇인지 Google로 검색해야했습니다. 그래서이 코드는 표준 라이브러리를 고수해야하는 Windows 사용자와 관련이 없습니다.
Daniel Katz

8

를 들어 C ++ (11) , 여기에 간단한 래퍼입니다 :

#include <iostream>
#include <chrono>

class Timer
{
public:
    Timer() : beg_(clock_::now()) {}
    void reset() { beg_ = clock_::now(); }
    double elapsed() const {
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count(); }

private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;
};

또는 * nix의 C ++ 03의 경우

class Timer
{
public:
    Timer() { clock_gettime(CLOCK_REALTIME, &beg_); }

    double elapsed() {
        clock_gettime(CLOCK_REALTIME, &end_);
        return end_.tv_sec - beg_.tv_sec +
            (end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
    }

    void reset() { clock_gettime(CLOCK_REALTIME, &beg_); }

private:
    timespec beg_, end_;
};

사용 예 :

int main()
{
    Timer tmr;
    double t = tmr.elapsed();
    std::cout << t << std::endl;

    tmr.reset();
    t = tmr.elapsed();
    std::cout << t << std::endl;
    return 0;
}

에서 https://gist.github.com/gongzhitaao/7062087


5

일반적으로 함수를 호출하는 데 걸리는 시간을 측정하려면 한 번보다 여러 번 수행하려고합니다. 함수를 한 번만 호출하고 실행하는 데 매우 짧은 시간이 걸리더라도 실제로 타이머 함수를 호출하는 오버 헤드가 있고 시간이 얼마나 걸리는지 알 수 없습니다.

예를 들어 함수를 실행하는 데 800ns가 걸릴 수 있다고 추정하는 경우 루프에서 1,000 만 번 호출합니다 (약 8 초 소요). 총 시간을 천만으로 나누어 통화 당 시간을 구하십시오.


actualyy, 특정 호출에 대해 api의 성능을 얻으려고합니다. 각 실행에 대해 다른 시간을 줄 수 있으며 성능 향상을 위해 그래프에 영향을 줄 수 있습니다. 따라서 시간은 나노초입니다. 하지만 예, 이것은 좋은 생각입니다.
gagneet

5

x86 프로세서에서 실행되는 gcc에서 다음 기능을 사용할 수 있습니다.

unsigned long long rdtsc()
{
  #define rdtsc(low, high) \
         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))

  unsigned int low, high;
  rdtsc(low, high);
  return ((ulonglong)high << 32) | low;
}

Digital Mars C ++ 사용 :

unsigned long long rdtsc()
{
   _asm
   {
        rdtsc
   }
}

칩의 고성능 타이머를 읽습니다. 프로파일 링을 할 때 이것을 사용합니다.


2
이것은 유용합니다. 실험을 위해 애플 맥을 사용하고 있기 때문에 프로세서가 x86인지 확인할 것입니다 ... 감사합니다 :-)
gagneet

1
사용자가 높고 낮은 값에 대해 어떤 값을 제공해야합니까? 함수 본문 안에 매크로를 정의하는 이유는 무엇입니까? 또한 unsigned long long으로 typedef 된 것으로 추정되는 ulonglong은 표준 유형이 아닙니다. 나는 이것을 사용하고 싶습니다하지만 난 확실히하는 방법을 모르겠어요)
조셉 가빈에게

1
unsigned long은 리눅스에서 사용하기에 옳지 않습니다. 64 비트 Linux에서 long과 long long이 모두 64 비트이면 대신 int를 사용하는 것이 좋습니다.
Marius

3
오늘날 TSC 카운터는 종종 신뢰할 수 없습니다. 주파수가 변경되면 많은 프로세서에서 속도가 변경되고 다른 코어에서 일관성이 없으므로 TSC가 항상 증가하지는 않습니다.
Blaisorblade 2012

1
@Marius : unsigned int내부 유형으로 사용하여 귀하의 의견을 구현했습니다 .
Blaisorblade 2012

3

1 초 미만의 정밀도가 필요한 경우 시스템 별 확장을 사용해야하며 운영 체제에 대한 문서를 확인해야합니다. POSIX는 gettimeofday 를 사용하여 최대 마이크로 초를 지원 하지만 컴퓨터의 주파수가 1GHz를 초과하지 않았기 때문에 더 정확한 것은 없습니다.

Boost를 사용하는 경우 boost :: posix_time 확인할 수 있습니다 .


코드를 이식 가능한 상태로 유지하려면 부스트 라이브러리를보고 코드와 함께 번들로 사용할 수 있는지 확인합니다. 감사합니다 :-)
gagneet

3

여기서 볼랜드 코드를 사용하고 있는데 ti_hund가 나에게 음수를주는 코드이지만 타이밍이 상당히 좋습니다.

#include <dos.h>

void main() 
{
struct  time t;
int Hour,Min,Sec,Hun;
gettime(&t);
Hour=t.ti_hour;
Min=t.ti_min;
Sec=t.ti_sec;
Hun=t.ti_hund;
printf("Start time is: %2d:%02d:%02d.%02d\n",
   t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
....
your code to time
...

// read the time here remove Hours and min if the time is in sec

gettime(&t);
printf("\nTid Hour:%d Min:%d Sec:%d  Hundreds:%d\n",t.ti_hour-Hour,
                             t.ti_min-Min,t.ti_sec-Sec,t.ti_hund-Hun);
printf("\n\nAlt Ferdig Press a Key\n\n");
getch();
} // end main

3

Brock Adams의 방법을 간단한 클래스와 함께 사용 :

int get_cpu_ticks()
{
    LARGE_INTEGER ticks;
    QueryPerformanceFrequency(&ticks);
    return ticks.LowPart;
}

__int64 get_cpu_clocks()
{
    struct { int32 low, high; } counter;

    __asm cpuid
    __asm push EDX
    __asm rdtsc
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    return *(__int64 *)(&counter);
}

class cbench
{
public:
    cbench(const char *desc_in) 
         : desc(strdup(desc_in)), start(get_cpu_clocks()) { }
    ~cbench()
    {
        printf("%s took: %.4f ms\n", desc, (float)(get_cpu_clocks()-start)/get_cpu_ticks());
        if(desc) free(desc);
    }
private:
    char *desc;
    __int64 start;
};

사용 예 :

int main()
{
    {
        cbench c("test");
        ... code ...
    }
    return 0;
}

결과:

테스트 소요 : 0.0002ms

일부 함수 호출 오버 헤드가 있지만 여전히 충분히 빨라야합니다. :)


3

당신이 사용할 수있는 프로파일 내장 (프로세서 사이클 카운트에서) 멀티 플랫폼 타이머에 대한 인터페이스를 가지고 (Windows 및 Linux 용 무료) 당신에게 초 당 사이클 수를 제공 할 수 있습니다 :

EProfilerTimer timer;
timer.Start();

... // Your code here

const uint64_t number_of_elapsed_cycles = timer.Stop();
const uint64_t nano_seconds_elapsed =
    mumber_of_elapsed_cycles / (double) timer.GetCyclesPerSecond() * 1000000000;

주기 수를 시간으로 재 계산하는 것은 CPU 주파수가 동적으로 변경 될 수있는 최신 프로세서에서 위험한 작업 일 수 있습니다. 따라서 변환 된 시간이 올바른지 확인하려면 프로파일 링 전에 프로세서 주파수를 수정해야합니다.


2

이것이 Linux의 경우에는 Epoch 이후 초와 마이크로 초를 제공하는 구조체를 반환하는 "gettimeofday"함수를 사용하고 있습니다. 그런 다음 timersub를 사용하여 두 값을 빼서 시간 차이를 얻고 원하는 시간 정밀도로 변환 할 수 있습니다. 그러나 나노초를 지정하면 clock_gettime () 함수 가 찾고있는 것처럼 보입니다 . 그것은 당신이 전달하는 구조에 초와 나노초 단위로 시간을 넣습니다.


clock_gettime ()이 트릭을 수행해야합니다. 내 목적을 위해 똑같이 사용해
보겠습니다

2

그것에 대해 어떻게 생각하십니까?

    int iceu_system_GetTimeNow(long long int *res)
    {
      static struct timespec buffer;
      // 
    #ifdef __CYGWIN__
      if (clock_gettime(CLOCK_REALTIME, &buffer))
        return 1;
    #else
      if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &buffer))
        return 1;
    #endif
      *res=(long long int)buffer.tv_sec * 1000000000LL + (long long int)buffer.tv_nsec;
      return 0;
    }

2

다음은 잘 작동 하는 멋진 부스트 타이머입니다.

//Stopwatch.hpp

#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP

//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>

class Stopwatch
{
public:
    Stopwatch();
    virtual         ~Stopwatch();
    void            Restart();
    std::uint64_t   Get_elapsed_ns();
    std::uint64_t   Get_elapsed_us();
    std::uint64_t   Get_elapsed_ms();
    std::uint64_t   Get_elapsed_s();
private:
    boost::chrono::high_resolution_clock::time_point _start_time;
};

#endif // STOPWATCH_HPP


//Stopwatch.cpp

#include "Stopwatch.hpp"

Stopwatch::Stopwatch():
    _start_time(boost::chrono::high_resolution_clock::now()) {}

Stopwatch::~Stopwatch() {}

void Stopwatch::Restart()
{
    _start_time = boost::chrono::high_resolution_clock::now();
}

std::uint64_t Stopwatch::Get_elapsed_ns()
{
    boost::chrono::nanoseconds nano_s = boost::chrono::duration_cast<boost::chrono::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(nano_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_us()
{
    boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(micro_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_ms()
{
    boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(milli_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_s()
{
    boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(sec.count());
}

2

최소한의 복사 및 붙여 넣기 구조체 + 지연 사용

아이디어가 빠른 테스트에 사용할 수있는 최소한의 구조체를 갖는 것이라면 C ++ 파일에서의 바로 뒤에 복사하여 붙여 넣는 것이 좋습니다 #include. 이것은 내가 Allman 스타일 형식을 희생하는 유일한 경우입니다.

구조체의 첫 번째 줄에서 정밀도를 쉽게 조정할 수 있습니다. 가능한 값은 다음과 같습니다 nanoseconds, microseconds, milliseconds, seconds, minutes, 또는 hours.

#include <chrono>
struct MeasureTime
{
    using precision = std::chrono::microseconds;
    std::vector<std::chrono::steady_clock::time_point> times;
    std::chrono::steady_clock::time_point oneLast;
    void p() {
        std::cout << "Mark " 
                << times.size()/2
                << ": " 
                << std::chrono::duration_cast<precision>(times.back() - oneLast).count() 
                << std::endl;
    }
    void m() {
        oneLast = times.back();
        times.push_back(std::chrono::steady_clock::now());
    }
    void t() {
        m();
        p();
        m();
    }
    MeasureTime() {
        times.push_back(std::chrono::steady_clock::now());
    }
};

용법

MeasureTime m; // first time is already in memory
doFnc1();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...

표준 출력 결과

Mark 1: 123
Mark 2: 32
Mark 3: 433234

실행 후 요약을 원하는 경우

나중에 보고서를 원하는 경우 예를 들어 그 사이의 코드도 표준 출력에 기록하기 때문입니다. 그런 다음 구조체에 다음 함수를 추가합니다 (MeasureTime () 바로 앞).

void s() { // summary
    int i = 0;
    std::chrono::steady_clock::time_point tprev;
    for(auto tcur : times)
    {
        if(i > 0)
        {
            std::cout << "Mark " << i << ": "
                    << std::chrono::duration_cast<precision>(tprev - tcur).count()
                    << std::endl;
        }
        tprev = tcur;
        ++i;
    }
}

따라서 다음을 사용할 수 있습니다.

MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();

이전과 마찬가지로 모든 마크를 나열하지만 다른 코드가 실행 된 후에 나열됩니다. 당신이 모두를 사용하지 않도록주의 m.s()하고 m.t().


Ubuntu 16.04에서 OpenMP와 완벽하게 작동합니다. 고마워요, 이것이 최고의 대답 IMO입니다!
Íhor Mé
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.