包含许多“if”的临界循环,其输出是常量:如何保存条件测试?

时间:2011-04-02 16:05:52

标签: c++ c optimization gcc g++

我的代码中有一个关键循环:

int myloop(int a, .....){

   /* some stuff */

   // Critical loop
   while(...){
       /* Some Stuff */
       if(a == 1){
          // .....
       }
       else if(a == 2){
          // .....
       }
       else if(a == 3){
          // .....
       }
       else{
          // ....
       }
   }
}

由于循环从不接触“a”的值,所采用的分支永远不会改变,但由于此循环非常重,因此需要多次测试“a”的值,这是完全没必要的。最好的事情可能是复制循环,以便在循环开始之前可以测试“if”,但是这意味着复制了两种情况常见的很多东西,并且会导致代码非常难看......

有没有办法让GCC / G ++在编译代码时复制它?或者其他任何避免多次测试价值的技巧?

感谢您的帮助!

Nathann

10 个答案:

答案 0 :(得分:6)

首先,您可以在此处使用switch语句:

switch(a) {

   case 0:
     // handle a==0
     break;

   case 1:
     // handle a==1
     break;

   default:
     // handle all other cases
}

可能使编译器能够生成更快的代码,即执行单个计算跳转而不是针对a的多次检查。

  

这意味着要复制两种情况共同的很多东西

<强>重构!将共享代码放入单独的函数中,可能会将其声明为inline,并希望编译器遵循提示?函数内联是让编译器进行代码复制的好方法(另外两种方式是模板和预处理器,这两种方式在这里显然都不合适。)

inline void sharedStuff() {...}

int myloop(int a, .....){

   /* some stuff */

   if (a==1) {

      while(...){

         // code specific to a==1

         // do shared stuff
         sharedStuff();
      }

   }
   else if ...
}

当然这取决于你在循环中做了什么,但你应该得到基本原则。

最后但并非最不重要:个人资料。检查循环是否真的是性能瓶颈。看看生成的机器代码。很可能编译器已经使用了大多数建议的优化。

答案 1 :(得分:2)

您可以使用switch声明:

while(...)
{
   switch(a)
   {
   case 1:
      // do what you want
      break;
   case 2:
      // do what you want
      break;
   case x:
      // do what you want
      break;
   default:
      //if not matching any previous..
   }
}

答案 2 :(得分:2)

这样做的一种常见方法如下:

// make function implementation inline...
static inline int myloop_inline(int a, .....){

   /* some stuff */

   // Critical loop
   while(...){
       /* Some Stuff */
       if(a == 1){
          // .....
       }
       else if(a == 2){
          // .....
       }
       else if(a == 3){
          // .....
       }
       else{
          // ....
       }
   }
}

// wrapper function which calls myloop_inline with hard-coded values of a
int myloop(int a, .....){
{
    switch (a)
    {

    // expand inline function for all performance-critical values of a...

    case 1:
        myloop_inline(1);
        break;

    case 2:
        myloop_inline(2);
        break;

    case 3:
        myloop_inline(3);
        break;

    ...

    // for any values of a for which the function is not performance-critical
    // we can just use a default case...

    default:
        myloop_inline(a);
        break;

    }
}

请注意,因为当a被调用myloop_inline()时,myloop()作为文字常量传递,编译器可以在扩展内联函数时优化掉所有不相关的测试和死代码。< / p>

您可能希望采取措施确保myloop_inline()实际上被内联,这包括当然启用优化的编译(例如-O3),并且在例如__attribute__ ((always_inline))的情况下。 gcc你可能想要添加{{1}}。

答案 3 :(得分:1)

我建议传递“a”作为模板参数,即

template< int a > 
int myloop(.....) {
  if( a==1 ) { ... }
}

就像那样,它会被正确地优化掉。

但是,您将无法将变量作为模板参数传递, 所以在其他地方,你必须放置那个开关。

switch(a) { 
  case 1: myloop<1>(...); break;
  ...
}

答案 4 :(得分:1)

如何定义一个函数来执行while循环中发生的任何事情并定义它inline?然后在while内移动if循环并在那里调用函数。这将完全符合您的要求。

答案 5 :(得分:1)

如何使每个单独的函数,然后有一个指向函数的指针?

void func_a() {
    // ...
}

void func_b() {
    // ...
}

int myloop(int a, ...) {
    void (*func)();
    if (a == 1) {
        func = &func_a;
    } else if (a == 2) {
        func = &func_b;
    } ...

    while (1) {
        /* Some stuff */
        func();
    }
}

答案 6 :(得分:0)

int myloop(int a, ...){
    if(a == 1){
      myloop1(a, ...);
    }
    else if(a == 2){
      myloop2(a, ...);
    }
    else if(a == 3){
      myloop3(a, ...);
    }
    else{
      myloopelse(a, ...);
    }
}

myloop#(int a, ...){
    SomeStuffAboveLoop(...)
    while(...){
        SomeStuffInsideLoop(...)
        //Do what you want for the appropriate #
    }
}

您也可以将if块更改为开关,其他许多答案都会显示。

答案 7 :(得分:0)

在每种情况下采取的操作是否完全相似?在这些情况下,我通常使用if语句来设置关键对象的值或指向对象的指针,然后在剩余的代码中使用这些值......基本上,最大化你对间接的使用。

dim x as object
dy y as string
如果(a == 1)那么 X = foo.object
Y = bar.string
elseif(a == 2)
X = yuk.object
Y = yo.string
结束如果
而... ... dofunction(X,Y)
结束时

但是,如果你的操作完全不同,那么它已经很难看了,你也可以创建多个循环来节省时钟周期......

答案 8 :(得分:0)

如果条件大多相邻,则可以使用带指针功能的数组。

typedef void ( *case_func )( type1 param1, type2 param2 );

void case_func1( type1 param1, type2 param2 );
void case_func2( type1 param1, type2 param2 );
void case_func3( type1 param1, type2 param2 );
void case_func5( type1 param1, type2 param2 );

case_func    func_array[] =
{
    &case_func1,
    &case_func2,
    &case_func3,
    NULL,        // Just a place holder, for non-contiguous values.
    &case_func5
};

int myloop(int a, .....){

   /* some stuff */

   // Critical loop
   while(...)
   {
       if ( func_array[ a ] && a < sizeof( func_array ) )
           func_array[ a ]( .... );
   }
}

如果您可以确保数组中没有漏洞且a永远不会超出数组范围,则可以省略if ( func_array[ a ] && a < sizeof( func_array ) ),将代码简化为无法进行比较。

无论如何,这种方法可以更清晰一些,即使它没有带来很大的性能提升也值得。

答案 9 :(得分:0)

您还可以考虑在以下条件下移动循环。这会牺牲代码大小来提高运行效率。

if (a == 0) {
    while (...) {
       /* some stuff */
       // 0 stuff
    }
} else if (a == 2) {
    while (...) {
       /* some stuff */
       // 2 stuff
    }
} else if (a == 3) {
 ....
}