如何比较两段代码的性能



我和编程领域的几个人进行了一场友好的竞争,最近我们对编写高效的代码非常感兴趣。我们面临的挑战是尝试以任何代价(可读性、可重用性等)优化代码(从cpu时间和复杂性的角度来看)。

问题是,现在我们需要比较我们的代码,看看哪种方法比其他方法更好,但我们不知道有什么工具可以达到这个目的。

我的问题是,有没有一些(任何!)工具需要一段代码作为输入,并计算触发器或cpu指令的数量运行它所必需的?有没有什么工具可以测量最优代码?

附言:目标语言是c++,但如果java也有这样的工具,那就太好了。

这里有一个我喜欢在需要计时时推出的C++11秒表:

#include <chrono>
#include <ctime>
template <typename T> class basic_stopwatch
{
    typedef T clock;
    typename clock::time_point p;
    typename clock::duration   d;
public:
    void tick()  { p  = clock::now();            }
    void tock()  { d += clock::now() - p;        }
    void reset() { d  = clock::duration::zero(); }
    template <typename S> unsigned long long int report() const
    {
        return std::chrono::duration_cast<S>(d).count();
    }
    unsigned long long int report_ms() const
    {
        return report<std::chrono::milliseconds>();
    }
    basic_stopwatch() : p(), d() { }
};
struct c_clock
{
    typedef std::clock_t time_point;
    typedef std::clock_t duration;
    static time_point now() { return std::clock(); }
};
template <> unsigned long long int basic_stopwatch<c_clock>::report_ms() const
{
  return 1000. * double(d) / double(CLOCKS_PER_SEC);
}
typedef basic_stopwatch<std::chrono::high_resolution_clock> stopwatch;
typedef basic_stopwatch<c_clock> cstopwatch;

用法:

stopwatch sw;
sw.tick();
run_long_code();
sw.tock();
std::cout << "This took " << sw.report_ms() << "ms.n";

在任何合适的实现中,默认的high_resolution_clock都应该提供非常准确的定时信息。

<ctime>中有一个std::clock()函数,它返回在当前进程上花费的CPU时间(这意味着它不计算由于CPU执行其他任务而导致程序空闲的时间)。此函数可用于精确测量算法的执行时间。使用常数std::CLOCKS_PER_SEC(也来自<ctime>)将返回值转换为秒。

从内联程序集,您可以使用rdtsc指令将32位(最低有效部分)计数器获取到eax,将32位的(最高有效部分)获取到edx。如果你的代码太小,你可以用eax寄存器检查总的近似cpu周期。如果计数大于32位值的最大值,则edx每32位值周期的最大值递增一次。

int cpu_clk1a=0;
int cpu_clk1b=0;
int cpu_clk2a=0;
int cpu_clk2b=0;
int max=0;
std::cin>>max; //loop limit
__asm
{
    push eax
    push edx
    rdtsc    //gets current cpu-clock-counter into eax&edx
    mov [cpu_clk1a],eax
    mov [cpu_clk1b],edx
    pop edx
    pop eax
}
long temp=0;
for(int i=0;i<max;i++)
{
    temp+=clock();//needed to defy optimization to  actually measure something
                          //even the smartest compiler cannot know what 
                          //the clock would be
}
__asm
{
    push eax
    push edx
    rdtsc     //gets current cpu-clock-counter into aex&edx
    mov [cpu_clk2a],eax
    mov [cpu_clk2b],edx
    pop edx
    pop eax
}
std::cout<<(cpu_clk2a-cpu_clk1a)<<std::endl;
   //if your loop takes more than ~2billions of cpu-clocks, use cpu_clk1b and 2b
getchar();
getchar();

输出:在我的机器上,1000次迭代74000个cpu周期,10000次迭代800000个cpu周期。因为clock()很耗时。

我的机器上的Cpu循环分辨率:约1000个循环。是的,你需要成千上万的加法/减法(快速指令)才能相对正确地测量它。

假设cpu工作频率恒定,1000个cpu周期几乎等于1GHz cpu的1微秒。在做这件事之前,你应该先预热一下你的cpu。

从一块代码中计算详细的cpu时间是非常困难的。要做到这一点,通常的方法是将较差/平均/最好的输入数据设计为测试用例。使用这些测试用例,根据您的真实代码进行时间分析。如果没有详细的输入测试数据和条件,没有任何工具可以告诉你失败的原因。

有一些称为profiler的软件可以完全按照您的意愿进行操作。

Windows的一个例子是AMD代码分析器和用于POSIX的gprof。

测量CPU指令的数量是非常无用的。

性能与瓶颈有关,根据手头的问题,瓶颈可能是网络、磁盘IO、内存或CPU。

对于一场友谊赛,我建议时间安排。当然,这意味着要提供足够大的测试用例来进行有意义的度量。

在Unix上,可以使用gettimeofday进行相对精确的测量。

最适合您的是valgrind/callgrind

相关内容

  • 没有找到相关文章

最新更新