雖然心裡早就有所準備,但跟硬體有關的內容實在是難度太高,學到後來也逐漸感受到精疲力竭。幸好依舊努力不曾放棄,希望這場冒險終將有所回報。
終於完成兩大 Low-Latency C++ 主題,當然要來好好慶祝一下!在這個值得紀念的一天,就用一個略微浪漫生活瑣事當作例子,輕鬆地來講解這些觀念吧 :)
🎁小故事時間 —— 低延遲的紀念日
紀念日那天,資深工程師 Yoyo 絞盡腦汁,準備了一份禮物。程式很簡單卻充滿了心意:
struct Gift {
int ribbon;
int chocolate;
};
Gift gift{1, 3};
void give(const Gift& g) {
// low latency: 資料就在手邊,不必到處找
printf("Happy Anniversary!\n");
}
Yoyo 看著程式,忽然覺得感情其實也很像 Hardware-Aware Memory & CPU Architecture。
CPU 最怕的,不一定是工作很多,而是無窮無盡的等待。現代 CPU 的運算速度遠高於主記憶體的存取速度,因此處理器會透過多層次的 Cache Hierarchy,盡可能把「接下來可能需要的資料」放在離 CPU 更近的位置。如果資料就在 L1 Cache,CPU 可以非常快速地取得;如果需要從 L2、L3 Cache 取得,延遲會逐層增加;而一旦發生 Cache Miss,必須一路存取到主記憶體,CPU 就可能需要付出更高的 latency。
這也是為什麼在 Low-Latency 的世界中,Memory Access Pattern 往往和單純的演算法複雜度一樣重要。而每次讓 CPU 加速的渴求,像是一場無法終止的談判,只能不斷地理性溝通及修復問題。CPU 為了維持高吞吐量,還會使用 Pipelining、Out-of-Order Execution、Branch Prediction 等機制,盡可能把 pending 隱藏在其他 issue 之後。因此低延遲最佳化從來不只是追求最快的速度,而是與 CPU 的互相理解。
Yoyo 想了想,最後決定不要把禮物藏在很遠的地方。她把巧克力放進盒子,把緞帶綁好,最後把一張小卡片放在最上面。沒有多餘的包裝,沒有複雜的流程,只有雙方的默契與體諒。就像一個好的 low-latency system,資料靠近 CPU,心意也靠近你。
Gift gift{1, 3};
if (anniversary) {
give(gift);
}
她把盒子遞出去。他打開卡片,上面只有一句:「我不知道怎麼讓每一秒都變快,但我想讓每一次你回頭,都能第一時間看到我。」這一次,沒有 cache miss。因為他立刻笑了,memory access 讓往日的回憶瘋狂湧現。而她的 latency,無限趨近接近零。他們之間的愛,亦沒有時差。
真正的低延遲不只是做得快,而是懂得資料在哪裡、CPU 怎麼跑,以及如何避免不必要的等待。真心對待系統的bottleneck,思考 CPU 執行時的實際路徑,才能找到效能的核心問題並找到解決方案。那些看起來很小的等待與卡頓,最後可能都會累積成巨大的效能差異。
所以,低延遲其實富有詩意 —— 因為重要的東西,就應該被放在離你最近的地方。讓資料保持良好的 locality,讓 CPU 少等待,也讓重要的資訊盡可能靠近需要它的位置。