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

我的代码中有一个关键循环,具有以下形状:

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

一种常见的方法如下:

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

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

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

首先,您可以在此处使用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 ... } 

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

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

你可以使用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.. } } 

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

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

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

但是,您将无法将变量作为模板参数传递,因此在其他地方您必须放置该开关。

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

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

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

 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(); } } 
 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块更改为开关,许多其他答案显示。

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

将x作为对象
dim y as string
如果(a == 1)那么
X = foo.object
Y = bar.string
elseif(a == 2)
X = yuk.object
Y = yo.string
万一
而……
dofunction(X,Y)
结束了

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

如果您的条件大多相邻,则可以使用带有指向函数的指针的数组。

 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 ]( .... ); } } 

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

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

你也可以考虑在以下条件下移动循环。 这牺牲了代码大小以提高运行时效率。

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