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

时间:2019-02-08 21:47:59

标签: c++ performance templates

我正在尝试执行此处所述的一些循环优化:Optimizing a Loop vs Code Duplication

我有一个额外的麻烦,就是循环内的某些代码只需要根据循环外部的运行时已知变量的组合来执行即可(可以用模板参数代替该变量进行优化,如链接中所述)以上)和一个运行时已知的变量,该变量仅在循环内部是已知的。

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

final static int DIV = 3;
final static int MOD = 5;

/** Returns an integer `r` such that `r / DIV = x` and `r % MOD = y`, else returns 0 */
public static int getIntegerH(int x, int y) {
    if (y >= MOD) {
        System.out.println(
                "getIntegerH(" + x + ", " + y + ") -> none; impossible to have a remainder of " + y
                        + " when the modulo is " + MOD);
        return 0;
    }

    // determine the min and max numbers inclusive where n / DIV = x
    int min = x > 0 ? x * DIV : x * DIV - DIV + 1;
    int max = x > 0 ? x * DIV + DIV - 1 : x * DIV;

    // get the min and max values of the mod for this
    int minMod = min % MOD;
    int maxMod = max % MOD;

    if ((MOD > DIV) && (y < minMod || y > maxMod)) {
        System.out
                .println("getIntegerH(" + x + ", " + y + ") -> none; there is no number `n` between "
                        + min + " and " + max + " where `n % " + MOD + " = " + y + "`");
        return 0;
    }

    int r = (x > 0) ? min + y - minMod : max + y - maxMod;
    System.out.println("getIntegerH(" + x + ", " + y + ") -> " + r + "; " + r + " / " + DIV + " = ["
            + (r / DIV) + "]; " + r + " % " + MOD + " = [" + (r % MOD) + "]");
    return r;
}

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

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;
}

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

请注意,代码太复杂了,可能无法内联该函数,而且编译器优化也可能通常无法对此进行整理。

2 个答案:

答案 0 :(得分:1)

  

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

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

首先,将条件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可以覆盖较大范围而无需更改内部条件的值。然后将循环分成较小的部分。

答案 1 :(得分:0)

在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;
    }
}