C ++에서 코드 조각의 실행 시간을 계산하는 방법


121

C ++ 코드 조각의 실행 시간을 초 단위로 계산해야합니다. Windows 또는 Unix 시스템에서 작동해야합니다.

이 작업을 수행하기 위해 다음 코드를 사용합니다. (이전에 가져 오기)

clock_t startTime = clock();
// some code here
// to compute its execution duration in runtime
cout << double( clock() - startTime ) / (double)CLOCKS_PER_SEC<< " seconds." << endl;

그러나 a = a + 1과 같은 작은 입력이나 짧은 문장의 경우 "0 초"결과를 얻습니다. 0.0000001 초 정도가 될 것 같아요.

System.nanoTime()이 경우 Java에서 잘 작동 한다는 것을 기억합니다 . 그러나 clock()C ++의 기능에서 동일한 기능을 얻을 수 없습니다 .

해결책이 있습니까?


29
OS가 처음부터 끝까지 스레드를 실행하지 못할 수 있기 때문에 시간차 기반 비교는 정확하지 않을 수 있습니다. 그것은 그것을 중단하고 당신의 것과 인터레이스 된 다른 스레드를 실행할 수 있으며, 이는 작업을 완료하는 데 걸리는 실제 시간에 상당한 영향을 미칩니다. 여러 번 실행하고 결과를 평균화 할 수 있습니다. 실행중인 다른 프로세스의 수를 최소화 할 수 있습니다. 그러나 이들 중 어느 것도 스레드 중단 효과를 완전히 제거하지 않습니다.
Mordachai

14
Mordachi, 왜 그것을 제거하고 싶습니까? 스레드가 중단되지 않는 마법의 영역이 아닌 실제 환경에서 함수가 어떻게 수행되는지 확인하려고합니다. 여러 번 실행하고 평균을내는 한 매우 정확합니다.
Thomas Bonini

예, 몇 번 실행하고 결과를 평균합니다.
AhmetB-Google

14
Andreas, Mordachai의 의견은 OP가 자신의 코드 성능을 다른 알고리즘과 비교하려는 경우 관련이 있습니다. 예를 들어, 그가 오늘 오후에 여러 시계 테스트를 실행 한 다음 내일 아침에 다른 알고리즘을 테스트하는 경우 아침보다 오후에 더 많은 프로세스와 리소스를 공유 할 수 있으므로 그의 비교는 신뢰할 수 없을 수 있습니다. 또는 한 세트의 코드로 인해 OS가 처리 시간을 단축 할 수 있습니다. 시간 기반 비교를 수행하려는 경우 이러한 유형의 성능 측정을 신뢰할 수없는 이유는 여러 가지가 있습니다.
weberc2 2013-08-30

4
@Mordachai 나는 내가 오래된 의견에 답하고 있다는 것을 알고 있지만, 내가 한 것처럼 이것을 우연히 발견하는 사람은 평균이 아닌 최소 몇 번의 실행을 원합니다. 이것은 OS에 의한 중단이 가장 적기 때문에 대부분 코드 타이밍입니다.
Baruch

답변:


115

내가 작성한이 기능을 사용할 수 있습니다. 을 호출 GetTimeMs64()하면 시스템 시계를 사용하는 유닉스 에포크 이후 경과 된 밀리 초 수를 반환합니다 time(NULL).

Windows와 Linux 모두에서 작동합니다. 스레드로부터 안전합니다.

세분성은 Windows에서 15ms입니다. Linux에서는 구현에 따라 다르지만 일반적으로 15ms입니다.

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#include <ctime>
#endif

/* Remove if already defined */
typedef long long int64; typedef unsigned long long uint64;

/* Returns the amount of milliseconds elapsed since the UNIX epoch. Works on both
 * windows and linux. */

uint64 GetTimeMs64()
{
#ifdef _WIN32
 /* Windows */
 FILETIME ft;
 LARGE_INTEGER li;

 /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
  * to a LARGE_INTEGER structure. */
 GetSystemTimeAsFileTime(&ft);
 li.LowPart = ft.dwLowDateTime;
 li.HighPart = ft.dwHighDateTime;

 uint64 ret = li.QuadPart;
 ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
 ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */

 return ret;
#else
 /* Linux */
 struct timeval tv;

 gettimeofday(&tv, NULL);

 uint64 ret = tv.tv_usec;
 /* Convert from micro seconds (10^-6) to milliseconds (10^-3) */
 ret /= 1000;

 /* Adds the seconds (10^0) after converting them to milliseconds (10^-3) */
 ret += (tv.tv_sec * 1000);

 return ret;
#endif
}

1
나중에 참조 할 수 있도록 헤더 파일에 넣어 사용합니다. 그것을 가지고 기쁩니다.
Daniel Handojo

1
gettimeofday시스템 시계가 변경되면 방법 이 의도하지 않은 결과를 줄 수 있다고 생각합니다 . 이것이 문제가되는 경우 clock_gettime대신 살펴볼 수 있습니다 .
Azmisov

Windows 용이 방법에 비해 장점이 GetTickCount있습니까?
MicroVirus

컴파일하지 않음gcc -std=c99
Assimilater

@MicroVirus : 예, GetTickCount시스템이 시작된 이후로 경과 한 시간이고 내 함수는 UNIX 시대 이후의 시간을 반환하므로 날짜와 시간에 사용할 수 있습니다. 두 이벤트 사이에 경과 된 시간에만 관심이 있다면 int64이기 때문에 내 것이 여전히 더 나은 선택입니다. GetTickCount는 int32이며 50 일마다 오버플로되므로 등록한 두 이벤트가 오버플로 사이에 있으면 이상한 결과를 얻을 수 있습니다.
Thomas Bonini

43

마이크로 초 (UNIX, POSIX 등)를 사용하는 또 다른 작업 예제가 있습니다.

    #include <sys/time.h>
    typedef unsigned long long timestamp_t;

    static timestamp_t
    get_timestamp ()
    {
      struct timeval now;
      gettimeofday (&now, NULL);
      return  now.tv_usec + (timestamp_t)now.tv_sec * 1000000;
    }

    ...
    timestamp_t t0 = get_timestamp();
    // Process
    timestamp_t t1 = get_timestamp();

    double secs = (t1 - t0) / 1000000.0L;

다음은이를 코딩 한 파일입니다.

https://github.com/arhuaco/junkcode/blob/master/emqbit-bench/bench.c


5
#include <sys/time.h>예제 시작 부분에 추가해야합니다 .
niekas

40

다음은 만족스러운 해상도를 제공하는 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의 경우

#include <iostream>
#include <ctime>

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


C ++ 11 솔루션에서이 오류가 발생합니다./usr/lib/x86_64-linux-gnu/libstdc++.so.6: version GLIBCXX_3.4.19 not found (required by ../cpu_2d/g500)
user9869932

@julianromera 어떤 플랫폼을 사용하고 있습니까? libstdc ++ 라이브러리와 g ++를 설치 했습니까?
gongzhitaao

Linux 우분투 12의 Slurm 그리드입니다. 방금 수정했습니다. 링커 끝에 -static-libstdc ++를 추가했습니다. @gongzhitaao를 요청 해 주셔서 감사합니다
user9869932

18
#include <boost/progress.hpp>

using namespace boost;

int main (int argc, const char * argv[])
{
  progress_timer timer;

  // do stuff, preferably in a 100x loop to make it take longer.

  return 0;
}

progress_timer범위를 벗어나 그것의 창조 이후 경과 된 시간을 인쇄합니다.

업데이트 : 다음은 Boost없이 작동하는 버전입니다 (macOS / iOS에서 테스트 됨).

#include <chrono>
#include <string>
#include <iostream>
#include <math.h>
#include <unistd.h>

class NLTimerScoped {
private:
    const std::chrono::steady_clock::time_point start;
    const std::string name;

public:
    NLTimerScoped( const std::string & name ) : name( name ), start( std::chrono::steady_clock::now() ) {
    }


    ~NLTimerScoped() {
        const auto end(std::chrono::steady_clock::now());
        const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count();

        std::cout << name << " duration: " << duration_ms << "ms" << std::endl;
    }

};

int main(int argc, const char * argv[]) {

    {
        NLTimerScoped timer( "sin sum" );

        float a = 0.0f;

        for ( int i=0; i < 1000000; i++ ) {
            a += sin( (float) i / 100 );
        }

        std::cout << "sin sum = " << a << std::endl;
    }



    {
        NLTimerScoped timer( "sleep( 4 )" );

        sleep( 4 );
    }



    return 0;
}

2
이것은 작동하지만 progress_timer는 더 이상 사용되지 않습니다 (때때로 boost 1.50 이전)-auto_cpu_timer가 더 적절할 수 있습니다.
davidA

3
@meowsqueak 흠, auto_cpu_timer는 Boost 시스템 라이브러리를 연결해야하는 것 같으므로 더 이상 헤더 전용 솔루션이 아닙니다. 너무 나빠요 ... 갑자기 다른 옵션이 더 매력적으로 보입니다.
Tomas Andrle 2012 년

1
예, 좋은 지적입니다. Boost를 아직 연결하지 않았다면 가치보다 문제가 더 많습니다. 하지만 이미 사용했다면 꽤 잘 작동합니다.
davidA

@meowsqueak 예, 또는 빠른 벤치 마크 테스트를 위해 이전 버전의 Boost를 받으십시오.
Tomas Andrle

@TomasAndrle 링크가 더 이상 존재하지 않습니다.
Zheng Qu

5

Windows는 QueryPerformanceCounter () 함수를 제공하고 Unix에는 gettimeofday ()가 있습니다. 두 함수 모두 최소 1 마이크로 초 차이를 측정 할 수 있습니다.


그러나 windows.h 사용은 제한됩니다. 동일한 컴파일 된 소스가 Windows와 Unix 모두에서 실행되어야합니다. 이 문제를 어떻게 처리합니까?
AhmetB-Google

2
그런 다음 래퍼 라이브러리를 찾으십시오. stackoverflow.com/questions/1487695/…
Captain Comic

4
동일한 컴파일 된 소스 가 두 시스템 모두에서 동일한 바이너리를 실행하려는 것처럼 들리지만 그렇지 않은 것 같습니다. 동일한 소스 를 의미 했다면 a #ifdef는 괜찮아 야합니다 (그리고 수락 한 답변으로 판단) #ifdef WIN32 #include <windows.h> ... #else ... #endif. 그러면 문제가 보이지 않습니다 ..
그냥 누군가

3

일부 프로그램에서는 이러한 목적으로 RDTS 를 사용 했습니다 . RDTSC는 시간에 관한 것이 아니라 프로세서 시작부터 사이클 수에 관한 것입니다. 초 단위로 결과를 얻으려면 시스템에서 보정해야하지만 성능을 평가하고 싶을 때 정말 편리합니다. 사이클 수를 다시 초로 변경하지 않고 직접 사용하는 것이 훨씬 좋습니다.

(위의 링크는 프랑스어 위키피디아 페이지이지만 C ++ 코드 샘플이 있으며 영어 버전은 여기에 있습니다 )


2

시스템에서 시간 정보를 얻기 위해 표준 라이브러리 함수를 사용하는 것이 좋습니다.

더 미세한 해결을 원하면 더 많은 실행 반복을 수행하십시오. 프로그램을 한 번 실행하고 샘플을 얻는 대신 1000 회 이상 실행하십시오.


2

전체 (루프 + 성능 타이밍)를 여러 번 실행하고 평균하는 것보다 내부 루프 반복을 분할하여 성능 타이밍을 한 번만 사용하여 내부 루프를 여러 번 실행하는 것이 좋습니다. 이렇게하면 실제 프로파일 링 된 섹션에 비해 성능 타이밍 코드의 오버 헤드가 줄어 듭니다.

적절한 시스템에 대한 타이머 호출을 래핑합니다. Windows의 경우 QueryPerformanceCounter는 매우 빠르고 사용하기에 "안전"합니다.

최신 X86 PC에서도 "rdtsc"를 사용할 수 있지만 일부 멀티 코어 시스템 (코어 호핑이 타이머를 변경할 수 있음)에 문제가 있거나 일종의 속도 단계가 켜져있는 경우 문제가있을 수 있습니다.


2

(Windows 특정 솔루션) Windows에서 정확한 타이밍을 얻는 현재 (2017 년 경) 방법은 "QueryPerformanceCounter"를 사용하는 것입니다. 이 접근 방식은 매우 정확한 결과를 제공하는 이점이 있으며 MS에서 권장합니다. 코드 blob을 새 콘솔 앱에 넣기 만하면 작동하는 샘플을 얻을 수 있습니다. 여기에 긴 논의가 있습니다. 고해상도 타임 스탬프 획득

#include <iostream>
#include <tchar.h>
#include <windows.h>

int main()
{
constexpr int MAX_ITER{ 10000 };
constexpr __int64 us_per_hour{ 3600000000ull }; // 3.6e+09
constexpr __int64 us_per_min{ 60000000ull };
constexpr __int64 us_per_sec{ 1000000ull };
constexpr __int64 us_per_ms{ 1000ull };

// easy to work with
__int64 startTick, endTick, ticksPerSecond, totalTicks = 0ull;

QueryPerformanceFrequency((LARGE_INTEGER *)&ticksPerSecond);

for (int iter = 0; iter < MAX_ITER; ++iter) {// start looping
    QueryPerformanceCounter((LARGE_INTEGER *)&startTick); // Get start tick
    // code to be timed
    std::cout << "cur_tick = " << iter << "\n";
    QueryPerformanceCounter((LARGE_INTEGER *)&endTick); // Get end tick
    totalTicks += endTick - startTick; // accumulate time taken
}

// convert to elapsed microseconds
__int64 totalMicroSeconds =  (totalTicks * 1000000ull)/ ticksPerSecond;

__int64 hours = totalMicroSeconds / us_per_hour;
totalMicroSeconds %= us_per_hour;
__int64 minutes = totalMicroSeconds / us_per_min;
totalMicroSeconds %= us_per_min;
__int64 seconds = totalMicroSeconds / us_per_sec;
totalMicroSeconds %= us_per_sec;
__int64 milliseconds = totalMicroSeconds / us_per_ms;
totalMicroSeconds %= us_per_ms;


std::cout << "Total time: " << hours << "h ";
std::cout << minutes << "m " << seconds << "s " << milliseconds << "ms ";
std::cout << totalMicroSeconds << "us\n";

return 0;
}

2

각 테스트마다 정확히 동일한 시간을 산출해야하는 스레드 스케줄링에 대한 완벽한 솔루션은 프로그램을 OS 독립적으로 컴파일하고 OS가없는 환경에서 프로그램을 실행하기 위해 컴퓨터를 부팅하는 것입니다. 그러나 이것은 대체로 비실용적이며 기껏해야 어려울 것입니다.

OS를 사용하지 않는 좋은 대안은 현재 스레드의 선호도를 1 코어로 설정하고 우선 순위를 가장 높게 설정하는 것입니다. 이 대안은 일관된 충분한 결과를 제공해야합니다.

또한 디버깅을 방해하는 최적화를 꺼야합니다. g ++ 또는 gcc의 경우 테스트중인 코드가 최적화되지 않도록 명령 줄에 추가 -Og하는 것을 의미 합니다. 이 -O0플래그는 타이밍 결과에 포함될 불필요한 추가 오버 헤드를 도입하여 코드의 시간 제한 속도를 왜곡하므로 사용해서는 안됩니다.

반대로 최종 프로덕션 빌드 -Ofast에서 사용 (또는 최소한 -O3) 한다고 가정하고 "죽은"코드 제거 문제를 무시하면 다음 -Og과 비교하여 매우 적은 최적화를 수행합니다 -Ofast. 따라서 -Og최종 제품에서 코드의 실제 속도를 잘못 나타낼 수 있습니다.

또한 모든 속도 테스트는 (어느 정도까지) 위증이됩니다.로 컴파일 된 최종 프로덕션 제품 -Ofast에서 코드의 각 스 니펫 / 섹션 / 기능은 격리되지 않습니다. 오히려 각 코드 조각이 계속해서 다음 코드로 흘러가므로 컴파일러가 모든 곳에서 코드 조각을 결합, 병합 및 최적화 할 수 있습니다.

동시에를 많이 사용하는 코드 조각을 벤치마킹하는 경우 realloc()메모리 조각화가 충분히 높은 프로덕션 제품에서 코드 조각이 느리게 실행될 수 있습니다. 따라서 최종 프로덕션 빌드의 코드가 속도 테스트중인 개별 스 니펫보다 눈에 띄게 더 빠르거나 느리게 실행될 수 있기 때문에 "전체가 부분의 합보다 크다"라는 표현이이 상황에 적용됩니다.

불일치를 줄일 수있는 부분적인 솔루션은 데드 코드 / 루프 제거를 방지하기 위해 테스트에 포함 된 변수를 -Ofast추가 하여 속도 테스트에 사용 하는 것입니다 asm volatile("" :: "r"(var)).

다음은 Windows 컴퓨터에서 제곱근 함수를 벤치마킹하는 방법의 예입니다.

// set USE_ASM_TO_PREVENT_ELIMINATION  to 0 to prevent `asm volatile("" :: "r"(var))`
// set USE_ASM_TO_PREVENT_ELIMINATION  to 1 to enforce `asm volatile("" :: "r"(var))`
#define USE_ASM_TO_PREVENT_ELIMINATION 1

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <chrono>
#include <cmath>
#include <windows.h>
#include <intrin.h>
#pragma intrinsic(__rdtsc)
#include <cstdint>

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

unsigned int guess_sqrt32(register unsigned int n) {
    register unsigned int g = 0x8000;
    if(g*g > n) {
        g ^= 0x8000;
    }
    g |= 0x4000;
    if(g*g > n) {
        g ^= 0x4000;
    }
    g |= 0x2000;
    if(g*g > n) {
        g ^= 0x2000;
    }
    g |= 0x1000;
    if(g*g > n) {
        g ^= 0x1000;
    }
    g |= 0x0800;
    if(g*g > n) {
        g ^= 0x0800;
    }
    g |= 0x0400;
    if(g*g > n) {
        g ^= 0x0400;
    }
    g |= 0x0200;
    if(g*g > n) {
        g ^= 0x0200;
    }
    g |= 0x0100;
    if(g*g > n) {
        g ^= 0x0100;
    }
    g |= 0x0080;
    if(g*g > n) {
        g ^= 0x0080;
    }
    g |= 0x0040;
    if(g*g > n) {
        g ^= 0x0040;
    }
    g |= 0x0020;
    if(g*g > n) {
        g ^= 0x0020;
    }
    g |= 0x0010;
    if(g*g > n) {
        g ^= 0x0010;
    }
    g |= 0x0008;
    if(g*g > n) {
        g ^= 0x0008;
    }
    g |= 0x0004;
    if(g*g > n) {
        g ^= 0x0004;
    }
    g |= 0x0002;
    if(g*g > n) {
        g ^= 0x0002;
    }
    g |= 0x0001;
    if(g*g > n) {
        g ^= 0x0001;
    }
    return g;
}

unsigned int empty_function( unsigned int _input ) {
    return _input;
}

unsigned long long empty_ticks=0;
double empty_seconds=0;
Timer my_time;

template<unsigned int benchmark_repetitions>
void benchmark( char* function_name, auto (*function_to_do)( auto ) ) {
    register unsigned int i=benchmark_repetitions;
    register unsigned long long start=0;
    my_time.reset();
    start=__rdtsc();
    while ( i-- ) {
        auto result = (*function_to_do)( i << 7 );
        #if USE_ASM_TO_PREVENT_ELIMINATION == 1
            asm volatile("" :: "r"(
                // There is no data type in C++ that is smaller than a char, so it will
                //  not throw a segmentation fault error to reinterpret any arbitrary
                //  data type as a char. Although, the compiler might not like it.
                result
            ));
        #endif
    }
    if ( function_name == nullptr ) {
        empty_ticks = (__rdtsc()-start);
        empty_seconds = my_time.elapsed();
        std::cout<< "Empty:\n" << empty_ticks
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << empty_seconds
                << " seconds\n\n";
    } else {
        std::cout<< function_name<<":\n" << (__rdtsc()-start-empty_ticks)
              << " ticks\n" << benchmark_repetitions << " repetitions\n"
               << std::setprecision(15) << (my_time.elapsed()-empty_seconds)
                << " seconds\n\n";
    }
}


int main( void ) {
    void* Cur_Thread=   GetCurrentThread();
    void* Cur_Process=  GetCurrentProcess();
    unsigned long long  Current_Affinity;
    unsigned long long  System_Affinity;
    unsigned long long furthest_affinity;
    unsigned long long nearest_affinity;

    if( ! SetThreadPriority(Cur_Thread,THREAD_PRIORITY_TIME_CRITICAL) ) {
        SetThreadPriority( Cur_Thread, THREAD_PRIORITY_HIGHEST );
    }
    if( ! SetPriorityClass(Cur_Process,REALTIME_PRIORITY_CLASS) ) {
        SetPriorityClass( Cur_Process, HIGH_PRIORITY_CLASS );
    }
    GetProcessAffinityMask( Cur_Process, &Current_Affinity, &System_Affinity );
    furthest_affinity = 0x8000000000000000ULL>>__builtin_clzll(Current_Affinity);
    nearest_affinity  = 0x0000000000000001ULL<<__builtin_ctzll(Current_Affinity);
    SetProcessAffinityMask( Cur_Process, furthest_affinity );
    SetThreadAffinityMask( Cur_Thread, furthest_affinity );

    const int repetitions=524288;

    benchmark<repetitions>( nullptr, empty_function );
    benchmark<repetitions>( "Standard Square Root", standard_sqrt );
    benchmark<repetitions>( "Original Guess Square Root", original_guess_sqrt32 );
    benchmark<repetitions>( "New Guess Square Root", new_guess_sqrt32 );


    SetThreadPriority( Cur_Thread, THREAD_PRIORITY_IDLE );
    SetPriorityClass( Cur_Process, IDLE_PRIORITY_CLASS );
    SetProcessAffinityMask( Cur_Process, nearest_affinity );
    SetThreadAffinityMask( Cur_Thread, nearest_affinity );
    for (;;) { getchar(); }

    return 0;
}

또한 Timer에 대한 Mike Jarvis의 공로를 인정합니다.

더 큰 코드 조각을 실행하려는 경우 컴퓨터가 중단되는 것을 방지하기 위해 반복 횟수를 줄여야한다는 점에 유의하십시오 (매우 중요합니다).


2
최적화 비활성화를 제외하고는 좋은 대답입니다. 벤치마킹 -O0코드는 -O0 일반 대신 오버 헤드가 발생 -O2하거나 코드와 워크로드에 -O3 -march=native따라 크게 달라 지므로 시간 낭비가 큽니다 . 예를 들어 추가로 명명 된 tmp vars는 -O0. volatile, 비 인라인 함수 또는 빈 인라인 asm 문 을 사용하여 최적화 프로그램에서 항목을 숨기는 것과 같이 최적화되지 않도록하는 다른 방법이 있습니다 . -O0코드는에서 다른 병목 현상 이 있기 때문에 -O0동일하지는 않지만 더 나쁘기 때문에 사용 가능 하지 않습니다.
Peter Cordes

1
으, -Og코드에 따라 여전히 현실적이지 않습니다. 적어도 -O2, 바람직하게 -O3는 더 현실적입니다. 사용 asm volatile("" ::: "+r"(var))또는 뭔가 컴파일러가 그것을 통해 레지스터에 값 패배 상수 전파를 실현 할 수 있습니다.
Peter Cordes

@PeterCordes 귀하의 통찰력에 다시 한 번 감사드립니다. 콘텐츠 -O3및 코드 스 니펫을 asm volatile("" ::: "+r"(var)).
Jack Giffin

1
asm volatile("" ::: "+r"( i ));불필요 해 보입니다. 최적화 된 코드에서는 컴파일러가 루프 내부 i뿐만 아니라 구체화하도록 강제 할 이유가 없습니다 i<<7. tmp -= 128매번 이동 하는 대신 최적화에서 멈 춥니 다 . 함수 호출의 결과를 사용하는 것은 좋지만, 그렇지 않은 경우에는 좋습니다 void. 처럼 int result = (*function_to_do)( i << 7 );. asm그 결과에 대한 진술을 사용할 수 있습니다.
Peter Cordes 2019

@PeterCordes 다시 한번 감사드립니다. 내 게시물에는 이제 제거되지 않고 인라인 될 수 function_to_do있도록 반환 값에 대한 수정 사항이 포함되어 있습니다 function_to_do. 추가 제안 사항이 있으면 알려주십시오.
Jack Giffin

1

코드가 실행될 때마다 동일한 시간을 측정하려는 경우 (예 : 병목 현상이 있다고 생각하는 코드를 프로파일 링하는 경우), 여기에 Andreas Bonini의 기능에 대한 래퍼가 있습니다 (약간 수정).

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#endif

/*
 *  A simple timer class to see how long a piece of code takes. 
 *  Usage:
 *
 *  {
 *      static Timer timer("name");
 *
 *      ...
 *
 *      timer.start()
 *      [ The code you want timed ]
 *      timer.stop()
 *
 *      ...
 *  }
 *
 *  At the end of execution, you will get output:
 *
 *  Time for name: XXX seconds
 */
class Timer
{
public:
    Timer(std::string name, bool start_running=false) : 
        _name(name), _accum(0), _running(false)
    {
        if (start_running) start();
    }

    ~Timer() { stop(); report(); }

    void start() {
        if (!_running) {
            _start_time = GetTimeMicroseconds();
            _running = true;
        }
    }
    void stop() {
        if (_running) {
            unsigned long long stop_time = GetTimeMicroseconds();
            _accum += stop_time - _start_time;
            _running = false;
        }
    }
    void report() { 
        std::cout<<"Time for "<<_name<<": " << _accum / 1.e6 << " seconds\n"; 
    }
private:
    // cf. http://stackoverflow.com/questions/1861294/how-to-calculate-execution-time-of-a-code-snippet-in-c
    unsigned long long GetTimeMicroseconds()
    {
#ifdef _WIN32
        /* Windows */
        FILETIME ft;
        LARGE_INTEGER li;

        /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
         *   * to a LARGE_INTEGER structure. */
        GetSystemTimeAsFileTime(&ft);
        li.LowPart = ft.dwLowDateTime;
        li.HighPart = ft.dwHighDateTime;

        unsigned long long ret = li.QuadPart;
        ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
        ret /= 10; /* From 100 nano seconds (10^-7) to 1 microsecond (10^-6) intervals */
#else
        /* Linux */
        struct timeval tv;

        gettimeofday(&tv, NULL);

        unsigned long long ret = tv.tv_usec;
        /* Adds the seconds (10^0) after converting them to microseconds (10^-6) */
        ret += (tv.tv_sec * 1000000);
#endif
        return ret;
    }
    std::string _name;
    long long _accum;
    unsigned long long _start_time;
    bool _running;
};

1

코드 블록을 벤치마킹하는 간단한 클래스 :

using namespace std::chrono;

class benchmark {
  public:
  time_point<high_resolution_clock>  t0, t1;
  unsigned int *d;
  benchmark(unsigned int *res) : d(res) { 
                 t0 = high_resolution_clock::now();
  }
  ~benchmark() { t1 = high_resolution_clock::now();
                  milliseconds dur = duration_cast<milliseconds>(t1 - t0);
                  *d = dur.count();
  }
};
// simple usage 
// unsigned int t;
// { // put the code in a block
//  benchmark bench(&t);
//  // ...
//  // code to benchmark
// }
// HERE the t contains time in milliseconds

// one way to use it can be :
#define BENCH(TITLE,CODEBLOCK) \
  unsigned int __time__##__LINE__ = 0;  \
  { benchmark bench(&__time__##__LINE__); \
      CODEBLOCK \
  } \
  printf("%s took %d ms\n",(TITLE),__time__##__LINE__);


int main(void) {
  BENCH("TITLE",{
    for(int n = 0; n < testcount; n++ )
      int a = n % 3;
  });
  return 0;
}

0

boost :: timer 는 필요한만큼의 정확도를 제공 할 것입니다. 얼마나 오래 걸릴지 말해 줄만큼 정확 a = a+1;하지는 않지만 몇 나노초가 걸리는 시간을 측정해야하는 이유는 무엇입니까?


clock()C ++ 표준 헤더 의 함수 에 의존합니다 .
Petter

0

함수 호출을 N 번 호출하고 평균을 반환하는 람다를 만들었습니다.

double c = BENCHMARK_CNT(25, fillVectorDeque(variable));

여기 에서 C ++ 11 헤더를 찾을 수 있습니다 .


0

chrono 라이브러리의 high_resolution_clock : https://github.com/nfergu/codetimer를 사용하여 코드 블록의 성능을 측정하기위한 간단한 유틸리티를 만들었습니다 .

다른 키에 대해 타이밍을 기록 할 수 있으며 각 키의 타이밍에 대한 집계보기를 표시 할 수 있습니다.

사용법은 다음과 같습니다.

#include <chrono>
#include <iostream>
#include "codetimer.h"

int main () {
    auto start = std::chrono::high_resolution_clock::now();
    // some code here
    CodeTimer::record("mykey", start);
    CodeTimer::printStats();
    return 0;
}

0

[cxx-rtimers][1]로컬 변수를 생성 할 수있는 코드 블록의 런타임에 대한 통계를 수집하기위한 일부 헤더 전용 루틴을 제공하는 GitHub 에서 볼 수도 있습니다 . 이러한 타이머에는 C ++ 11에서 std :: chrono, Boost 라이브러리의 타이머 또는 표준 POSIX 타이머 함수를 사용하는 버전이 있습니다. 이 타이머는 함수 내에서 소요 된 평균, 최대 및 최소 기간과 호출 횟수를보고합니다. 다음과 같이 간단하게 사용할 수 있습니다.

#include <rtimers/cxx11.hpp>

void expensiveFunction() {
    static rtimers::cxx11::DefaultTimer timer("expensive");
    auto scopedStartStop = timer.scopedStart();
    // Do something costly...
}

0

그렇게 많은 코드가 아니라 이해하기 쉽고 내 요구에 맞는 방법입니다.

void bench(std::function<void()> fnBench, std::string name, size_t iterations)
{
    if (iterations == 0)
        return;
    if (fnBench == nullptr)
        return;
    std::chrono::high_resolution_clock::time_point start, end;
    if (iterations == 1)
    {
        start = std::chrono::high_resolution_clock::now();
        fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    else
    {
        start = std::chrono::high_resolution_clock::now();
        for (size_t i = 0; i < iterations; ++i)
            fnBench();
        end = std::chrono::high_resolution_clock::now();
    }
    printf
    (
        "bench(*, \"%s\", %u) = %4.6lfs\r\n",
        name.c_str(),
        iterations,
        std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count()
    );
}

용법:

bench
(
    []() -> void // function
    {
        // Put your code here
    },
    "the name of this", // name
    1000000 // iterations
);

0
#include <omp.h>

double start = omp_get_wtime();

// code 

double finish = omp_get_wtime();

double total_time = finish - start;

2
이 코드가 문제를 해결할 수 있지만, 이것이 문제를 해결하는 방법과 이유에 대한 설명포함 하여 게시물의 품질을 향상시키는 데 실제로 도움이 될 것이며 아마도 더 많은 찬성표를 얻게 될 것입니다. 지금 질문하는 사람뿐만 아니라 미래에 독자를 위해 질문에 답하고 있다는 것을 기억하십시오. 제발 편집 설명을 추가하고 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을.
Dharman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.