如何比较两段代码的性能表现

14

我和几个编程领域的小伙伴参加了一场友好的比赛,最近我们都对编写高效代码非常感兴趣。我们的挑战是不计任何成本(包括可读性、可复用性等),尝试优化代码的性能(即CPU时间和复杂度)。

问题在于,现在我们需要比较我们的代码,并查看哪种方法与其他方法相比更好,但我们不知道有任何可以完成此任务的工具。

我的问题是,是否有一些(任何!)工具可以将代码片段作为输入,并计算运行它所需的FLOPS或CPU指令数量? 是否有任何工具可以衡量代码的优化性?

P.S. 目标语言是C++,但如果有类似的工具也适用于Java,则也很不错。


3
+1 代表“最佳状态”。运行 time ./prog 是否足够? - Kerrek SB
1
@KerrekSB 我相信 OP 想要一个分析器。 - user529758
3
我认为计算浮点运算次数或CPU指令数量并不是衡量效率的好方法。很容易组合一些无用代码来达到最大化这些指标。 - Mysticial
7个回答

12

这是一个我喜欢使用的 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默认情况下应该提供非常精确的时间信息。


+1:在确定哪段代码更快时,计时通常比分析更好。分析通常会忽略缓存等类似因素,这可能对性能产生巨大影响。 - Leo
为了高分辨率计时,您应该直接使用适当的系统调用,例如在Linux上使用clock_gettime(),它提供实际的纳秒分辨率。Chrono调用可能会导致太多的抖动/其他负面影响。clock_gettime()系统调用需要几百个CPU时钟周期才能运行,但其持续时间似乎非常恒定。如果您希望获得完美的计时,则应禁用操作系统的CPU频率管理器,并使用处理器的汇编指令(例如rdtsc)计算实际的CPU时钟周期数。 - mic_e
1
@mic_e:TSC 的问题在于它无法与多个核心很好地协同工作。由 chronoclock_gettime 提供的稳定时钟可以克服这一问题。 - Kerrek SB
为什么这么复杂?和在函数调用前后使用“std :: chrono :: high_resolution_clock :: now();”并减去差异相比,这有什么改进吗? - northerner
@LewisChan:我认为这是因为主模板返回的计数通常是整数,所以特化模板也是如此。 - Kerrek SB
显示剩余3条评论

3

有一个来自<ctime>的函数叫做std::clock(),可以返回当前进程在CPU上运行的时间(也就是不计算程序因为CPU执行其他任务而空闲的时间)。这个函数可以用来准确地测量算法的执行时间。使用常量std::CLOCKS_PER_SEC(同样来自<ctime>)将返回值转换为秒。


1

从内联汇编中,您可以使用rdtsc指令将32位(最低有效部分)计数器加载到eax寄存器中,并将32位(最高有效部分)加载到edx寄存器中。如果您的代码太小,您可以通过eax寄存器检查总近似CPU周期。如果计数超过32位值的最大值,则每个max-32位值周期edx递增。

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周期几乎等于1微秒,适用于1GHz的CPU。在进行此操作之前,您应该预热CPU。


0

从一段代码中计算CPU时间的详细数字是相当困难的。通常的做法是设计最坏/平均/最好的输入数据作为测试用例,并基于这些测试用例对您的实际代码进行时间分析。如果没有详细的输入测试数据和条件,任何工具都无法告诉您FLOPS。


0

1
使用性能竞赛来评估程序性能的问题在于,剖析本身会减慢程序执行速度。某些算法可能会受到更大的影响,这会使比赛结果产生偏差。此外,使用剖析支持进行编译会阻止一些编译器优化,这些优化可以用来挤出额外的速度。不要误解 - 剖析器是发现性能瓶颈的重要工具。但是你不应该盲目地信任它们。 - Philipp
@Philipp,我不会盲目地相信分析器,我不是那种疯狂的数学家类型的人。我认为提到它们是一个好主意,因为在我看来,OP完全不知道它们的存在。 - user529758
@Philipp 我也不明白为什么这个答案会被踩 - 它在技术上是正确的... - user529758
如果您因为明确的理由而对此进行了负评,请再次留下评论! - user529758

0

0

测量CPU指令的数量是相当无用的。

性能是相对于瓶颈而言的,取决于手头的问题,瓶颈可能是网络、磁盘IO、内存或CPU。

对于友好的竞争,我建议使用计时。当然,这意味着提供足够大的测试用例来获得有意义的度量。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接