如果Else构造

时间:2015-11-16 17:59:34

标签: c++ c if-statement data-structures

if (a == 1)
    //do something
else if (a == 2)
    //do something
else if (a == 3)
    //do something
else if (a == 4)
    //do something
else if (a == 5)
    //do something
else if (a == 6)
    //do something
else if (a == 7)
    //do something
else if (a == 8)
    //do something    

现在想象一下,我们知道一个主要是7,我们在一个程序中多次执行这个代码块。是否会将(a == 7)支票移至最高点以改善任何时间的表现?那就是:

if (a == 7)
     //do something
else if (a == 1)
    //do something
else if (a == 2)
    //do something
else if (a == 3)
    //do something

等等。它改善了什么,或者只是一厢情愿的想法?

7 个答案:

答案 0 :(得分:3)

您可以使用switch case来改善计划的效果

switch (a) 
{
    case 1:
    break;    

    case 2:
    break;    

    case 3:
    break;    

    case 4:
    break;    

    case 5:
    break;    

    case 6:
    break;    

    case 7:
    break;    
}

答案 1 :(得分:2)

由于按照指定的顺序检查if条件,是的。它是否可测量(因此,你应该关心)将取决于调用该部分代码的次数。

答案 2 :(得分:1)

想象一下,你去了一家酒店,给了一个号码为7的房间。

你必须穿过大厅检查每个房间,直到找到号码为7的房间。

您所花费的时间取决于您在分配之前检查了多少个房间?

..

但是要知道这一点,在您的情景中,时间差异将非常非常短暂地被注意到。

对于需要检查的数字太多的情况,将一个数字放在开头会多次出现确实可以提高性能。事实上,一些网络协议使用这种方法来比较协议号

答案 3 :(得分:1)

如果编译器无法将构造转换为跳转表,则需要支付一些惩罚,我认为切换/案例实现将被编译为汇编中的跳转表,如果不是作为跳转表然后开关/ case有一个边缘,如果否则。我想这再次取决于架构和编译器。

如果是switch / case,编译器只能根据我们提供的常量(例如连续值)生成asm跳转表。

我在我的机器上运行的测试给了if / else的程序集(不是跳转表),

主: .LFB0:

    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $7, -4(%rbp)
    cmpl    $1, -4(%rbp)
    jne     .L2
    movl    $97, %edi
    call    putchar
    jmp     .L3
.L2:

    **cmpl    $2, -4(%rbp)
    jne     .L4**
    movl    $97, %edi
    call    putchar
    jmp     .L3
.L4:

    **cmpl    $3, -4(%rbp)
    jne     .L5**
    movl    $97, %edi
    call    putchar
    jmp     .L3
.L5:

    **cmpl    $4, -4(%rbp)
    jne     .L6**
    movl    $97, %edi
    call    putchar
    jmp     .L3
.L6:

    **cmpl    $5, -4(%rbp)
    jne     .L7**
    movl    $97, %edi
    call    putchar
    jmp     .L3
.L7:

    **cmpl    $6, -4(%rbp)
    jne     .L8**
    movl    $97, %edi
    call    putchar
    jmp     .L3
.L8:

    cmpl    $7, -4(%rbp)
    jne     .L9
    movl    $97, %edi
    call    putchar
    jmp     .L3
.L9:

    cmpl    $8, -4(%rbp)
    jne     .L3
    movl    $97, %edi
    call    putchar
.L3:

    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

但对于开关/案例(跳转表), 主要: .LFB0:

    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $7, -4(%rbp)
    cmpl    $7, -4(%rbp)
    ja      .L2
    movl    -4(%rbp), %eax
    movq    .L4(,%rax,8), %rax
    jmp     *%rax
    .section        .rodata
    .align 8
    .align 4
.L4:

    .quad   .L2
    .quad   .L3
    .quad   .L5
    .quad   .L6
    .quad   .L7
    .quad   .L8
    .quad   .L9
    .quad   .L10
    .text
.L3:

    movl    $97, %edi
    call    putchar
    jmp     .L2
.L5:

    movl    $97, %edi
    call    putchar
    jmp     .L2
.L6:

    movl    $97, %edi
    call    putchar
    jmp     .L2
.L7:

    movl    $97, %edi
    call    putchar
    jmp     .L2
.L8:

    movl    $97, %edi
    call    putchar
    jmp     .L2
.L9:

    movl    $97, %edi
    call    putchar
    jmp     .L2
.L10:

    movl    $97, %edi
    call    putchar
    nop
.L2:

    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

从测试中我觉得开关/盒子更好,因为它不必通过前面的条目来找到匹配。

我建议尝试使用gcc -S选项生成程序集以检查asm以查看。

答案 4 :(得分:1)

TL; DR版

对于如此少的数值,速度上的任何差异都将是无法估量的,并且你最好坚持使用更直接,更易于理解的版本。直到您需要开始搜索包含数千到数百万条目的表格,您才会想要比线性有序搜索更聪明的条目。

James Michener版本

尚未提及的另一种可能性是进行分区搜索,如下所示:

if ( a > 4 )
{
  if ( a > 6 )
  {
    if ( a == 7 ) // do stuff
    else // a == 8, do stuff
  }
  else
  {
    if ( a == 5 ) // do stuff
    else // a == 6, do stuff
  }
}
else
{
  if ( a > 2 )
  {
    if ( a == 3 ) // do stuff
    else // a == 4, do stuff
  }
  else
  {
    if ( a == 1 ) // do stuff
    else // a == 2, do stuff
  }
}

a的任何值执行的测试不超过三次。当然,对于a的任何值,也不会执行 less 三次测试。平均而言,当大多数输入为7时,应该提供比天真的1-8搜索更好的性能,但是......

与性能相关的所有内容一样,规则是衡量,不要猜测。编写不同的版本,对其进行分析,分析结果。对于如此少的值进行测试,很难获得可靠的数字;你需要为给定的值执行每个方法数千次才能得到有用的非零时间测量(这也意味着方法之间的任何差异都会非常小)。

这样的东西也会受到编译器优化设置的影响。您需要构建不同的优化级别并重新运行测试。

只是为了咯咯笑,我编写了我自己的版本,测量了几种不同的方法:

  • naive - 按顺序从1到8的直接测试;
  • sevenfirst - 首先检查7,然后检查1 - 6和8;
  • eightfirst - 以相反的顺序从8到1进行检查;
  • partitioned - 使用上面的分区搜索;
  • switcher - 使用switch语句代替if-else;

我使用了以下测试工具:

int main( void )
{
  size_t counter[9] = {0};
  struct timeval start, end;
  unsigned long total_nsec;

  void (*funcs[])(int, size_t *) = { naive, sevenfirst, eightfirst, partitioned, switcher };

  srand(time(NULL));
  printf("%15s %15s %15s %15s %15s %15s\n", "test #", "naive", "sevenfirst", "eightfirst", "partitioned", "switcher" );
  printf("%15s %15s %15s %15s %15s %15s\n", "------", "-----", "----------", "----------", "-----------", "--------" );

  unsigned long times[5] = {0};

  for ( size_t t = 0; t < 20; t++ )
  {
    printf( "%15zu ", t );
    for ( size_t f = 0; f < 5; f ++ )
    {
      total_nsec = 0;
      for ( size_t i = 0; i < 1000; i++ )
      {
        int a = generate();
        gettimeofday( &start, NULL );
        for ( size_t j = 0; j < 10000; j++ )
          (*funcs[f])( a, counter );
        gettimeofday( &end, NULL );
      }
      total_nsec += end.tv_usec - start.tv_usec;
      printf( "%15lu ", total_nsec );
      times[f] += total_nsec;
      memset( counter, 0, sizeof counter );
    }
    putchar('\n');
  }

  putchar ('\n');
  printf( "%15s ", "average:" );
  for ( size_t i = 0; i < 5; i++ )
    printf( "%15f ", (double) times[i] / 20 );
  putchar ('\n' );
  return 0;
}

generate函数生成从18的随机数,加权,以便7出现一半时间。我为每个生成的值运行10000次以获得可测量的时间,生成1000个值。

我不希望各种控制结构之间的性能差异被// do stuff代码淹没,因此每种情况只会增加一个计数器,例如

if ( a == 1 )
  counter[1]++;

这也让我有办法验证我的数字生成器是否正常工作。

我遍历整个序列20次并对结果取平均值。即便如此,数字在不同的运行中也会有所不同,所以不要太信任它们。如果没有别的,他们表明这个级别的变化不会带来巨大的改进。例如:

     test #           naive      sevenfirst      eightfirst     partitioned        switcher
     ------           -----      ----------      ----------     -----------        --------
          0             121             100             118             119             111
          1             110             100             131             120             115
          2             110             100             125             121             111
          3             115             125             117             105             110
          4             120             116             125             110             115
          5             129             100             110             106             116
          6             115             176             105             106             115
          7             111             100             111             106             110
          8             139             100             106             111             116
          9             125             100             136             106             111
         10             106             100             105             106             111
         11             126             112             135             105             115
         12             116             120             135             110             115
         13             120             105             106             111             115
         14             120             105             105             106             110
         15             100             131             106             118             115
         16             106             113             116             111             110
         17             106             105             105             118             111
         18             121             113             103             106             115
         19             130             101             105             105             116

   average:      117.300000      111.100000      115.250000      110.300000      113.150000

数字以微秒为单位。代码是使用gcc 4.1.2构建的,没有在SLES 10系统 1 上运行优化。

因此,对于1000个值运行每个方法10000次,平均超过20次运行,总变化大约为7微秒。这真的不值得一试。对于仅在8个不同值中搜索并且不会运行超过“多次”的内容,无论使用何种方法,您都不会看到任何可衡量的性能改进。坚持使用最容易阅读和理解的方法。

现在,要搜索包含数百到数千到数百万个条目的表格,您肯定希望使用比线性搜索更智能的东西。

<小时/> 1。不言而喻,但上述结果仅适用于使用此特定编译器构建的此代码并在此特定系统上运行。<登记/>

答案 5 :(得分:0)

应该有一点点差别,但这取决于平台和比较的性质 - 不同的编译器可能会以不同的方式优化这样的东西,不同的架构也会有不同的效果,而且还取决于比较的内容实际上是(例如,如果它是比简单的原始类型比较更复杂的比较)

测试您实际要使用的特定情况可能是一种很好的做法,如果这可能实际上是性能瓶颈。

或者,switch语句(如果可用)对于任何独立于order的值都应具有相同的性能,因为它是使用内存中的偏移量实现的,而不是连续的比较。

答案 6 :(得分:-5)

可能不是,您仍然具有相同数量的条件,并且它们中的任何一个仍然可以评估为true,即使您首先检查a == 7,所有其他条件都可能为真,因此将进行评估。

如果a == 7在程序运行时可能会更快地执行,则执行的代码块 - 但基本上你的代码仍然是相同的,具有相同数量的语句。