gpt4 book ai didi

c++ - 在 C++ 中优化对成员的访问

转载 作者:IT老高 更新时间:2023-10-28 22:29:33 25 4
gpt4 key购买 nike

以下代码使用不同的编译器时,我遇到了不一致的优化行为:

class tester
{
public:
tester(int* arr_, int sz_)
: arr(arr_), sz(sz_)
{}

int doadd()
{
sm = 0;
for (int n = 0; n < 1000; ++n)
{
for (int i = 0; i < sz; ++i)
{
sm += arr[i];
}
}
return sm;
}
protected:
int* arr;
int sz;
int sm;
};

doadd 函数模拟了对成员的一些密集访问(忽略此问题的溢出)。与实现为函数的类似代码相比:

int arradd(int* arr, int sz)
{
int sm = 0;
for (int n = 0; n < 1000; ++n)
{
for (int i = 0; i < sz; ++i)
{
sm += arr[i];
}
}
return sm;
}

doadd 方法在使用 Visual C++ 2008 以 Release模式编译时比 arradd 函数 慢约 1.5 倍。当我修改doadd 方法如下(将所有成员别名为 locals):

int doadd()
{
int mysm = 0;
int* myarr = arr;
int mysz = sz;
for (int n = 0; n < 1000; ++n)
{
for (int i = 0; i < mysz; ++i)
{
mysm += myarr[i];
}
}
sm = mysm;
return sm;
}

运行时大致相同。我是否认为这是 Visual C++ 编译器缺少的优化?使用 -O2-O3 编译时,g++ 似乎做得更好,并且以相同的速度运行成员函数和普通函数。


基准测试是通过在一些足够大的数组(几百万个整数)上调用 doadd 成员和 arradd 函数来完成的。


编辑: 一些细粒度的测试表明,罪魁祸首是 sm 成员。用本地版本替换所有其他版本仍然会使运行时变长,但是一旦我将 sm 替换为 mysm 运行时就等于函数版本。


alt text

分辨率

对答案感到失望(对不起,伙计们),我摆脱了懒惰,开始研究这段代码的反汇编 list 。我的 answer below总结了调查结果。简而言之:它与别名无关,它与循环展开有关,并且在决定展开哪个循环时,MSVC 应用了一些奇怪的启发式方法。

最佳答案

可能是别名问题——编译器无法知道实例变量sm永远不会被arr指向,所以它必须处理 sm 就好像它实际上是 volatile 一样,并在每次迭代时保存它。您可以使 sm 成为不同的类型来检验这个假设。或者只是使用一个临时的本地总和(它将被缓存在一个寄存器中)并在最后将其分配给 sm

关于c++ - 在 C++ 中优化对成员的访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3930813/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com