嵌套循环内部或外部的变量声明之间的性能比较

时间:2016-09-24 19:44:09

标签: c++

案例I

while (!channels_.empty())
            {
                for (auto it = channels_.begin(); it != channels_.end(); ++it)
                {
                    time_t stop_time;
                    if (it->second->active_playlist()->status_stop_time(it->second->on_air_row(), stop_time))
                    {

                    }
                }
            }

案例II

while (!channels_.empty())
        {
            time_t stop_time;
            for (auto it = channels_.begin(); it != channels_.end(); ++it)
            {

                if (it->second->active_playlist()->status_stop_time(it->second->on_air_row(), stop_time))
                {

                }
            }
        }

变量stop_time在Case I和II的嵌套循环之外或之内声明。哪一个在性能方面更好?为什么呢?

2 个答案:

答案 0 :(得分:2)

该标准对绩效的评价很少,但有一条明确的规则,即优化不得改变可观察到的副作用。

标准没有描述堆栈或堆使用情况,因此编译器在使用之前的任何时刻为堆栈上的变量分配空间是完全合法的。

但最佳将取决于各种因素。在最常见的体系结构中,最有意义的是在2个位置进行所有堆栈指针调整 - 进入和退出。在x86上将堆栈指针改为640而不是8,没有成本差异。

此外,如果编译器可以确定值没有改变,那么优化器也可以将分配提升出循环。

实际上,x86和基于arm的平台上的主流编译器(gcc,clang,msvc)会将堆栈分配聚合为单个向上和向下,并且在给定足够的优化器设置/参数的情况下提升循环不变量。

如有疑问,请检查组件或基准。

我们可以使用godbolt非常快速地证明这一点:

#include <vector>

struct Channel
{
  void test(int&);
};

std::vector<Channel> channels;

void test1()
{
  while (!channels.empty())
  {
    for (auto&& channel : channels)
    {
      int stop_time;
      channel.test(stop_time);
    }
  }
}

void test2()
{
  while (!channels.empty())
  {
    int stop_time;
    for (auto&& channel : channels)
    {
      channel.test(stop_time);
    }
  }
}

void test3()
{
  int stop_time;
  while (!channels.empty())
  {
    for (auto&& channel : channels)
    {
      channel.test(stop_time);
    }
  }
}

使用GCC 5.1和-O3 generates three identical pieces of assembly

    test1():
            pushq   %rbp
            pushq   %rbx
            subq    $24, %rsp
    .L8:
            movq    channels+8(%rip), %rbp
            movq    channels(%rip), %rbx
            cmpq    %rbp, %rbx
            je      .L10
    .L7:
            leaq    12(%rsp), %rsi
            movq    %rbx, %rdi
            addq    $1, %rbx
            call    Channel::test(int&)
            cmpq    %rbx, %rbp
            jne     .L7
            jmp     .L8
    .L10:
            addq    $24, %rsp
            popq    %rbx
            popq    %rbp
            ret

    test2():
            pushq   %rbp
            pushq   %rbx
            subq    $24, %rsp
    .L22:
            movq    channels+8(%rip), %rbp
            movq    channels(%rip), %rbx
            cmpq    %rbp, %rbx
            je      .L20
    .L14:
            leaq    12(%rsp), %rsi
            movq    %rbx, %rdi
            addq    $1, %rbx
            call    Channel::test(int&)
            cmpq    %rbx, %rbp
            jne     .L14
            jmp     .L22
    .L20:
            addq    $24, %rsp
            popq    %rbx
            popq    %rbp
            ret

    test3():
            pushq   %rbp
            pushq   %rbx
            subq    $24, %rsp
    .L26:
            movq    channels+8(%rip), %rbp
            movq    channels(%rip), %rbx
            cmpq    %rbp, %rbx
            je      .L28
    .L25:
            leaq    12(%rsp), %rsi
            movq    %rbx, %rdi
            addq    $1, %rbx
            call    Channel::test(int&)
            cmpq    %rbx, %rbp
            jne     .L25
            jmp     .L26
    .L28:
            addq    $24, %rsp
            popq    %rbx
            popq    %rbp
            ret

答案 1 :(得分:0)

许多与绩效相关的问题的一般答案是“衡量并亲眼看看”。如果这很容易,那就去做吧。在这种情况下, 很容易。

有时,查看汇编代码是件好事 - 如果代码在两种情况下相同(我猜它是相同的),你甚至不需要测量它的性能。