【问题标题】:What is the best way to time how long functions take in C++? [duplicate]在 C++ 中计算函数需要多长时间的最佳方法是什么? [复制]
【发布时间】:2011-01-18 20:42:16
【问题描述】:

在 C# 中,我会启动 Stopwatch 类来快速确定某些方法需要多长时间。

在 C++ 中这个等价物是什么?有内置高精度定时器吗?

【问题讨论】:

    标签: c++ timing


    【解决方案1】:

    我之前已经为这样的情况实现了一个计时器:实际上,我最终得到了一个具有两种不同实现的类,一种用于 Windows,一种用于 POSIX。

    原因是 Windows 具有 QueryPerformanceCounter() 功能,它使您可以访问非常准确的时钟,这是此类计时的理想选择。

    在 POSIX 上,但是这不可用,所以我只是使用 boost.datetime 的类来存储开始和结束时间,然后从中计算持续时间。它提供了一个“高分辨率”计时器,但分辨率未定义,并且因平台而异。

    【讨论】:

      【解决方案2】:

      我使用boost::timer 来测量操作的持续时间。它提供了一种非常简单的测量方法,同时独立于平台。这是一个例子:

      boost::timer myTimer;
      doOperation();
      std::cout << myTimer.elapsed();
      

      附:为了克服精度误差,测量需要几秒钟的操作会很棒。特别是当您尝试比较几种替代方案时。如果您想测量花费很少时间的东西,请尝试将其放入循环中。例如运行操作 1000 次,然后将总时间除以 1000。

      【讨论】:

        【解决方案3】:

        我使用我自己版本的 Python 的 time_it 函数。此函数的优点是它可以根据需要重复计算多次以获得有意义的结果。如果计算速度很快,就会重复很多次。最后,您获得所有重复的平均时间。它不使用任何非标准功能:

        #include <ctime>
        
        double clock_diff_to_sec(long clock_diff)
        {
            return double(clock_diff) / CLOCKS_PER_SEC;
        }
        
        template<class Proc>
        double time_it(Proc proc, int N=1) // returns time in microseconds
        {   
            std::clock_t const start = std::clock();
            for(int i = 0; i < N; ++i)
                proc();
            std::clock_t const end = std::clock(); 
            if(clock_diff_to_sec(end - start) < .2) 
                return time_it(proc, N * 5); 
            return clock_diff_to_sec(end - start) * (1e6 / N);
        }
        

        以下示例使用time_it函数来衡量不同STL容器的性能:

        void dummy_op(int i)
        {
            if(i == -1)
                std::cout << i << "\n";
        }
        
        template<class Container>
        void test(Container const & c)
        {
            std::for_each(c.begin(), c.end(), &dummy_op);
        }
        
        template<class OutIt>
        void init(OutIt it)
        {
            for(int i = 0; i < 1000; ++i)
                *it = i;
        }
            
        int main( int argc, char ** argv )
        {
            {
                std::vector<int> c;
                init(std::back_inserter(c));
                std::cout << "vector: " 
                          << time_it(boost::bind(&test<std::vector<int> >, c)) << "\n";
            }      
            {
                std::list<int> c;
                init(std::back_inserter(c));
                std::cout << "list: "
                          << time_it(boost::bind(&test<std::list<int> >, c)) << "\n";
            }
            {
                std::deque<int> c;
                init(std::back_inserter(c));
                std::cout << "deque: " 
                          << time_it(boost::bind(&test<std::deque<int> >, c)) << "\n";
            }
            {
                std::set<int> c;
                init(std::inserter(c, c.begin()));
                std::cout << "set: " 
                          << time_it(boost::bind(&test<std::set<int> >, c)) << "\n";
            }
            {
                std::tr1::unordered_set<int> c;
                init(std::inserter(c, c.begin()));
                std::cout << "unordered_set: " 
                   << time_it(boost::bind(&test<std::tr1::unordered_set<int> >, c)) << "\n";
            }    
        }
        

        如果有人好奇,这里是我得到的输出(在发布模式下使用 VS2008 编译):

        矢量:8.7168

        列表:27.776

        双端队列:91.52

        设置:103.04

        无序集:29.76

        【讨论】:

        • 非常有趣。无论是计时功能还是对不同容器的洞察!谢谢。
        • @Morlock - 谢谢!我会花一点时间来把握时间,std::deque 在这次测试中表现得很糟糕,但我确信在一般情况下它还不错。
        【解决方案4】:

        您可以使用ctime library 获取以秒为单位的时间。以毫秒为单位获取时间是特定于实现的。 Here is a discussion 探索一些方法来做到这一点。

        另见:How to measure time in milliseconds using ANSI C?

        【讨论】:

          【解决方案5】:

          高精度计时器是特定于平台的,因此 C++ 标准未指定,但有可用的库。请参阅this question 进行讨论。

          【讨论】:

          【解决方案6】:

          我谦虚地提交我自己的micro-benchmarking mini-library (on Github)。它超级简单——它比自己滚动的唯一优势是它已经为 Windows 和 Linux 实现了高性能计时器代码,并抽象出烦人的样板。

          只需传入一个函数(或 lambda)、每次测试运行应调用的次数(默认值:1)和测试运行次数(默认值:100)。返回最快的测试运行(以毫秒为单位):

          // Example that times the compare-and-swap atomic operation from C++11
          // Sample GCC command: g++ -std=c++11 -DNDEBUG -O3 -lrt main.cpp microbench/systemtime.cpp -o bench
          #include "microbench/microbench.h"
          
          #include <cstdio>
          #include <atomic>
          
          int main()
          {
              std::atomic<int> x(0);
              int y = 0;
          
              printf("CAS takes %.4fms to execute 100000 iterations\n",
                  moodycamel::microbench(
                      [&]() { x.compare_exchange_strong(y, 0); },  /* function to benchmark */
                      100000, /* iterations per test run */
                      100 /* test runs */
                  )
              );
          
              // Result: Clocks in at 1.2ms (12ns per CAS operation) in my environment
          
              return 0;
          }
          

          【讨论】:

            【解决方案7】:
            #include <time.h>
            
            clock_t start, end;
            start = clock();
            //Do stuff
            end = clock();
            
            printf("Took: %f\n", (float)((end - start) / (float)CLOCKS_PER_SEC));
            

            【讨论】:

            • 这可行,但分辨率会很差。
            • 这实际上不适用于测量绝对时间,因为clock() 只返回 your 代码占用的 CPU 时间。因此,如果将 \\Do stuff 替换为 usleep(1000000),printf 将返回 0.0000 秒而不是 1 秒。
            【解决方案8】:

            这可能是依赖于操作系统的问题,而不是语言问题。

            如果您使用的是 Windows,则可以通过 GetTickCount() 或 GetTickCount64() 访问 毫秒 10 到 16 毫秒的计时器。只需在开始时调用一次,在结束时调用一次,然后减去。

            如果我没记错的话,那是我以前用过的。链接页面还有其他选项。

            【讨论】:

            • GetTickCount() 是在底层使用 QueryPerformanceCounter() 还是?
            • 我不知道。我添加了一个指向 GetTickCount() 页面的链接,根据那里的内容,您似乎还有其他可能更好的选择。
            • 没有。 GetTickCount() 不准确。如果你想要一个准确的计数,你必须使用 QueryPerformanceCounter() (编辑:不准确我的意思是 +/- 10 毫秒)
            • BillyONeal:如果你执行一个函数的次数足够多,你就可以使用挂钟来衡量性能。
            【解决方案9】:

            您可以找到有用的this 类。

            使用 RAII 习语,它在调用析构函数时打印构造中给出的文本,用适当的值填充经过时间的占位符。

            使用示例:

            int main()
            {
               trace_elapsed_time t("Elapsed time: %ts.\n");
               usleep(1.005 * 1e6);
            } 
            

            输出:

            Elapsed time: 1.00509s.
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2010-10-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-11-13
              • 1970-01-01
              • 2017-01-11
              相关资源
              最近更新 更多