关于优化:测量C ++中函数的执行时间

Measuring execution time of a function in C++

我想知道某个函数在我的C++程序上在Linux上执行的时间。然后,我想做一个速度比较。我看到了一些时间函数,但最后从Boost得到了这个结果。Chrono:

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

现在,我还不清楚是否使用了上述函数,我会得到CPU在该函数上花费的唯一时间吗?

其次,我找不到任何使用上述函数的例子。有人能帮我使用上述功能吗?

P.S:现在,我正在使用std::chrono::system_clock::now()以秒为单位获取时间,但由于每次CPU负载不同,这会给我带来不同的结果。


这是一个非常容易使用的方法在C++ 11中。您必须使用来自头的std::chrono::high_resolution_clock

像这样使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

void function()
{
    long long number = 0;

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

int main()
{
    high_resolution_clock::time_point t1 = high_resolution_clock::now();
    function();
    high_resolution_clock::time_point t2 = high_resolution_clock::now();

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

    cout << duration;
    return 0;
}

这将测量函数的持续时间。

注意:不需要总是获得相同的输出,因为您的计算机的CPU可以被运行在您的计算机上的其他进程使用得更少或更多。当你解决一个数学问题时,你的思想或多或少会集中起来,所以你会在不同的时间解决这个问题。在人类的头脑中,我们可以记住一个数学问题的解决方案,尽管对于一台计算机来说,相同的过程总是新的,所以,正如我所说,不需要总是得到相同的结果!


下面是一个函数,它将测量作为参数传递的任何函数的执行时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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);
}

示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include

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",'=')<<"
"
;
    std::cout<<"algo:"<<funcTime(countWithAlgorithm,"precision=10",'=');
    return 0;
}

输出:

1
2
norm: 15555
algo: 2976


查找函数执行时间所用的简单程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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.
"
, difference );

return 0;
}


在ScottMeyers的书中,我发现了一个通用通用通用lambda表达式的例子,它可以用来测量函数执行时间。(C++ 14)

1
2
3
4
5
6
7
8
9
10
auto timeFuncInvocation =
    [](auto&& func, auto&&... params) {
        // get time before function invocation
        const auto& start = 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 = high_resolution_clock::now();
        return stop - start;
     };

问题是,您只度量一个执行,因此结果可能非常不同。为了得到可靠的结果,您应该测量大量的执行。根据Andrei Alexandrescu在代码::Dive 2015会议上的演讲-快速编写代码I:

测量时间:tm=t+tq+tn+to

在哪里?

TM-测量(观察)时间

t-实际利息时间

TQ—量化噪声增加的时间

tn-各种噪声源增加的时间

到-开销时间(测量、循环、调用函数)

根据他在后面的讲座中所说的,你应该把这么多的执行作为你的结果。我鼓励你看看他解释原因的讲座。

还有一个非常好的谷歌图书馆-https://github.com/google/benchmark。这个库使用简单,功能强大。你可以在YouTube上查看钱德勒·卡拉斯的一些讲座,他在那里使用这个图书馆进行实践。例如,2017年CPPCON:Chandler Carruth"无路可走";

示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#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这样的工具在这种情况下是有用的。


老C++的简单方法,或者C:

1
2
3
4
5
6
7
8
9
10
11
12
13
#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


  • It is a very easy to use method in C++11.
  • We can use std::chrono::high_resolution_clock from header
  • We can write a method to print the method execution time in a much readable form.

例如,要找到1到1亿之间的所有素数,大约需要1分40秒。因此执行时间打印为:

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

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#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 <<"
Execution 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,";
}


下面是一个非常好的只包含头的类模板,用于测量函数或任何代码块的运行时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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

以下是它的一些用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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;
}

由于类是一个模板,因此我们可以轻松地指定希望如何测量和显示时间。这是一个非常方便的工具类模板做基准,非常容易使用。