c++ - 间接成本 ~ 浮点乘法的 3 倍,真的吗? (带演示)

标签 c++ optimization c++14 indirection

我刚刚发现间接成本大约是浮点乘法的 3 倍!
这是可以预料的吗?我的测试错了吗?

背景

读完How much does pointer indirection affect efficiency? ,我对间接成本感到 panic 。

Going through a pointer indirection can be much slower because of how a modern CPU works.

在我过早地优化我的真实代码之前,我想确保它真的像我担心的那样花费很多。

我做了一些技巧来找到粗略的数字 (3x),如下所示:-

第一步

  • Test1:没有间接 -> 计算一些东西
  • Test2:间接 -> 计算一些东西(相同)

我发现 Test2Test1 花费更多时间。
这里没有什么奇怪的。

第二步

  • Test1:没有间接 -> 计算一些昂贵的东西
  • Test2:间接 -> 计算便宜的东西

我尝试更改我在calculate something expensive 中的代码,使其逐渐变得更昂贵,以使两个测试 的成本接近相同。

结果

最后,我发现使两个测试使用相同时间量(即收支平衡)的可能函数之一是:-

  • Test1:无间接 -> 返回 float*float*... 3 次
  • Test2 :间接 -> 简单地返回一个 float

这是我的测试用例(ideone demo):-

class C{
    public: float hello;  
    public: float hello2s[10];  
    public: C(){
        hello=((double) rand() / (RAND_MAX))*10;
        for(int n=0;n<10;n++){
            hello2s[n]= ((double) rand() / (RAND_MAX))*10;
        }
    }
    public: float calculateCheap(){
        return hello;
    }
    public: float calculateExpensive(){
        float result=1;
        result=hello2s[0]*hello2s[1]*hello2s[2]*hello2s[3]*hello2s[4];
        return result;
    }
};

这是主要的:-

int main(){
    const int numTest=10000;
    C  d[numTest];
    C* e[numTest];
    for(int n=0;n<numTest;n++){
        d[n]=C();
        e[n]=new C();
    }
    float accu=0;
    auto t1= std::chrono::system_clock::now();
    for(int n=0;n<numTest;n++){
        accu+=d[n].calculateExpensive();  //direct call
    }
    auto t2= std::chrono::system_clock::now();
    for(int n=0;n<numTest;n++){
        accu+=e[n]->calculateCheap();     //indirect call
    }
    auto t3= std::chrono::system_clock::now();
    std::cout<<"direct call time ="<<(t2-t1).count()<<std::endl;
    std::cout<<"indirect call time ="<<(t3-t2).count()<<std::endl;
    std::cout<<"print to disable compiler cheat="<<accu<<std::endl;
}

直接调用时间间接调用时间调整为与上面提到的相似(通过编辑calculateExpensive)。

结论

间接成本 = 3x 浮点乘法。
在我的桌面(带有 -O2 的 Visual Studio 2015)中,它是 7 倍。

问题

是否可以预期间接成本约为浮点乘法的 3 倍?
如果不是,我的测试怎么错了?

(感谢enhzflep建议改进,已编辑。)

最佳答案

简而言之,您的测试非常不具有代表性,实际上并没有准确衡量您可能认为的结果。

请注意,您调用了 new C() 100'000 次。这将在你的内存中创建 100'000 个 C 实例,每个实例都非常小。如果您的内存访问是有规律的,现代硬件非常擅长预测。由于每次分配、每次调用 new 都是独立发生的,因此内存地址不会很好地组合在一起,从而使这种预测变得更加困难。这会导致所谓的缓存未命中。

分配为数组 (new C[numTest]) 可能会产生完全不同的结果,因为在这种情况下地址再次非常可预测。尽可能紧密地将内存分组并以线性、可预测的方式访问它通常会提供更好的性能。这是因为大多数缓存和地址预取器都希望在普通程序中出现这种模式。

次要添加:像这样初始化 C d[numTest] = {}; 将在每个元素上调用构造函数

关于c++ - 间接成本 ~ 浮点乘法的 3 倍,真的吗? (带演示),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44171675/

相关文章:

c++ - 如何将曲面网格变成多面体?

MySQL GROUP BY 函数需要很多时间

php - 如何优化php文件中的这段代码

c++ - 将std::shared_ptr <T>传递给采用std::shared_ptr <const T>的函数?

c++ - 非模板类中的可变参数成员

c++ - 重用可变类型

c++ - 通过排队的 Qt 信号槽连接发送 QSharedPointers 的 QVector

c++ - 是否有整洁的等价物来查看成员函数/变量?

c++ - 堆上的二维数组,哪个版本更快?

oracle - 是否存在与DETERMINISTIC类似的PL/SQL编译指示,但仅适用于单个SQL SELECT的范围?