Scaling-up BERT Inference on CPU (Part 1)
Hugging Face 詳細說明了一種在現代 CPU 上擴展 BERT 類模型推理的方法,證明了透過部署多個綁定到特定實體 CPU 核心的獨立模型實例,可以實現最大吞吐量。這種方法避免了同時多執行緒 (SMT) 和跨插槽 (cross-socket) 通訊的開銷,從而實現近乎線性的吞吐量擴展性。
Out-of-the-Box Framework Performance
在 AWS c5.metal 實例 (Intel Xeon Platinum 8275 CPU, 48 cores/96 threads) 上進行的初步基準測試顯示,在各種配置下,PyTorch (1.8.1) 的開箱即用 BERT 推理性能通常優於 Google TensorFlow (2.4.1)。這種性能差異歸因於底層執行技術:PyTorch 使用 OpenMP 和 Intel MKL (oneDNN),而 TensorFlow 依賴於 Eigen 和其自身的執行緒實作。
CPU Architecture and Thread Affinity
為了優化像 BERT 推理這樣主要由通用矩陣乘法 (GEMMs) 組成的 CPU 密集型任務,需要考慮特定的硬體因素:
Simultaneous Multi-Threading (SMT)
SMT (或 Hyper-Threading) 允許兩個軟體執行緒共享單個實體核心。然而,由於 BERT 推理是 CPU 密集型的,邏輯核心會競爭相同的執行資源,這意味著 SMT 不會提供性能增益,應避免使用 SMT 並改為僅使用實體核心。
NUMA and Socket Management
現代多插槽伺服器使用非一致性記憶體存取 (NUMA),其中每個 CPU 插槽管理其自身的記憶體子集。為了防止跨插槽通訊開銷導致的性能下降,必須使用 numactl 等工具將程序綁定到特定的核心與記憶體池。
Key Configuration Strategy:
- Thread Affinity: 綁定程序到特定的實體核心集 (例如,
numactl -C 0-47)。 - Memory Allocation: 確保記憶體分配在最靠近執行運算的核心所在的插槽上 (例如,
numactl -m 0,1)。
Core Count Scaling vs. Multi-Stream Inference
擴展推理資源主要有兩種方式,每種方式對延遲和吞吐量都有不同的影響。
Core Count Scaling (Strong Scaling)
這涉及增加分配給單個任務的核心數量以降低延遲。這種方法的有效性取決於問題規模:
- Small to Medium Problems: 使用單個插槽通常會產生最佳性能。
- Large Problems: 跨插槽通訊的開銷會被運算成本抵消,使得使用跨兩個插槽的所有可用核心是有益的。
Multi-Stream Inference (Instance Parallelism)
這種方法不為單個實例分配更多核心,而是分配多個獨立的模型實例,每個實例綁定到不重疊的實體核心子集。
例如,運行四個各綁定 12 個核心的獨立實例,與使用單個 24 個核心的實例相比,單個實例的延遲會略微增加,但總體吞吐量會增加 4 倍。這允許進行「智慧調度」,將請求路由到針對不同序列長度優化的特定實例 (例如,8 個核心用於短序列,24 個核心用於長序列)。
Batch Size Scaling and Throughput
Batch Size Scaling 涉及將全域批次 $B$ 分配到 $N$ 個實例中,其中每個實例在 $C/N$ 個核心上處理 $B/N$ 的子批次。
Latency and Throughput Trade-offs
- Latency: 批次的整體延遲由池中速度最慢的實例決定。要最小化延遲的最佳實例數量取決於問題規模;對於批次大小為 8 且序列長度為 128 的情況,4 個實例 (每個實例具有批次大小為 2 且使用 12 個核心) 提供了最佳結果。
- Throughput: 只要每個實例的工作負載按比例減少,吞吐量就會隨著實例數量的增加而近乎線性地擴展。這表明了最佳的硬體利用率。
Summary of Hardware Optimization Impact
透過正確調整執行緒親和性 (thread affinity) 和實例分配,企業可以顯著降低基礎設施成本。Hugging Face 指出,對於某些工作負載,如果問題規模不需要額外的核心來獲得最佳延遲,那麼從 48 核機器 ($4.848/h) 遷移到 8 核實例 ($0.808/h) 可以實現 6 倍的成本降低。