C 语言大量重复使用 if else 时,大概率执行的执行体是放在 if 中还是 else 中对执行效率的影响

时间:2021-08-16 12:17:39

之前听前辈说过,使用 if else 时,大概率执行的执行体的位置是有所考究的。

今天编写对效率敏感的代码时又想起了这个,当时没有实际去验证这一说法,今天就趁机进行了一下验证。

示例代码如下:

#include "stdio.h"

int main(int argc, char **argv)
{
    int i;
    int a = 1;
    int b = 0;

    if(a > b){
        i = 1;
    }else{
        i = 0;
    }

    printf("%d",i);
    return 0;
}

使用 VC 编译并提取编译后的部分汇编代码如下:

9:        if(a > b)
0040D7C6   mov         eax,dword ptr [ebp-8]
0040D7C9   cmp         eax,dword ptr [ebp-0Ch]
0040D7CC   jle         main+37h (0040d7d7)
10:       {
11:           i = 1;
0040D7CE   mov         dword ptr [ebp-4],1
12:       }else{
0040D7D5   jmp         main+3Eh (0040d7de) //进入 if,会多执行 jmp 指令
13:           i = 0;
0040D7D7   mov         dword ptr [ebp-4],0
14:       }
15:
16:       printf("%d",i);
0040D7DE   mov         ecx,dword ptr [ebp-4]
0040D7E1   push        ecx
0040D7E2   push        offset string "%d" (0042201c)
0040D7E7   call        printf (00401110)
0040D7EC   add         esp,8
17:       return 0;
0040D7EF   xor         eax,eax

使用 gcc -S 编译源文件并提取汇编文件的部分代码如下:

    subl    $32, %esp
    movl    $1, 24(%esp)
    movl    $0, 28(%esp)
    movl    24(%esp), %eax
    cmpl    28(%esp), %eax
    jle .L2
    movl    $1, 20(%esp)
    jmp .L3  //进入 if,会多执行 jmp 指令
.L2:
    movl    $0, 20(%esp)
.L3:
    movl    $.LC0, %eax
    movl    20(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

以上都是在没有经过编译器优化的情况下进行编译的,如果使用优化选项,编译的汇编代码会直接使用判断的结果来执行后续代码的。

总结:
通过以上验证过程可以得知,在频繁大量的使用 if else 的时候,按照如下方式来进行处理,会相应提高运行速度:

if(条件)
{
    //放置执行次数少的执行体else{
    //放置执行次数多的执行体
}

即 else 中放置条件满足次数多的执行体,这样可以减少跳转指令的调用次数
一次判断,影响可以忽略不计,但多次重复调用,累积的结果还是会有一定影响,特别是在一些性能不强的硬件平台上使用的时候,应该尽量在软件上进行优化,提高运行效率。

在性能强大的硬件平台上,这点影响几乎可以忽略不计,因为 cpu 内部会有大量的结构来优化代码的执行效率,比如流水线结构等等····本人能力有限,在这里只能浅显的表述一下。

实用场景如下,函数被外部重复调用的前提下

void vFunc(void)
{
    static int i = 1000;

    if(0 == i){
        //执行其他需要的操作
    }else{
        i--;
    }
}

以上执行就比下面的执行效率高一些

void vFunc(void)
{
    static int i = 1000;

    if(i>0){
        i--; //此处每次都会比上面的执行多一条跳转指令
    }else{
        //执行其他需要的操作
    }
}