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

问题描述 投票:4回答:2

我有一个像下面那个有一个不变量的循环,这里是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)的这种优化,而不是一些可能缺少优化的异乎寻常的优化,这在大多数编译器中都是事实上的标准。

c++ compiler-optimization
2个回答
1
投票

之前在MSVC编译器中曾经有/ Og(全局优化),现在默认启用。我的猜测是其他编译器也这样做。

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

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

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


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>();
}
© www.soinside.com 2019 - 2024. All rights reserved.