在下面的模板化函数示例中,if
循环中的中心for
是否保证优化,只留下使用过的指令?
如果不能保证优化(在GCC 4,MSVC 2013和llvm 8.0中),最多使用C ++ 11的替代方案是什么?
请注意,此功能不起作用,我知道可以通过多种方式优化此特定功能,依此类推。但我想关注的是bool
模板参数在生成代码时的工作原理。
template <bool IsMin>
float IterateOverArray(float* vals, int arraySize) {
float ret = (IsMin ? std::numeric_limits<float>::max() : -std::numeric_limits<float>::max());
for (int x = 0; x < arraySize; x++) {
// Is this code optimized by the compiler to skip the unnecessary if?
if (isMin) {
if (ret > vals[x]) ret = vals[x];
} else {
if (ret < vals[x]) ret = vals[x];
}
}
return val;
}
答案 0 :(得分:4)
理论上没有。 C ++标准允许编译器不仅是愚蠢的,而且是彻头彻尾的敌意。只要抽象机器行为保持不变,它就可以毫无理由地注入无用的东西。
在实践中,是的。死代码消除和常量分支检测很容易,我检查过的每个编译器都消除了if
分支。
请注意,两个分支在被删除之前编译,因此它们都必须是完全有效的代码。输出程序集表现为“好像”两个分支都存在,但分支指令(和无法访问的代码)不是抽象机器行为的可观察特征。
当然,如果不进行优化,可能会保留分支和死代码,因此您可以使用调试器将指令指针移动到“死代码”中。
答案 1 :(得分:1)
无法保证它会被优化掉。虽然它是一个编译时常量,但它很有可能存在。
那就是说C ++ 17给了我们if constexpr,它只会编译通过检查的代码。如果您需要保证,我建议您改用此功能。
在C ++ 17之前如果你只想要编译一部分代码,你需要专门化这个函数并只编写与该特化相关的代码。
答案 2 :(得分:1)
由于您在C ++ 11中要求替代方法,因此这里有一个:
float IterateOverArrayImpl(float* vals, int arraySize, std::false_type)
{
float ret = -std::numeric_limits<float>::max();
for (int x = 0; x < arraySize; x++) {
if (ret < vals[x])
ret = vals[x];
}
return ret;
}
float IterateOverArrayImpl(float* vals, int arraySize, std::true_type)
{
float ret = std::numeric_limits<float>::max();
for (int x = 0; x < arraySize; x++) {
if (ret > vals[x])
ret = vals[x];
}
return ret;
}
template <bool IsMin>
float IterateOverArray(float* vals, int arraySize) {
return IterateOverArrayImpl(vals, arraySize, std::integral_constant<bool, IsMin>());
}
您可以在实时here中看到它。
这个想法是使用函数重载来处理测试。