簡體   English   中英

嵌套循環內外變量聲明的性能比較

[英]Performance comparison between variable declaration within or outside nested loop

案例一

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))
                    {

                    }
                }
            }

案例二

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))
                {

                }
            }
        }

在案例 I 和案例 II 中,變量 stop_time 在嵌套循環的外部或內部聲明。 哪一個在性能方面更好? 為什么?

該標准幾乎沒有提及性能,但有一個明確的規則,即優化不得改變可觀察到的副作用。

該標准沒有描述堆棧或堆的使用,因此編譯器在使用之前的任何時候在堆棧上為變量分配空間是完全合法的。

但最佳將取決於各種因素。 在最常見的架構上,在兩個地方進行所有堆棧指針調整是最有意義的 - 進入和退出。 在 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 這會生成三個相同的程序集

    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

許多與性能相關的問題的一般答案是“衡量它並親自查看”。 如果這很容易,那就去做吧。 在這種情況下,這容易。

有時,查看匯編代碼是件好事——如果代碼在您的兩種情況下相同(我猜是這樣),您甚至不需要測量其性能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM