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_clock및 std::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_clock와 std::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.