在C++中混合模板参数和变量时的分支



我正在尝试进行一些循环优化,如下所述:优化循环与代码重复

我还有一个额外的复杂性,即循环中的一些代码只需要根据循环外部的运行时已知变量的组合来执行(可以用模板参数替换以进行优化,如上面的链接中所讨论的)一个仅在循环内已知的运行时未知变量。

以下是完全未优化的代码版本:

for (int i = 0; i < 100000, i++){
if (external_condition_1 || (external_condition_2 && internal_condition[i])){
run_some_code;
}
else{
run_some_other_code;
}
run_lots_of_other_code;
}

这是我尝试将循环封装在模板化函数中,如上面链接的问题所建议的那样,通过编写多个版本的循环来优化性能并避免代码重复:

template<bool external_condition_1, external_condition_2>myloop(){
for (int i = 0; i < 100000, i++){
if (external_condition_1 || (external_condition_2 && internal_condition[i]){
run_some_code;
}
else{
run_some_other_code;
}
run_lots_of_other_code;
}

我的问题是:如何编写代码以避免分支和代码重复?

请注意,代码非常复杂,函数可能无法内联,编译器优化通常也不会解决这个问题。

我的问题是:如何编写代码以避免分支和代码重复?

好吧,您已经编写了模板来避免代码重复,对吧?那么让我们看看剩下什么分支。为此,我们应该查看从模板生成的每个函数(共有四个)。我们还应该根据模板参数应用预期的编译器优化。

首先,将条件1设置为true。这应该产生两个函数,本质上(使用一点伪语法)如下:

myloop<true, bool external_condition_2>() {
for (int i = 0; i < 100000, i++){
// if ( true || whatever ) <-- optimized out
run_some_code;
run_lots_of_other_code;
}
}

没有分支。好的转到第一个条件为假,第二个条件为真。

myloop<false, true>(){
for (int i = 0; i < 100000, i++){
if ( internal_condition[i] ){ // simplified from (false || (true && i_c[i]))
run_some_code;
}
else{
run_some_other_code;
}
run_lots_of_other_code;
}
}

好的,这里有一些分支。但是,需要对每个i进行分析,以确定应该执行哪些代码。我认为如果没有更多关于internal_condition的信息,这里就没有什么可做的了。稍后我会对此进行一些思考,但现在让我们转到第四个函数。

myloop<false, false>() {
for (int i = 0; i < 100000, i++){
// if ( false || (false && whatever) ) <-- optimized out
run_some_other_code;
run_lots_of_other_code;
}
}

这里没有分支。您已经很好地避免了分支和代码重复。


好的,让我们回到myloop<false,true>,那里有分支。分支在很大程度上是不可避免的,因为你的情况是如何设置的。您将进行多次迭代。有些迭代你想做一件事,而其他迭代应该做另一件事。为了解决这个问题,你需要重新设想你的设置,这样你就可以在每次迭代中做同样的事情。(您正在进行的优化是基于每次迭代都做相同的事情,即使下次循环开始时可能会有所不同。)

最简单但不太可能的场景是internal_condition[i]等效于i < 5000。如果您可以先执行所有"一些代码",然后再执行任何"许多其他代码",这也会很方便。然后,您可以从0循环到4999,每次迭代都运行"一些代码"。然后从5000循环到99999,运行"其他代码"。然后是第三个循环来运行"许多其他代码"。

我能想到的任何解决方案都包括调整你的情况,使其更像不太可能的简单场景。你能计算出internal_condition[i]为真的次数吗?你能多次迭代并将你的(新的)循环控制变量映射到i(旧的循环控制变量)的适当值吗?(或者i的确切值并不重要?)然后进行第二次循环以覆盖其余情况?在某些情况下,这可能是微不足道的。在其他方面,远非如此。

可能还有其他技巧可以做,但它们取决于你在做什么,你需要做什么,以及你认为你需要做但实际上没有做的更多细节。(所需的详细程度可能会超过StackOverflow。)订单重要吗?i的确切值重要吗?

最后,我会选择对代码进行分析。在没有代码重复但有分支的情况下评测代码。用最少的分支来评测代码,但要避免代码重复。是否存在可衡量的变化?如果是这样,请考虑如何重新安排内部条件,以便i能够在不更改内部条件值的情况下覆盖大范围。然后把你的环分成更小的部分。

在C++17中,为了保证没有额外的分支评估,您可以执行以下操作:

template <bool external_condition_1, bool external_condition_2>
void myloop()
{
for (int i = 0; i < 100000, i++){
if constexpr (external_condition_1) {
run_some_code;
} else if constexpr (external_condition_2){
if (internal_condition[i]) {
run_some_code;
} else {
run_some_other_code;
}
} else {
run_some_other_code;
}
run_lots_of_other_code;
}
}

最新更新