我可以依靠编译器查找和优化简单的布尔循环不变式吗?

时间:2019-04-15 20:26:18

标签: c++ compiler-optimization

我有一个像下面这样的循环,其中有一个不变的变量,这里的值scaleEveryValueByTwo不变。 我是否可以依靠编译器找到该不变性,而不在每次迭代中检查条件(本质上是编译为与底部代码相似的东西)?

void loadValuesFromDisk(const bool scaleEveryValueByTwo)
{
    std::vector<MyValueType> xs;
    while(fileHasNewValues())
    {
        auto x = loadNextValue();
        if (scaleEveryValueByTwo)
        {
            x *= 2;
        }
        xs.push_back(x);
    }
}

我当然可以手动将其分成两个循环(请参见下文),或者将缩放部分放在单独的函数中,但是在许多情况下,这会使代码更长,而且我认为更难阅读(例如,如果我对于3D数据所有维度的嵌套循环,我将复制所有三行循环标题和最多六行花括号)。

void loadValuesFromDisk(const bool scaleEveryValueByTwo)
{
    std::vector<MyValueType> xs;
    while(fileHasNewValues())
    {
        auto x = loadNextValue();
        xs.push_back(x);
    }

    if (scaleEveryValueByTwo)
    {
        for(auto &x : xs)
        {
            x *= 2;
        }
    }
}

我主要感兴趣的是,我是否可以依靠这种优化(或更好地实施)来优化gcc或MSVC等常用编译器,而不是一些可能缺少优化的异国情调的优化器,而这些优化器实际上已成为大多数编译器的标准。

2 个答案:

答案 0 :(得分:1)

以前,MSVC编译器中曾经有/ Og(全局优化)功能,现在默认情况下启用了该功能。 我的猜测是其他编译器也可以这样做。

要了解循环优化的完成方式,请查看以下链接并搜索“循环优化”

https://docs.microsoft.com/en-us/cpp/build/reference/og-global-optimizations?view=vs-2019

现在默认情况下,您可以依赖编译器。

答案 1 :(得分:1)

您可以将scaleEveryValueByTwo用作模板参数,以确保条件仅被评估一次。 在C ++ 17中,您可以按以下方式使用if constexpr

template <bool scaleEveryValueByTwo>
void loadValuesFromDisk()
{
    std::vector<MyValueType> xs;
    while(fileHasNewValues())
    {
        auto x = loadNextValue();
        if constexpr (scaleEveryValueByTwo)
        {
            x *= 2;
        }
        xs.push_back(x);
    }
}

如果您还没有C ++ 17,则可以例如通过如下涉及辅助模板函数multiply来获得上述代码

template <bool activate>
void multiply(decltype(loadNextValue())& x);

template <>
void multiply<true>(decltype(loadNextValue())& x) { x *= 2; }

template <>
void multiply<false>(decltype(loadNextValue())& x) { }

template <bool scaleEveryValueByTwo>
void loadValuesFromDisk()
{
    std::vector<MyValueType> xs;
    while(fileHasNewValues())
    {
        auto x = loadNextValue();
        multiply<scaleEveryValueByTwo>(x);
        xs.push_back(x);
    }
}

(注意:我正在使用decltype,因为我不知道您的例程loadNextValue()返回什么。)

然后您呼叫loadValuesFromDisk<true>()loadValuesFromDisk<false>()。如果scaleEveryValueByTwo仅在运行时已知,则可以跳转到适当的函数:

void loadValuesFromDisk(bool const scaleEveryValueByTwo)
{
    if (scaleEveryValueByTwo)
        loadValuesFromDisk<true>();
    else
        loadValuesFromDisk<false>();
}