【问题标题】:How can I test two algorithms and determine which is faster?如何测试两种算法并确定哪个更快?
【发布时间】:2018-03-27 17:48:32
【问题描述】:

每当处理特定问题时,我可能会遇到不同的解决方案。我不确定如何选择两个选项中的更好的。第一个想法是计算两个解决方案的复杂度,但有时它们可​​能具有相同的复杂度,或者它们可能不同但输入的范围很小,常数因子很重要。

第二个想法是对两种解决方案进行基准测试。但是,我不确定如何使用 c++ 为它们计时。我发现了这个问题: How to Calculate Execution Time of a Code Snippet in C++ ,但我不知道如何正确处理编译器优化或处理器不一致。

简而言之:上述问题中提供的代码是否足以进行日常测试?在运行测试之前,我应该在编译器中启用一些选项吗? (我使用的是 Visual C++)我应该做多少测试,两个基准测试之间的时间差有多大?

这是我要测试的代码示例。其中哪个更快?我自己怎么计算?

unsigned long long fiborecursion(int rank){
    if (rank == 0) return 1;
    else if (rank < 0) return 0;
    return fiborecursion(rank-1) + fiborecursion(rank-2);
}

double sq5 = sqrt(5);
unsigned long long fiboconstant(int rank){
    return pow((1 + sq5) / 2, rank + 1) / sq5 + 0.5;
}

【问题讨论】:

  • 可以说的是,您必须在打开编译器优化的情况下测试您的真实代码。否则,您将测试未优化的代码,并且您不会得到有用的结果。如果您测试人工代码,您可能会冒编译器优化所有内容或重要部分的风险。
  • This response 来自您链接的问题会很好。构建优化的代码,选择足够大的样本量,进行多次运行并平均执行时间。
  • 如何启用所有优化? “足够大的样本量”是多少?
  • 您必须添加优化标志 -O1 用于大小和 -O2 用于速度并使用 big int fiborecursion(1000000) 或 `fuborrvursion(100000000L) 测试您的功能
  • 在互联网上搜索“如何对 C++ 程序进行基准测试”。关键字是benchmarking

标签: c++ benchmarking


【解决方案1】:

使用来自this answer的时钟

#include <iostream>
#include <chrono>

class Timer
{
public:
    Timer() : beg_(clock_::now()) {}
    void reset() { beg_ = clock_::now(); }
    double elapsed() const { 
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count(); }

private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;
};

你可以编写一个程序来为你的两个函数计时。

int main() {
    const int N = 10000;
    Timer tmr;

    tmr.reset();
    for (int i = 0; i < N; i++) {
        auto value = fiborecursion(i%50);
    }
    double time1 = tmr.elapsed();

    tmr.reset();
    for (int i = 0; i < N; i++) {
        auto value = fiboconstant(i%50);
    }
    double time2 = tmr.elapsed();

    std::cout << "Recursion"
            << "\n\tTotal: " << time1
            << "\n\tAvg: " << time1 / N
            << "\n"
            << "\nConstant"
            << "\n\tTotal: " << time2
            << "\n\tAvg: " << time2 / N
            << "\n";
}

我会尝试在没有编译器优化 (-O0) 和最大编译器优化 (-O3) 的情况下进行编译,看看有什么区别。在最大优化时,编译器可能会完全消除循环。

【讨论】:

  • 当你说它可能会摆脱它时,你指的是哪个循环?
  • 两者。如果编译器意识到循环中计算的唯一值从未实际使用过,它可能会完全跳过两个循环。
  • 我的递归解决方案效率极低,因此恒定解决方案获胜。然而,另一种递归解决方案确实比恒定解决方案更好。 (即使它在技术上具有较低的复杂性)。恒定的解决方案也会因更大的值而中断。
  • @Megadardery:如果您检查编译器的 asm 输出以查看您 实际上 计时的循环,您可能会发现编译器优化了您的递归版本,该版本做得更好循环而不是递归,甚至可能将其变成正常的迭代斐波那契 (a+=b; b+=a),它的运行时间为 O(N) 而不是 O(2^n) for the horrible naive recursive version
  • Do not benchmark with -O0。这是没有用的,因为在进行反优化调试构建时,某些代码的速度比其他代码慢得多。在 SO 上看到很多被否决/关闭的性能问题导致了这个错误......-O3 vs. -O3 -fno-tree-vectorize vs. -O3 -march=native 很有趣,看看自动矢量化做了什么。 要检查 -O3 是否没有优化您的代码,请增加重复次数或问题大小并检查运行时间是否增加
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-15
  • 1970-01-01
  • 2018-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多