C ++에서 함수의 실행 시간 측정


138

C ++ 프로그램에서 특정 함수가 Linux 에서 실행되는 데 걸리는 시간을 알고 싶습니다 . 그 후 속도 비교를하고 싶습니다. 나는 몇 가지 시간 기능을 보았지만 부스트에서 이것으로 끝났습니다. 크로노 :

process_user_cpu_clock, captures user-CPU time spent by the current process

이제 위의 기능을 사용하는지 확실하지 않습니다. CPU가 해당 기능에 소비 한 유일한 시간을 얻을 수 있습니까?

둘째, 위의 기능을 사용하는 예를 찾지 못했습니다. 위의 기능을 사용하는 방법을 알려주시겠습니까?

추신 : 지금 std::chrono::system_clock::now()은 초 단위로 시간을 사용 하고 있지만 매번 다른 CPU로드로 인해 다른 결과를 얻습니다.


2
Linux 사용 : clock_gettime.. gcc는 다른 시계를 다음과 같이 정의 typedef system_clock steady_clock; typedef system_clock high_resolution_clock;합니다 QueryPerformanceCounter. Windows에서는을 사용하십시오 .
Brandon

이 질문의 중복 아닌가요 이 하나 또는 시나리오의 솔루션은 서로 다른 어떻게해야합니까?
북부의

두 가지 기능 구현이 있으며 어떤 기능이 더 나은지 알고 싶습니다.
북부의

매우 중요합니다 : 최적화를 활성화하십시오 . 최적화 되지 않은 코드는 일반적인 최적화 된 코드와 는 다른 병목 현상이 있으며 의미있는 내용 이 없습니다 . 최종 할당을위한 C 루프 최적화 도움말 (컴파일러 최적화가 비활성화 된 상태) . 그리고 일반적으로 마이크로 벤치마킹에는 많은 함정이 있습니다. 특히 CPU 주파수 및 페이지 오류에 대해 예열 루프를 먼저 수행하지 못하는 경우 : 관용적 인 성능 평가 방법? . 그리고이 답변
Peter Cordes

함수의 성능을 어떻게 벤치마킹 하시겠습니까?를 참조하십시오 . Google 벤치 마크를 사용하면 자신의 마이크로 벤치 마크를 굴리는 데 따르는 많은 위험을 피할 수 있습니다. 또한 Simple for () 루프 벤치 마크는 최적화가 벤치 마크 루프와 상호 작용하는 방법과 그에 대한 조치에 대해 루프 바운드와 동일한 시간이 걸립니다 .
Peter Cordes

답변:


264

C ++ 11에서 사용하기 매우 쉬운 방법입니다. 당신은 사용해야 std::chrono::high_resolution_clock에서 <chrono>헤더입니다.

다음과 같이 사용하십시오.

#include <iostream>
#include <chrono>

void function()
{
    long long number = 0;

    for( long long i = 0; i != 2000000; ++i )
    {
       number += 5;
    }
}

int main()
{
    auto t1 = std::chrono::high_resolution_clock::now();
    function();
    auto t2 = std::chrono::high_resolution_clock::now();

    auto duration = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count();

    std::cout << duration;
    return 0;
}

기능의 지속 시간을 측정합니다.

참고 : 기능의 타이밍이 항상 같은 것은 아닙니다. 이는 수학 연습을 풀 때 마음이 다소 집중 될 수있는 것처럼 컴퓨터의 CPU가 컴퓨터에서 실행중인 다른 프로세스에 의해 사용되거나 사용되지 않을 수 있기 때문입니다. 인간의 마음에는 수학 문제의 해결책을 기억할 수 있지만 컴퓨터의 경우 동일한 프로세스가 항상 새로운 것입니다. 따라서 내가 말했듯이 항상 동일한 결과를 얻지는 못할 것입니다!


이 기능을 사용할 때 처음 실행하면 118440535 마이크로 초가되었고 동일한 기능의 두 번째 실행에서는 83221031 마이크로 초가되었습니다. 해당 기능의 지속 시간 만 측정 할 때 두 시간 측정 값이 동일하지 않아야합니까?
Xara

1
아니요. 컴퓨터의 프로세서를 적게 또는 더 많이 사용할 수 있습니다. 는 high_resolution_clock함수 실행에 필요한 실제 및 실시간을 제공합니다. 따라서 첫 실행에서 CPU가 다음 실행보다 적게 사용되었습니다. "사용"이란 다른 응용 프로그램 작업에서 CPU를 사용하는 것을 의미합니다.
Victor

1
그렇습니다. 평균 시간이 필요한 경우이를 얻는 좋은 방법입니다. 세 번 뛰고 평균을 계산하십시오.
Victor

3
일반적으로 "네임 스페이스 사용"없이 코드를 게시 할 수 있습니까? 어디에서 오는지 쉽게 볼 수 있습니다.
눈사람

1
이것이 steady_clock아닌가? 그것은 가능하지 high_resolution_clock않은 단순 클럭을 할 수 있을까?
Gillespie

15

다음은 인수로 전달 된 함수의 실행 시간을 측정하는 함수입니다.

#include <chrono>
#include <utility>

typedef std::chrono::high_resolution_clock::time_point TimeVar;

#define duration(a) std::chrono::duration_cast<std::chrono::nanoseconds>(a).count()
#define timeNow() std::chrono::high_resolution_clock::now()

template<typename F, typename... Args>
double funcTime(F func, Args&&... args){
    TimeVar t1=timeNow();
    func(std::forward<Args>(args)...);
    return duration(timeNow()-t1);
}

사용법 예 :

#include <iostream>
#include <algorithm>

typedef std::string String;

//first test function doing something
int countCharInString(String s, char delim){
    int count=0;
    String::size_type pos = s.find_first_of(delim);
    while ((pos = s.find_first_of(delim, pos)) != String::npos){
        count++;pos++;
    }
    return count;
}

//second test function doing the same thing in different way
int countWithAlgorithm(String s, char delim){
    return std::count(s.begin(),s.end(),delim);
}


int main(){
    std::cout<<"norm: "<<funcTime(countCharInString,"precision=10",'=')<<"\n";
    std::cout<<"algo: "<<funcTime(countWithAlgorithm,"precision=10",'=');
    return 0;
}

산출:

norm: 15555
algo: 2976

2
@ RestlessC0bra : 구현이 정의되어 high_resolution_clock있으며 system_clock(벽 시계) 의 별칭 steady_clock이거나 세 번째 독립 시계 일 수 있습니다. 자세한 내용은 여기를 참조하십시오 . CPU 시계를 std::clock위해 사용될 수 있습니다
Jahid

2
두 개의 매크로와 전역 유형 정의-단일 키 입력으로 안전하지 않은 것은 확실히 우아하지 않습니다. 함수 객체를 전달하고 인수를 개별적으로 전달하는 것은 약간의 과잉입니다 (과도한 함수조차도 불편한), 시간 코드를 람다에 넣으면됩니다. 그러나 인수를 전달하는 것이 선택적인 한 좋습니다.
MikeMB

2
그리고 이것은 매크로의 이름 지정에 관한 모든 지침을 위반 한 이유입니까? 접두사를 사용하지 않고 대문자를 사용하지 않고 일부 지역 기호와 충돌 할 가능성이 높은 매우 일반적인 이름을 선택합니다. 왜 함수 대신 매크로를 사용합니까? )? 그리고 우리가 그 동안 : 왜 당신은 왜 지속 시간을 나노 초를 두 배로 반환합니까? 우리는 동의하지 않을 것에 동의해야합니다. 나의 원래 의견은 "이것은 내가 우아한 코드라고 부르는 것이 아니다"입니다.
MikeMB

1
문제는 범위가 지정되지 않았다는 것입니다. 내가 걱정하는 것은 그러한 매크로가 내 코드에 포함 된 (라이브러리의 일부로 간접적으로) 헤더 파일로 끝나는 것입니다. 일반 이름은 매크로에 사용되며 windows.h사소한 c ++ 프로젝트에 포함됩니다. 관해서 assert우선 "감옥 licet iovi 비 licet BOVI"). 둘째, 표준 라이브러리 (때로는 수십 년 전)의 모든 결정이 실제로 현대 표준에 의해 좋은 아이디어로 간주되는 것은 아닙니다. 왜 C ++ 모듈 디자이너가 기본적으로 매크로를 내 보내지 않으려 고 노력하는지에 대한 이유가 있습니다.
MikeMB

2
@Jahid : 감사합니다. 이 경우 내 의견이 void 및 null이라고 생각하십시오.
MikeMB

9

함수 실행 시간을 찾는 간단한 프로그램.

#include <iostream>
#include <ctime> // time_t
#include <cstdio>

void function()
{
     for(long int i=0;i<1000000000;i++)
     {
        // do nothing
     }
}

int main()
{

time_t begin,end; // time_t is a datatype to store time values.

time (&begin); // note time before execution
function();
time (&end); // note time after execution

double difference = difftime (end,begin);
printf ("time taken for function() %.2lf seconds.\n", difference );

return 0;
}

6
매우 정확하지 않으며 초만 표시하지만 밀리 초는 표시하지 않음
user25

7

Scott Meyers 책에서 함수 실행 시간을 측정하는 데 사용할 수있는 범용 일반 람다 식의 예를 찾았습니다. (C ++ 14)

auto timeFuncInvocation = 
    [](auto&& func, auto&&... params) {
        // get time before function invocation
        const auto& start = std::chrono::high_resolution_clock::now();
        // function invocation using perfect forwarding
        std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
        // get time after function invocation
        const auto& stop = std::chrono::high_resolution_clock::now();
        return stop - start;
     };

문제는 하나의 실행 만 측정하므로 결과가 매우 다를 수 있다는 것입니다. 안정적인 결과를 얻으려면 많은 실행을 측정해야합니다. 코드 : : 다이브 2015 컨퍼런스에서 Andrei Alexandrescu 강의에 따르면-빠른 코드 작성 :

측정 시간 : tm = t + tq + tn + to

어디:

tm-측정 된 (관찰 된) 시간

t-실제 관심 시간

tq-양자화 노이즈로 추가 된 시간

tn-다양한 소음원에 의해 추가 된 시간

-오버 헤드 시간 (측정, 루핑, 호출 함수)

그가 강의 후반에 말한 것에 따르면, 당신은 그 결과로이 많은 수의 집행을 최소한으로 취해야합니다. 그 이유를 설명하는 강의를 살펴 보시기 바랍니다.

또한 google- https ://github.com/google/benchmark의 매우 훌륭한 라이브러리가 있습니다 . 이 라이브러리는 사용하기 매우 간단하고 강력합니다. Chandler Carruth의 강의를 YouTube에서 실제로이 라이브러리를 사용하고있는 YouTube에서 확인할 수 있습니다. 예를 들어 CppCon 2017 : Chandler Carruth“가는 곳이 더 빠릅니다”;

사용법 예 :

#include <iostream>
#include <chrono>
#include <vector>
auto timeFuncInvocation = 
    [](auto&& func, auto&&... params) {
        // get time before function invocation
        const auto& start = high_resolution_clock::now();
        // function invocation using perfect forwarding
        for(auto i = 0; i < 100000/*largeNumber*/; ++i) {
            std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
        }
        // get time after function invocation
        const auto& stop = high_resolution_clock::now();
        return (stop - start)/100000/*largeNumber*/;
     };

void f(std::vector<int>& vec) {
    vec.push_back(1);
}

void f2(std::vector<int>& vec) {
    vec.emplace_back(1);
}
int main()
{
    std::vector<int> vec;
    std::vector<int> vec2;
    std::cout << timeFuncInvocation(f, vec).count() << std::endl;
    std::cout << timeFuncInvocation(f2, vec2).count() << std::endl;
    std::vector<int> vec3;
    vec3.reserve(100000);
    std::vector<int> vec4;
    vec4.reserve(100000);
    std::cout << timeFuncInvocation(f, vec3).count() << std::endl;
    std::cout << timeFuncInvocation(f2, vec4).count() << std::endl;
    return 0;
}

편집 : 물론 컴파일러는 무언가를 최적화 할 수 있다는 것을 항상 기억해야합니다. 이러한 경우 perf와 같은 도구가 유용 할 수 있습니다.


흥미 롭다-함수 템플릿에 람다를 사용하면 어떤 이점이 있습니까?
user48956

1
주요 차이점은 호출 가능한 객체이지만 실제로는 가변 템플릿 및 std :: result_of_t와 매우 유사한 것을 얻을 수 있다는 것입니다.
Krzysztof Sommerfeld

@KrzysztofSommerfeld 타이밍 (Object.Method1)을 통과하면 함수 메서드에 대해이 작업을 수행하는 방법 오류 "비표준 구문, '&'를 사용하여 멤버에 대한 포인터 만들기"오류를 반환합니다.
RobinAtTech

timeFuncInvocation ([& objectName] (auto && ... args) {objectName.methodName (std :: forward <decltype (args)> (args) ...);}, arg1, arg2, ...); 또는 objectName 앞에 생략 및 서명 (그러면 객체의 사본을 갖게 됨)
Krzysztof Sommerfeld

4

이전 C ++ 또는 C를위한 쉬운 방법 :

#include <time.h> // includes clock_t and CLOCKS_PER_SEC

int main() {

    clock_t start, end;

    start = clock();
    // ...code to measure...
    end = clock();

    double duration_sec = double(end-start)/CLOCKS_PER_SEC;
    return 0;
}

초 단위의 타이밍 정밀도는 1.0/CLOCKS_PER_SEC


1
이것은 휴대용이 아닙니다. Linux의 프로세서 시간과 Windows의 클럭 시간을 측정합니다.
BugSquasher 2016 년

2
  • C ++ 11에서 메소드를 사용하는 것은 매우 쉽습니다.
  • 헤더에서 std :: chrono :: high_resolution_clock을 사용할 수 있습니다
  • 메소드 실행 시간을 훨씬 읽기 쉬운 형식으로 인쇄하는 메소드를 작성할 수 있습니다.

예를 들어 1에서 1 억 사이의 모든 소수를 찾으려면 약 1 분 40 초가 걸립니다. 따라서 실행 시간은 다음과 같이 인쇄됩니다.

Execution Time: 1 Minutes, 40 Seconds, 715 MicroSeconds, 715000 NanoSeconds

코드는 다음과 같습니다.

#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

typedef high_resolution_clock Clock;
typedef Clock::time_point ClockTime;

void findPrime(long n, string file);
void printExecutionTime(ClockTime start_time, ClockTime end_time);

int main()
{
    long n = long(1E+8);  // N = 100 million

    ClockTime start_time = Clock::now();

    // Write all the prime numbers from 1 to N to the file "prime.txt"
    findPrime(n, "C:\\prime.txt"); 

    ClockTime end_time = Clock::now();

    printExecutionTime(start_time, end_time);
}

void printExecutionTime(ClockTime start_time, ClockTime end_time)
{
    auto execution_time_ns = duration_cast<nanoseconds>(end_time - start_time).count();
    auto execution_time_ms = duration_cast<microseconds>(end_time - start_time).count();
    auto execution_time_sec = duration_cast<seconds>(end_time - start_time).count();
    auto execution_time_min = duration_cast<minutes>(end_time - start_time).count();
    auto execution_time_hour = duration_cast<hours>(end_time - start_time).count();

    cout << "\nExecution Time: ";
    if(execution_time_hour > 0)
    cout << "" << execution_time_hour << " Hours, ";
    if(execution_time_min > 0)
    cout << "" << execution_time_min % 60 << " Minutes, ";
    if(execution_time_sec > 0)
    cout << "" << execution_time_sec % 60 << " Seconds, ";
    if(execution_time_ms > 0)
    cout << "" << execution_time_ms % long(1E+3) << " MicroSeconds, ";
    if(execution_time_ns > 0)
    cout << "" << execution_time_ns % long(1E+6) << " NanoSeconds, ";
}

0

다음은 함수 또는 코드 블록의 경과 시간을 측정하기위한 우수한 헤더 전용 클래스 템플릿입니다.

#ifndef EXECUTION_TIMER_H
#define EXECUTION_TIMER_H

template<class Resolution = std::chrono::milliseconds>
class ExecutionTimer {
public:
    using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
                                     std::chrono::high_resolution_clock,
                                     std::chrono::steady_clock>;
private:
    const Clock::time_point mStart = Clock::now();

public:
    ExecutionTimer() = default;
    ~ExecutionTimer() {
        const auto end = Clock::now();
        std::ostringstream strStream;
        strStream << "Destructor Elapsed: "
                  << std::chrono::duration_cast<Resolution>( end - mStart ).count()
                  << std::endl;
        std::cout << strStream.str() << std::endl;
    }    

    inline void stop() {
        const auto end = Clock::now();
        std::ostringstream strStream;
        strStream << "Stop Elapsed: "
                  << std::chrono::duration_cast<Resolution>(end - mStart).count()
                  << std::endl;
        std::cout << strStream.str() << std::endl;
    }

}; // ExecutionTimer

#endif // EXECUTION_TIMER_H

다음은 그 사용법입니다.

int main() {
    { // empty scope to display ExecutionTimer's destructor's message
         // displayed in milliseconds
         ExecutionTimer<std::chrono::milliseconds> timer;

         // function or code block here

         timer.stop();

    } 

    { // same as above
        ExecutionTimer<std::chrono::microseconds> timer;

        // code block here...

        timer.stop();
    }

    {  // same as above
       ExecutionTimer<std::chrono::nanoseconds> timer;

       // code block here...

       timer.stop();

    }

    {  // same as above
       ExecutionTimer<std::chrono::seconds> timer;

       // code block here...

       timer.stop();

    }              

    return 0;
}

수업은 템플릿이기 때문에 시간을 측정하고 표시하는 방법을 쉽게 지정할 수 있습니다. 이것은 벤치 마킹을 수행하기위한 매우 유용한 유틸리티 클래스 템플릿이며 사용하기 매우 쉽습니다.


stop()소멸자가 타이머를 중지하기 때문에 개인적으로 멤버 함수는 필요하지 않습니다.
Casey

@Casey 클래스의 디자인은 반드시 정지 기능을 필요로하지는 않지만, 특정한 이유가 있습니다. test code타이머를 시작 하기 전에 객체를 만들 때의 기본 구성 입니다. 그런 다음 test code타이머 객체를 명시 적으로 사용하고 stop 메소드를 호출하십시오. stop타이머 를 원할 때 수동으로 호출해야 합니다. 클래스는 매개 변수를 사용하지 않습니다. 당신은 내가 보여준 것처럼이 클래스를 사용 또한 경우이 호출 사이의 시간의 최소 경과가 있음을 볼 수 obj.stop와 그 destructor.
Francis Cugler

@Casey ... 또한 동일한 범위 내에 여러 타이머 객체를 가질 수 있습니다. 실제로 필요한 것이 아니라 또 다른 실행 가능한 옵션입니다.
Francis Cugler

이 예제는 제시된 형식으로 컴파일 할 수 없습니다. 오류는 "operator << ..."와 일치하지 않습니다!
Celdor

@ 셀 도르에는 적절한 포함이 필요합니다; 같은 <chrono>?
Francis Cugler 19

0

steady_clock과 달리 단조로운 것으로 보증되는 것을 사용 하는 것이 좋습니다 high_resolution_clock.

#include <iostream>
#include <chrono>

using namespace std;

unsigned int stopwatch()
{
    static auto start_time = chrono::steady_clock::now();

    auto end_time = chrono::steady_clock::now();
    auto delta    = chrono::duration_cast<chrono::microseconds>(end_time - start_time);

    start_time = end_time;

    return delta.count();
}

int main() {
  stopwatch(); //Start stopwatch
  std::cout << "Hello World!\n";
  cout << stopwatch() << endl; //Time to execute last line
  for (int i=0; i<1000000; i++)
      string s = "ASDFAD";
  cout << stopwatch() << endl; //Time to execute for loop
}

산출:

Hello World!
62
163514

0

이런 종류의 측정에 사용할 수있는 간단한 클래스를 가질 수 있습니다.

class duration_printer {
public:
    duration_printer() : __start(std::chrono::high_resolution_clock::now()) {}
    ~duration_printer() {
        using namespace std::chrono;
        high_resolution_clock::time_point end = high_resolution_clock::now();
        duration<double> dur = duration_cast<duration<double>>(end - __start);
        std::cout << dur.count() << " seconds" << std::endl;
    }
private:
    std::chrono::high_resolution_clock::time_point __start;
};

그 함수의 시작 부분에 함수에서 객체를 생성하는 것이 필요합니다.

void veryLongExecutingFunction() {
    duration_calculator dc;
    for(int i = 0; i < 100000; ++i) std::cout << "Hello world" << std::endl;
}

int main() {
    veryLongExecutingFunction();
    return 0;
}

그리고 그게 다야. 요구 사항에 맞게 수업을 수정할 수 있습니다.


0

제공된 답변 중 어느 것도 매우 정확하지 않거나 재현 가능한 결과를 제공하지 않기 때문에 나노 초 이하의 정밀도와 과학적 통계가있는 코드에 대한 링크를 추가하기로 결정했습니다.

이것은 (매우) 짧은 실행 시간 (일명, 몇 번의 클럭 사이클에서 수천 번)이 걸리는 코드를 측정하는 경우에만 작동합니다. 그러므로 재현 가능하고 정확한 결과를 제공하는 것은 불가능합니다. 그 결과 측정이 끝나지 않습니다. 즉, 통계적으로 99.9 %가 될 때까지 계속 측정하여 코드가 너무 오래 걸릴 때 다른 프로세스가 실행되고있는 기계에서는 발생하지 않는 정답을 얻을 수 있습니다.

https://github.com/CarloWood/cwds/blob/master/benchmark.h#L40


0

시간과 코드 라인을 안전하게 유지하려면 함수 실행 시간을 한 줄 매크로로 측정 할 수 있습니다.

a) 위에서 이미 제안한 시간 측정 클래스를 구현하십시오 (여기서는 안드로이드 구현입니다).

class MeasureExecutionTime{
private:
    const std::chrono::steady_clock::time_point begin;
    const std::string caller;
public:
    MeasureExecutionTime(const std::string& caller):caller(caller),begin(std::chrono::steady_clock::now()){}
    ~MeasureExecutionTime(){
        const auto duration=std::chrono::steady_clock::now()-begin;
        LOGD("ExecutionTime")<<"For "<<caller<<" is "<<std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()<<"ms";
    }
};

b) 현재 함수 이름을 TAG로 사용하는 편리한 매크로를 추가합니다 (매크로를 사용하는 것이 중요합니다. 그렇지 않으면 측정하려는 함수 대신 __FUNCTION__평가 함).MeasureExecutionTime

#ifndef MEASURE_FUNCTION_EXECUTION_TIME
#define MEASURE_FUNCTION_EXECUTION_TIME const MeasureExecutionTime measureExecutionTime(__FUNCTION__);
#endif

c) 측정하고자하는 기능의 시작 부분에 매크로를 작성하십시오. 예:

 void DecodeMJPEGtoANativeWindowBuffer(uvc_frame_t* frame_mjpeg,const ANativeWindow_Buffer& nativeWindowBuffer){
        MEASURE_FUNCTION_EXECUTION_TIME
        // Do some time-critical stuff 
}

결과는 다음과 같습니다.

ExecutionTime: For DecodeMJPEGtoANativeWindowBuffer is 54ms

이것은 (다른 모든 제안 된 솔루션과 마찬가지로) 함수가 호출 된 시점과 반환 된 시점 사이의 시간을 측정하지만 CPU가 함수를 실행 한 시간은 필요하지 않습니다. 그러나 스케줄러에 sleep () 또는 이와 유사한 기능을 호출하여 실행 코드를 일시 중단하도록 변경하지 않으면 차이가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.