簡體   English   中英

為什么 cmp 指令花費太多時間?

[英]Why does cmp instruction cost too much time?

我正在嘗試使用 libunwind(使用 linux perf)進行分析,使用perf top監視目標進程,我得到了這個組裝時間成本屏幕: trace_lookup 源代碼

  0.19 │       mov    %rcx,0x18(%rsp)                                                                                                                                    ▒
       │     trace_lookup():                                                                                                                                             ▒
  1.54 │       mov    0x8(%r9),%rcx                                                                                                                                      ▒
       │     _ULx86_64_tdep_trace():                                                                                                                                     ▒
  0.52 │       and    $0x1,%edx                                                                                                                                          ◆
  0.57 │       mov    %r14d,0xc(%rsp)                                                                                                                                    ▒
  0.40 │       mov    0x78(%rsp),%r10                                                                                                                                    ▒
  1.24 │       sub    %rdx,%r15                                                                                                                                          ▒
       │     trace_lookup():                                                                                                                                             ▒
  0.35 │       shl    %cl,%r12d                                                                                                                                          ▒
       │     _ULx86_64_tdep_trace():                                                                                                                                     ▒
  2.18 │       mov    0x90(%rsp),%r8                                                                                                                                     ▒
       │     trace_lookup():                                                                                                                                             ▒
  0.46 │       imul   %r15,%r13                                                                                                                                          ▒
       │     _ULx86_64_tdep_trace():                                                                                                                                     ▒
  0.59 │       mov    %r15,0x88(%rsp)                                                                                                                                    ▒
       │     trace_lookup():                                                                                                                                             ▒
  0.50 │       lea    -0x1(%r12),%rdx                                                                                                                                    ▒
  1.22 │       shr    $0x2b,%r13                                                                                                                                         ▒
  0.37 │       and    %r13,%rdx                                                                                                                                          ▒
  0.57 │177:   mov    %rdx,%rbp                                                                                                                                          ▒
  0.43 │       shl    $0x4,%rbp                                                                                                                                          ▒
  1.33 │       add    %rdi,%rbp                                                                                                                                          ▒
  0.49 │       mov    0x0(%rbp),%rsi                                                                                                                                     ▒
 24.40 │       cmp    %rsi,%r15                                                                                                                                          ▒
       │     ↓ jne    420                                                                                                                                                ▒
       │     _ULx86_64_tdep_trace():                                                                                                                                     ▒
  2.10 │18e:   movzbl 0x8(%rbp),%edx                                                                                                                                     ▒
  3.68 │       test   $0x8,%dl                                                                                                                                           ▒
       │     ↓ jne    370                                                                                                                                                ▒
  1.27 │       mov    %edx,%eax                                                                                                                                          ▒
  0.06 │       shl    $0x5,%eax                                                                                                                                          ▒
  0.73 │       sar    $0x5,%al                                                                                                                                           ▒
  1.70 │       cmp    $0xfe,%al                                                                                                                                          ▒
       │     ↓ je     380                                                                                                                                                ▒
  0.01 │     ↓ jle    2f0                                                                                                                                                ▒
  0.01 │       cmp    $0xff,%al                                                                                                                                          ▒
       │     ↓ je     3a0                                                                                                                                                ▒
  0.02 │       cmp    $0x1,%al                                                                                                                                           ▒
       │     ↓ jne    298                                                                                                                                                ▒
  0.01 │       and    $0x10,%edx                                                                                                                                         ▒
       │       movl   $0x1,0x10(%rsp)                                                                                                                                    ▒
       │       movl   $0x1,0x1c8(%rbx)                                                                                                                                   ▒
  0.00 │     ↓ je     393                             

對應的源碼在這里trace_lookup source code ,如果我沒有看錯的話,這條熱路徑cmp指令對應的代碼行數是296行,但是不知道為什么這一行這么慢,而且大部分時間都在消耗?

命令cmp %rsi,%r15被標記為具有巨大的開銷,因為它等待通過mov 0x0(%rbp),%rsi命令從緩存或 memory 加載數據。 該命令可能存在 L1 甚至 L2 緩存未命中。

對於代碼片段

       │     trace_lookup():
  0.50 │       lea    -0x1(%r12),%rdx
  1.22 │       shr    $0x2b,%r13     
  0.37 │       and    %r13,%rdx      
  0.57 │177:   mov    %rdx,%rbp      
  0.43 │       shl    $0x4,%rbp      
  1.33 │       add    %rdi,%rbp      
  0.49 │       mov    0x0(%rbp),%rsi 
 24.40 │       cmp    %rsi,%r15      
       │     ↓ jne    420    

當前 function 的分析事件的 24% 占到了 cmp 指令。 采樣分析的默認事件是“cycles”(CPU 時鍾周期的硬件事件)或“cpu-clock”(線性時間的軟件事件)。 因此,大約 24% 的采樣中斷確實中斷了這個 function 被報告為這個 cmp 命令的指令地址。 分析和現代亂序 CPU 可能存在系統性偏差,當報告成本不是針對運行緩慢的命令,而是針對未快速完成執行(退出)的命令。 如果 %rsi 寄存器值不等於 %r15 寄存器值,則此 cmp+jne 命令對(融合 uop)將更改程序的指令流。 對於古代這樣的命令應該只讀取兩個寄存器並比較它們的值,這是快速的並且不應該花費 function 執行時間的 1/4。 但是現代 CPU 寄存器不僅僅是存儲值的 32 位或 64 位位置,它們還有一些用於亂序引擎的隱藏標志(或重命名技術)。 在您的示例中,有mov 0x0(%rbp),%rsi確實更改了 %rsi 寄存器。 這個命令是從 memory 通過地址 *%rbp 加載的。 CPU 確實開始將此加載到緩存/內存子系統中,並將 %rsi 寄存器標記為“從內存加載掛起”,繼續執行指令。 下一條指令有可能不需要該加載的結果(這需要一些時間,例如Haswell :L1 命中 4 個 cpu 周期,L2 命中 12 個,L3 命中 36-66 個,額外 50-100 ns緩存未命中和 RAM 讀取)。 但是在你的情況下,下一條指令是 cmp+jne 並從 %rsi 讀取,並且在將來自 memory 的數據寫入 %rsi 之前,該指令無法完成(CPU 可能會在 cmp+jne 執行過程中阻塞或多次重啟那個命令)。 因此, cmp 有 24% 的開銷,因為 mov 確實錯過了最近的緩存。 使用更高級的計數器,您可以估計它錯過了哪個緩存,以及哪個緩存/內存層最常為請求提供服務。

對應的源碼在這里trace_lookup源碼,如果我沒有看錯的話,這個hot path cmp指令對應的代碼行數是296行,但是不知道為什么這一行這么慢,而且大部分時間都是耗費的?

對於如此短的 asm 片段,很難在 trace_lookup 的源代碼中找到相應的代碼行,也很難找到什么值以及為什么不在 L1/L2 緩存中。 您應該嘗試編寫簡短的可重現示例。

暫無
暫無

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

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