18

假设您有一个 C/C++ 中的函数,它在第一次运行时会以某种方式运行。然后,所有其他时间它都以另一种方式表现(例如,见下文)。第一次运行后,if 语句变得多余,如果速度很重要,可以优化掉。有没有办法进行这种优化?

bool val = true; 

void function1() {

   if (val == true) {
      // do something
      val = false; 
   }
   else {
      // do other stuff, val is never set to true again 
   }

}
4

11 回答 11

17

gcc有一个内置函数,可让您通知实现有关分支预测的信息:

 __builtin_expect 

http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

例如在你的情况下:

bool val = true; 

void function1()
{
    if (__builtin_expect(val, 0)) {
       // do something
       val = false; 
    }
    else {
      // do other stuff, val is never set to true again 
    }
}
于 2012-10-19T21:53:19.800 回答
16

只有当您确定它确实是一个瓶颈时,您才应该进行更改。使用分支预测,if语句可能是即时的,因为它是一种非常可预测的模式。

也就是说,您可以使用回调:

#include <iostream>
using namespace std;
typedef void (*FunPtr) (void);
FunPtr method;
void subsequentRun()
{
    std::cout << "subsequent call" << std::endl;
}
void firstRun()
{
    std::cout << "first run" << std::endl;
    method = subsequentRun;  
}
int main()
{
    method = firstRun;
    method();
    method();
    method();
}

产生输出:

第一次运行
后续调用
后续调用

于 2012-10-19T21:50:08.950 回答
9

您可以使用函数指针,但无论如何它都需要间接调用:

void (*yourFunction)(void) = &firstCall;

void firstCall() {
 ..
 yourFunction = &otherCalls;
}

void otherCalls() {
 ..
}

void main()
{
  yourFunction();
}
于 2012-10-19T21:50:44.180 回答
4

一种可能的方法是编译该函数的两个不同版本(这可以使用模板从源中的单个函数完成),并在运行时使用函数指针或对象来决定。但是,除非您的函数非常昂贵,否则指针开销可能会超过任何潜在收益。

于 2012-10-19T21:47:48.250 回答
4

您可以使用static成员变量而不是全局变量..

或者,如果您第一次运行的代码更改了某些内容以供将来使用(例如,打开文件?),您可以使用该更改作为检查来确定是否运行代码(即检查是否文件已打开)。这将为您节省额外的变量。此外,它可能有助于错误检查 - 如果由于某种原因初始更改未被其他操作更改(例如,文件位于不正确删除的可移动媒体上),您的检查可能会尝试重新进行更改。

于 2012-10-19T21:51:14.980 回答
1

编译器只能优化编译时已知的内容。

在您的情况下,该值val仅在运行时已知,因此无法优化。

测试if非常快,您不必担心对其进行优化。

于 2012-10-19T21:50:47.810 回答
1

如果您想让代码更简洁一些,您可以使用以下方法将变量设为函数的本地变量static

void function() {
    static bool firstRun = true;
    if (firstRun) {
        firstRun = false;
        ...
    }
    else {
        ...
    }
}

第一次进入该函数时,firstRun将为真,并且它会持续存在,因此每次调用该函数时,该firstRun变量将与之前的实例相同(并且每次后续都为假)。

这可以很好地与@ouah 的解决方案一起使用。

于 2012-10-22T14:14:25.090 回答
0

像 g++(我相信 msvc)这样的编译器支持在第一次运行时生成配置文件数据,然后使用该数据更好地猜测最有可能遵循哪些分支,并进行相应的优化。如果您使用 gcc,请查看该-fprofile-generate选项。

预期的行为是编译器将优化该 if 语句,以便首先对 else 进行排序,从而避免在所有后续调用中执行 jmp 操作,使其与不存在时一样快,尤其是在您返回时在 else 中的某个地方(从而避免跳过“if”语句)

于 2012-10-19T21:47:43.337 回答
0

进行这种优化的一种方法是将函数一分为二。代替:

void function1()
{
    if (val == true) {
        // do something
        val = false; 
    } else {
       // do other stuff
    }
}

做这个:

void function1()
{
    // do something
}

void function2()
{
   // do other stuff
}
于 2012-10-19T22:02:01.497 回答
0

您可以做的一件事是将逻辑放入对象的构造函数中,然后定义static。如果这样的static对象出现在块作用域中,则构造函数在该作用域的执行发生的第一次运行。编译器发出一次性检查。

您还可以将static对象放在文件范围内,然后在main调用之前对其进行初始化。

我给出这个答案是因为您可能没有有效地使用 C++ 类。

(关于C/C++,没有这样的语言。有 C 和 C++。你是在 C 中工作,还必须编译为 C++(有时称为,非正式地,“干净的 C”),还是你真的在 C++ 中工作?)


什么是“清洁 C”,它与标准 C 有何不同?

于 2012-10-20T00:48:10.457 回答
0

为了保持编译器的独立性,您可以在一个函数和else{}另一个函数中编写 if() 的各个部分。几乎所有的编译器都优化了if() else{}- 所以,一旦最有可能是else{} - 因此将偶尔的可执行代码编码if()在一个单独的函数中,然后在一个单独的函数中调用else

于 2012-10-20T06:57:11.437 回答