# 模式 1: performance-snapshot ## Elapsed Time worst ![image](https://hackmd.io/_uploads/rJl7_Wm2ke.png) best ![image](https://hackmd.io/_uploads/ByvYiW73Jx.png) #### IPC (Instructions per cycle) 每時鐘週期內成功執行的指令數,是整數,SIMD 指令也只被視作一個。 若 cache miss 高,SIMD 指令多,指令不夠時會低。 #### SP GFLOPS (單精度浮點) 單精度浮點計算(十億)/秒 #### DP GFLOPS (雙精度浮點) 雙精度浮點計算(十億)/秒 #### Average CPU frequency Average actual CPU frequency. Values above nominal frequency indicate that the CPU is operating in a turbo boost mode. ## Locical Core Utilization worst ![image](https://hackmd.io/_uploads/r1yVdZX21l.png) best ![image](https://hackmd.io/_uploads/SJlT8MXhJl.png) #### 邏輯/物理 cpu 使用率 我的計算機上有 20 logical core, 6 E-core, 8 P-core。 每個 P-core 由於 HT ,算為兩 logical core。 ## Microarchitecture Usage 提前須知: cpu 大致執行架構和一些術語。 Intel Skylake Microarchitecture Graph [(src)](https://en.wikichip.org/wiki/intel/microarchitectures/skylake_%28client%29) ![image](https://hackmd.io/_uploads/S1Tkmm72kg.png) CPU 大致工作流程: cpu 分為前後端,前端負責分支預測和把指令解碼為 uOp,後端負責執行 uOps 與儲存系統。 ### Micro-operation (uOp) Instruction Set Architecture (ISA 指令集,如: CISC, RISC) 中的指令解碼出來的更細分化的指令。 ### 前端 1. 從 L1 I-Cache 取指令。 2. Branch Predictor Unit (BPU) 預測程式碼執行路徑。 3. Instreuction Fetch (IF) 依照分支預測結果取出預測執行的指令交給 Decoder。 4. 將 ISA 指令轉換為 Micro-operations (uOp),並存入 DSB (Decoded Stream Buffer 或 uOp cache) 5. uOps 進入 Instruction Decode Queue (IDQ) ### 後端 6. uOps 進入 Reservation Station (RS),等待 Execution Unit (EU) 可用。 7. RS 發送到不同的 EU (ALU, EPU, AVX, Load/Store Unit, Branch Unit) 8. uOp retirement (退休),更新記憶體/暫存器。 上述行為會發生的延遲情況。 note: chatGPT gen | **問題類型** | **影響的環節** | **結果** | **Pipeline Slot 是否空閒?** | **Retiring 會下降嗎?** | |-------------|--------------|--------|--------------------|----------------| | **指令快取命中率低(I-Cache Miss)** | 前端 | 指令取不到 | ✅ 是 | ✅ 下降 | | **分支預測錯誤(Branch Misprediction)** | 前端 | 清空管線 | ✅ 是 | ✅ 下降 | | **指令解碼過慢(Decoding Bottleneck)** | 前端 | uOps 產生太慢 | ✅ 是 | ✅ 下降 | | **資源調度延遲(RS 滿載或資源依賴)** | 中端 | uOps 等待執行 | ✅ 是 | ✅ 下降 | | **執行單元不足(EU Underutilization)** | 後端 | uOps 執行速度慢 | ✅ 是 | ✅ 下降 | | **記憶體存取延遲(Memory Latency)** | 後端 | 記憶體取數據慢 | ✅ 是 | ✅ 下降 | ### Branch Prediction Branch Predictor 在 Fetch 階段之前運作,它的目的是減少「條件跳轉指令(如 if, loop, function call)」導致的流水線清空(Pipeline Flush)。 ex: `if (x > 0) { doA(); } else { doB(); }` 邏輯上, cpu 應該要等 x 確定才能執行 doA or doB,但 cpu 能提前解碼 doA 或 doB,並存入 uOp,然後在後端給 EU 執行。但若 cpu 猜錯了,錯誤的 uOps 需要被清空 (pipeline flush),caches 回朔,並重新解碼正確的指令。 ### OoO (Out-of-Order) 和 Retirement Out-of-Order execution (亂序執行): cpu 後端會根據可用資源和指令依賴性,動態調整 uOps 執行順序,以最大化使用 EUs。 也就是說 uOps 被執行時不一定按照程式碼順序。但最後 cpu 還需要一個機制來確認最終結果與順序執行一致,此機制稱為 retirement。uOps 執行完後還需在 Record Buffer (ROB) 等待 retirement。retirement 的順序與程式碼相同,有點像 TCP 的移動窗口 ACK。 | **TCP Sliding Window ACK** | **CPU Backend uOps Execution and Retirement** | |--------------------------------|------------------------------------------------------------| | 非最後的封包到達並進入緩存等待 | uOps 預先執行好 | | 最前方封包來到 | 依賴鍊中最後一個 uOps 完成 | | 整列封包都能被 ACK,窗口向前 | 整列 uOps 進行 retirement, ROB 釋放資源 | ex: ``` load r1, [mem] -No.1 add r2, r1, 5 -No.2 mul r3, r2, 3 -No.3 add r4, r3, 7 -No.4 sub r6, r7, 2 -No.5 ``` No.1 到 No.4 會有 retirement dependence。由於從 RAM 找資料慢,會卡住。No.5 可以先執行並retirement。 在找到 r1 後,cpu會執行並一次 retirement No.1 到 No.4。 ### pipeline slot 處理一個 uOp 需要的硬體資源,可以視為 CPU 整體處理資源的度量。 Use xxx% of pipeline slots 代表將使用 cpu xxx% 的整體資源。 ### TLB (Instruction Translation Lookaside Buffer 轉譯後備緩衝區) hierarchy 頁表的快取,頁表用於轉換虛擬地址和物理地址。此 cache 加速這件事。 TLB cache 有三個。 L1 I-Cache 旁有 ITLB (Instruction TLB)。 L1 D-Cache 旁有 DTLB(Data TLB)。 L2 Cache 旁有 STLB (Second TLB)。 若 TLB Cache 都 miss了,則需要進行 "硬體頁面走查"(hardware page walk)。 ### xxx% of clock tick 代表 xxx 佔用了整體時鐘週期的百分比。 worst P-core ![image](https://hackmd.io/_uploads/B1aIjGX3Je.png) 幾個單位為 pipeline slots 的加起來會是 100,代表整個執行的資源花費分佈。 11.5 (from retiring) \+ 3.6 (from front-end bound) \+ 0.5 (from bad speculation) \+ 84.5 (from back-end bound) = 100.1 ### Retiring 做有用的事佔整體的資源的比率。 #### Light Operation 解碼出來需 1 uOp 的指令使用的資源的比率。 #### Heavy Operation 解碼出來需 2+ uOps 的指令使用的資源比率。 ### Front-End Bound 前端受限 前端用的資源比率。 #### Front-End Latency 指令存取,頁表緩存存取,分支錯誤預測重新存取指令 所使用的資源比率。 #### Front-End Bandwidth 前端解碼器的使用資源比率。 ### Bad Speculation (糟糕的預測) #### Branch Mispredict 分支預測錯誤使用的資源比率。 Branch Mispredict 後有些指令仍在 pipeline 中移動,被算於此。 Branch Mispredict 後 caches 的回朔開銷,被算於此。 <!-- CPU 會不斷解碼和執行 uOps,當遇到 if 時,會猜測分支,並解碼和執行情中一個,若猜錯了,以下事情會被執行。 - 執行單元需要清空 - 寄存器的狀態需要回溯 - Pipeline 需要重新載入正確的指令 --> #### Machine Clears CPU 遇到異常狀況時,需要強制清空 Pipeline 並重新執行,此行為佔用的資源比率。 這意味著,解碼好的 uOps 會被捨棄,並重新載入指令。 異常狀況: - Memory Ordering Violations(記憶體順序違規) - Self-Modifying Code(自我修改程式碼) - Certain Loads to Illegal Address Ranges(存取非法記憶體位址) ### Back-End Bound 後端佔用的資源比率,例如 除法器過載 或 資料緩存缺失。 被切分為 Memory Bound 和 Core Bound. #### Memory Bound Memory load/store 使用的資源比率。 ##### L1 Bound L1 hit 佔 cpu 週期比率。 ###### DTLB Overhead 此指標表示頁表沒在 DTLB 命中,而去 STLB 或 hardware page walk 的比例。 ###### Load STLB Hit 此指標表示頁表在 STLB 命中比例。 ###### Load STLB Miss 此指標表示頁表沒在 STLB 命中,而去 hardware page walk 的比例。 ###### Loads Blocked by Store Forwarding 如果先前的一個 store 指令正在寫入某個記憶體位置,而 load 指令正好需要讀取該數據,則 load 可以避免等待主記憶體,直接從 store 獲取數據,這個機制稱為 "store forwarding"。此時若 store 寫入的區塊 小於 load 讀取的區塊,此時會阻塞一段時間。此指標測量此類阻塞載入的效能損失。 ###### L2 Bound This metric shows how often machine was stalled on L2 cache. ##### L3 Bound This metric shows how often CPU was stalled on L3 cache, or contended with a sibling Core. ###### L3 Latency This metric show the fraction of cycles in which demand load occur and hit L3 Cache. 如果高,說明在讀取資料時,都命中於 L3 Cache。若低,則可能資料讀取發生於 DRAM。 ###### SQ Full SQ 滿載的 cpu 週期比率,考慮到所有存取類型以及 硬體同步多執行緒線程。 如果高,則 L2 Cache 的存取請求過多。 Super Queue (SQ): 一個隊列,管理 L2 Cache 的存/取與送往 Uncore 的請求。 ##### DRAM Bound 卡在 DRAM 的 cpu 週期比率。 ###### Memory Bandwidth 因 DRAM 帶寬不足而卡住的 cpu 週期比率。此指標不集中來自其他線程/核心/套接字,請查看 Uncore counters。 在 NUMA multi-socket systems, 考慮加強數據局部性。 ###### Memory Latency 因 DRAM 延遲而卡住的 cpu 週期比率。此指標不集中來自其他線程/核心/套接字,請查看 Uncore counters。 考慮優化數據布局,或使用 Software Prefetches (through the compiler) [gcc_opt_fprefetch-loop-arrays](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fprefetch-loop-arrays),這會在 `-O2` 時自己開啟。 或者你打算通過手動控制,可以查看此 api ,[gcc_api_index-\__fbuiltin__fprefetch](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fprefetch) ##### Store Bound 卡在 store operation 的 cpu 週期比率。這很少發生。 ###### Store Latency cpu 處理 long-latency store misses 的 cpu 週期比率。(通常是缺乏 L2 Cache) ###### Split Store 一個 Cache line 是 64 bits,資料移動是以快取行為單位。integer, float, double 都小於此值,但有些未對齊的此類資料可能會跨越多快取行。Intel 通過 split registers 加快了問題,但問題仍可能存在。 #### Core Bound 非 Memory 的問題,和 EU 以及 OOO Resource 相關的。 Backend Out-of-Order (OOO) Resources 後端亂序執行資源: 一群 cache, register 或其他硬件用於實現 OoO。 ex: - Reorder Buffer (ROB) - 重排序緩衝區,追蹤 uOps 的順序,確保 retirement 正確。 - Reservation Station (RS) - 予約站, uOps 儲存的地方。 - Load/Store Queue (LSQ) - 加載/存儲佇列 - Physical Register File (PRF) - 物理暫存器,OoO 時暫存運算結果。 - Execution Units (EUs) - 執行單元 以上硬體他們可能會出現的問題 1. ROB 滿了或 ROB 回朔 due to BP 2. RS 滿了,通常是 EUs 過載 3. LSQ 滿了或 Machine Clear due to 記憶體非法存取 4. PRF 滿了,可能會存到 ROB。 5. 特定的 EU 滿了,該類指令會延遲,如 SIMD 單元滿載。 worst E-core ![image](https://hackmd.io/_uploads/rJk_izXnkx.png) best ![image](https://hackmd.io/_uploads/B1lLxDGQ3ye.png) ## Memory Bound worst ![image](https://hackmd.io/_uploads/Bk5rubQhJe.png) Cache Bound 是卡於 L1, L2, L3 Cache 的時間。 DRAM Bound 是卡於 DRAM 的時間 best ![image](https://hackmd.io/_uploads/BJCHVH721e.png) ## Vectorization (向量化) 提前知識: 向量化 ISA 中除了標準的指令集,還有 SIMD 指令集。 標準指令集執行的是 Scalar (純量) Operation,如 `ADD r3, r1, r2` SIMD 指令集執行的是 Packed (打包) Operation,如 `ADDPS xmm1, xmm2`,一次處理多個數據。 常見的 SIMD 指令集如下,他們的差異在於一次能處理的資料量 (xxx-bits)。 ### 常見的 SIMD 指令集 | 指令集 | 暫存器 | 運算寬度 | 典型用途 | |--------|--------|---------|---------| | **MMX** | 64-bit | 64-bit | 早期的多媒體加速(已淘汰) | | **SSE** | 128-bit | 128-bit | 4 個 `float` 或 2 個 `double` | | **AVX** | 256-bit | 256-bit | 8 個 `float` 或 4 個 `double` | | **AVX-512** | 512-bit | 512-bit | 16 個 `float` 或 8 個 `double` | | **NEON(ARM)** | 128-bit | 128-bit | ARM 架構的 SIMD 運算 | | **SVE(ARM)** | 128-bit ~ 2048-bit | 可變寬度 | 先進的向量擴展 | 這些 SIMD 指令是彙編指令,編譯器把 SIMD 彙編指令 封裝成 C++ SIMD Intrinsic (內建指令) ,以避免直接寫彙編。 具體的 API 根據不同編譯器會略有不同,但大致會遵守標準 interface。 ### **不同編譯器對 SIMD Intrinsic 的支持** | **架構** | **SIMD 指令集** | **標頭檔** | **GCC/Clang** | **MSVC** | **Intel ICC** | |---------|---------------|-----------|------------|--------|-----------| | x86 | MMX | `mmintrin.h` | ✅ | ✅ | ✅ | | x86 | SSE | `xmmintrin.h` | ✅ | ✅ | ✅ | | x86 | SSE2 | `emmintrin.h` | ✅ | ✅ | ✅ | | x86 | SSE3 | `pmmintrin.h` | ✅ | ✅ | ✅ | | x86 | AVX | `immintrin.h` | ✅ | ✅ | ✅ | | x86 | AVX-512 | `avx512fintrin.h` | ✅ | ✅ | ✅ | | ARM | NEON | `arm_neon.h` | ✅ | ❌ | ✅ | | ARM | SVE | `arm_sve.h` | ✅ | ❌ | ✅ | ### **AVX 與 AVX2 的主要區別** | **特性** | **AVX** | **AVX2** | |----------|--------|--------| | **發布年份** | 2011(Sandy Bridge) | 2013(Haswell) | | **位寬** | 256-bit | 256-bit | | **主要運算類型** | 浮點數(FP32、FP64) | 浮點數 + 整數運算(INT8、INT16、INT32) | | **支援的 SIMD 寬度** | 128-bit, 256-bit | 128-bit, 256-bit | | **支援的運算** | 僅浮點數運算 | **新增整數 SIMD 運算** | | **整數 SIMD 支持** | 128-bit(透過 SSE2) | **256-bit SIMD 整數運算(例:_mm256_add_epi32)** | | **資料存取** | `VEX-encoded` 記憶體存取 | **引入 `Gather` 操作(_mm256_i32gather_epi32)** | | **FMA(Fused Multiply-Add)** | 需 AVX+FMA3 支持 | **內建 FMA**(提升浮點計算效能) | | **Broadcasting(廣播)** | 只對浮點數 | **新增整數廣播** | 相關連結: 指令集 ref [x86_and_amd64_instruction_reference](https://www.felixcloutier.com/x86/) [x86 Instruction Set Reference](https://mudongliang.github.io/x86/html/file_module_x86_id_7.html) Intel ICC 內建指令 [Intel® Intrinsics Guide](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#) gcc 內建指令 [gcc X86 Built-in Functions](https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/X86-Built-in-Functions.html) worst ![image](https://hackmd.io/_uploads/B1ILu-73Jg.png) best ![image](https://hackmd.io/_uploads/rJPkvCBhyx.png) 註: performance-snapshot 只檢測浮點向量化且只檢測大核。 總 uOps = SP FLOPs + DP FLOPs + x87 FLOPs + Non-FP 註2: 這份代碼是浮點矩陣運算,但兩者 90+% 的 uOps 都在非浮點運算。可能是 Memory load/store 都算是整數運算。 #### Vectorization 打包浮點計算佔總計算的百分比,0% 代表完全 scalar。此指標不考慮 vector length,舊 SIMD 指令集不會影響此指標。 ### SP FLOPs 單精度浮點計算/總計算 的 uOps 百分比。如果 FMA 指令集 (Fused-Multiply-Add) 被使用,則可能超計。 SP FLOPs = 單精度下 Packed + 單精度下 Scalar #### Packed 單精度下,打包浮點計算 佔單精度的百分比。 From SP FP 是指來自 single precision floating point。 Packed = 128-bit + 256-bit ##### 128-bit 單精度下,128-bit 打包浮點計算 佔單精度的百分比。 ##### 256-bit 單精度下,256-bit 打包浮點計算 佔單精度的百分比。 #### Scalar 單精度下,純量浮點計算 佔單精度的百分比。 #### DP FLOPs 雙精度浮點計算/總計算 的 uOps 百分比。如果 FMA 指令集 (Fused-Multiply-Add) 被使用,則可能超計。 #### x87 FLOPs x87 是 x86 架構早期的浮點運算指令集,不常遇到。 #### Non-FP 非浮點運算的百分比。