如何让编译器用std :: map循环不变代码运动?

时间:2017-04-27 02:57:23

标签: c++ compiler-optimization stdmap

以下是我的代码的一部分:(请注意,M是一个很大的数字)

void myFunc(std::map<int, int>& myMap, int** arr) {

    for (int i = 0; i < N; i++) { 
        for (int j = 0; j < M; j++) { 
            arr[i][j] = myMap[i] * j + i;
        } 
    }

}

myMap[i]是内循环中的循环不变量。所以我认为当我使用-O1时,gcc会自动将其移出内循环。但它不起作用。因为我手动移动时如下:

void myFunc(std::map<int, int>& myMap, int** arr) {

    for (int i = 0; i < N; i++) { 
        int tmp = myMap[i]; 
        for (int j = 0; j < M; j++) { 
            arr[i][j] = tmp * j + i;
        } 
    }

}

运行时比第一个版本好得多。

也许编译器认为myMap会修改地图,所以它选择不进行优化。但我确信我只想阅读myMap的值,不会修改它。如何让编译器理解?

1 个答案:

答案 0 :(得分:0)

你是正确的,因为地图operator[]总是返回一个值的可修改引用,所以编译器不能假定将它移到该内部循环之外是安全的。这是因为如果值不存在,操作员将插入值。哪个可能你想要什么。

只需使用const方法迭代地图即可轻松修复。您可以将地图作为myFunc传递给const&,然后使用cbegin()cend()成员函数来获取对其中存储的值的引用。

E.g:

void myFunc(const std::map<int, int>& m, int** arr) {
    int i = 0;
    auto it = m.cbegin();
    while (it != m.cend()) { // assumes m.size() == N
        for (int j = 0; j < M; j++) {
            arr[i][j] = (*it).second * j + i;
        }
        it++;
        i++;
    }
}

这是我的机器上的一些快速基准测试,适用于N=10M=1000000。 (我跑了几次,这些都在中间的某个地方。)

您的原始版本:

real   0m0.449s
user   0m0.434s
sys    0m0.012s

你的第二个版本:

real   0m0.162s
user   0m0.148s
sys    0m0.012s

我的解决方案:

real   0m0.176s
user   0m0.162s
sys    0m0.012s

你的第二个解决方案实际上似乎总是快10%左右,所以如果你关心最后一点速度,那就自己提起这个任务。