关于算法:如何查找C ++代码的运行时效率

How to find runtime efficiency of a C++ code

我正在努力寻找一个程序的效率,我最近在StackOverflow上发布了这个程序。

如何有效地从给定另一个向量的向量中删除元素

为了将代码的效率与其他答案进行比较,我使用了chrono对象。

它是检查运行时效率的正确方法吗?

如果没有,那么请用一个例子来建议一种方法。

科利鲁代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <vector>
#include
#include <chrono>
#include <ctime>
using namespace std;

void remove_elements(vector<int>& vDestination, const vector<int>& vSource)
{
    if(!vDestination.empty() && !vSource.empty())
    {
        for(auto i: vSource) {
            vDestination.erase(std::remove(vDestination.begin(), vDestination.end(), i), vDestination.end());
        }
    }
}

int main() {
    vector<int> v1={1,2,3};
    vector<int> v2={4,5,6};
    vector<int> v3={1,2,3,4,5,6,7,8,9};
    std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
    remove_elements(v3,v1);
    remove_elements(v3,v2);
    std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now();
    std::cout <<"Time difference =" << std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() <<std::endl;
    for(auto i:v3)
        cout << i << endl;
    return 0;
}

产量

1
2
3
4
Time difference = 1472
7
8
9


Is it a correct way to check the runtime efficiency?

看起来不是最好的方法。我发现你的方法有以下缺陷:

  • 值已排序。当使用相同的算法测试排序值与未排序值时,分支预测可能会暴露出荒谬的效果。可能的解决方法:对排序和未排序进行测试,并比较结果。
  • 值是硬编码的。CPU缓存是一个棘手的问题,它可能会在硬编码值测试和实际值测试之间引入细微的差异。在现实世界中,您不太可能对硬编码值执行这些操作,因此您可以从文件中读取这些值,也可以生成随机值。
  • 值太少。您的代码的执行时间比计时器精度小得多。
  • 您只运行一次代码。如果修复所有其他问题并运行代码两次,由于缓存预热,第二次运行可能比第一次运行快得多:后续运行的缓存未命中往往比第一次少。
  • 在固定大小的数据上运行一次代码。最好至少进行四次正确的测试,以涵盖以下参数的笛卡尔积:
    • 排序数据与未排序数据
    • v3适合CPU缓存,而v3的大小超过了CPU缓存。还可以考虑当(v1.length() + v3.length()) * sizeof(int)与缓存是否匹配,(v1.length() + v2.length() + v3.length()) * sizeof(int)与缓存是否匹配等所有组合的情况。

  • 你的方法最大的问题是:

    1)您要测试的代码太短,而且是可预测的。您需要运行它至少几千次,以便在测量之间至少有几百毫秒。您需要使数据集更大,更不容易预测。一般来说,CPU缓存确实基于合成输入数据PITA进行精确测量。

    2)编译器可以自由地重新排序代码。一般来说,很难确保您正在计时的代码在调用之间执行以检查时间(而没有其他事情,因为这一点)。一方面,您可以向下拨优化,但另一方面,您希望测量优化的代码。

    一种解决方案是关闭整个程序优化,并将计时调用放入另一个编译单元。

    另一种可能的解决方案是在测试周围使用内存隔离,例如

    1
        std::atomic_thread_fence(std::memory_order_seq_cst);

    (需要EDCOX1×4)和一个C++ 11能力编译器。

    此外,您可能希望用事件探查器数据补充您的度量,以了解使用1/2/3缓存的效率、内存瓶颈、指令失效率等。不幸的是,英特尔x86的最佳工具是商业(vtune),但在AMD x86上,类似的工具是免费的(codexl)。


    您可以考虑使用一个基准库(如celero)来为您进行度量,并处理性能度量中的棘手部分,而您仍然关注您试图优化的代码。在我在前面问题的答案中链接的代码中有更复杂的示例(如何有效地从给定另一个向量的向量中删除元素),但是一个简单的用例如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    BENCHMARK(VectorRemoval, OriginalQuestion, 100, 1000)
    {
        std::vector destination(10000);
        std::generate(destination.begin(), destination.end(), std::rand);
        std::sample(destination.begin(), destination.end(), std::back_inserter(source),
            100, std::mt19937{std::random_device{}()})    

        for (auto i: source)
            destination.erase(std::remove(destination.begin(), destination.end(), i),
                destination.end());    
    }