在没有分析器的情况下在C++中测试代码速度的最佳方法,还是尝试没有意义?

8 c++ performance profiling timer

在SO上,有很多关于性能分析的问题,但我似乎没有找到整体情况.涉及到相当多的问题,大多数问答都会忽略所有问题,但不能忽视其中的一些问题.

我想知道什么.如果我有两个功能做同样的事情,我很好奇速度的差异,没有外部工具,定时器测试这个是否有意义,或者在测试中编译会影响结果吗?

我问这个是因为如果它是明智的,作为一个C++程序员,我想知道它应该如何做到最好,因为它们比使用外部工具简单得多.如果它有意义,让我们继续讨论所有可能的陷阱:

考虑这个例子.以下代码显示了执行相同操作的两种方法:

#include <algorithm>
#include <ctime>
#include <iostream>

typedef unsigned char byte;

inline
void
swapBytes( void* in, size_t n )
{
   for( size_t lo=0, hi=n-1; hi>lo; ++lo, --hi )

      in[lo] ^= in[hi]
   ,  in[hi] ^= in[lo]
   ,  in[lo] ^= in[hi] ;
}

int
main()
{
         byte    arr[9]     = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
   const int     iterations = 100000000;
         clock_t begin      = clock();

   for( int i=iterations; i!=0; --i ) 

      swapBytes( arr, 8 );

   clock_t middle = clock();

   for( int i=iterations; i!=0; --i ) 

      std::reverse( arr, arr+8 );

   clock_t end = clock();

   double secSwap = (double) ( middle-begin ) / CLOCKS_PER_SEC;
   double secReve = (double) ( end-middle   ) / CLOCKS_PER_SEC;


   std::cout << "swapBytes,    for:    "   << iterations << " times takes: " << middle-begin
             << " clock ticks, which is: " << secSwap    << "sec."           << std::endl;

   std::cout << "std::reverse, for:    "   << iterations << " times takes: " << end-middle
             << " clock ticks, which is: " << secReve    << "sec."           << std::endl;

   std::cin.get();
   return 0;
}

// Output:

// Release:
//  swapBytes,    for: 100000000 times takes: 3000 clock ticks, which is: 3sec.
//  std::reverse, for: 100000000 times takes: 1437 clock ticks, which is: 1.437sec.

// Debug:
//  swapBytes,    for: 10000000 times takes: 1781  clock ticks, which is: 1.781sec.
//  std::reverse, for: 10000000 times takes: 12781 clock ticks, which is: 12.781sec.
Run Code Online (Sandbox Code Playgroud)

问题:

  1. 使用哪些计时器以及如何获得所讨论的代码实际消耗的CPU时间?
  2. 编译器优化有什么影响(因为这些函数只是来回交换字节,最有效的事情显然是什么都不做)?
  3. 考虑到此处提供的结果,您认为它们是否准确(我可以向您保证多次运行会得到非常相似的结果)?如果是的话,考虑到自定义函数的简单性,你能解释一下std :: reverse如何变得如此之快.我没有用于此测试的vc ++版本的源代码,但这里是 GNU 的实现.它归结为iter_swap函数,这对我来说是完全不可理解的.预计这也会比自定义函数快两倍,如果是这样,为什么呢?

沉思:

  1. 似乎提出了两个高精度计时器:clock()QueryPerformanceCounter(在windows上).显然我们想测量代码的cpu时间而不是实时,但据我所知,这些函数没有提供该功能,因此系统上的其他进程会干扰测量.gnu c库上的这个页面似乎与此相矛盾,但是当我在vc ++中放置一个断点时,调试过程会获得大量的时钟滴答,即使它被挂起(我还没有在gnu下测试过).我是否遗漏了替代计数器,或者我们是否至少需要特殊的库或类?如果没有,在这个例子中时钟是否足够好或者是否有理由使用QueryPerformanceCounter?

  2. 没有调试,反汇编和分析工具,我们可以确定什么?有什么事实发生吗?函数调用是否内联?在检查调试器时,字节实际上是交换的,但我宁愿从理论上知道为什么,而不是从测试.

谢谢你的任何指示.

更新

由于来自tojas提示,swapBytes函数现在运行速度与std :: reverse一样快.我没有意识到在一个字节的情况下临时副本必须只是一个寄存器,因此非常快.优雅可以使你失明.

inline
void
swapBytes( byte* in, size_t n )
{
   byte t;

   for( int i=0; i<7-i; ++i )
    {
        t       = in[i];
        in[i]   = in[7-i];
        in[7-i] = t;
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢ChrisW的一个提示,我发现在Windows上你可以通过Windows Management Instrumentation获得(读取:你的)进程所消耗的实际cpu时间.这绝对看起来比高精度计数器更有趣.

Chr*_*isW 5

显然,我们想测量我们代码的 CPU 时间而不是实时时间,但据我所知,这些函数没有提供该功能,因此系统上的其他进程会干扰测量。

我做了两件事,以确保挂钟时间和 CPU 时间大致相同:

  • 测试相当长的时间,即几秒钟(例如,通过测试数千次迭代的循环)

  • 在机器或多或少相对空闲时进行测试,除了我正在测试的任何内容。

或者,如果您只想/更准确地测量每个线程的 CPU 时间,则可以将其用作性能计数器(参见例如perfmon.exe)。

如果没有调试、反汇编和分析工具,我们能确定什么?

几乎没有(除了 I/O 往往相对较慢)。