到目前為止,我們的研究已十分明確!我們希望了解以 Linux 預設排程器、「快取最佳化」排程器,以及手動限制執行緒位置三種方式,自動排程執行緒時對效能造成的影響。

如同許多學校專案一樣,我們開始得有點接近截止期限,因此一開始就採取相當線性的思路,直接進行測試。我們的實驗設定是追蹤一致性動作(透過跨 socket 發送的 read-for-ownership 請求)以及不同設定下的延遲數據,試圖精確找出執行環境改變的哪個層面造成了效能提升或下降。由於所有基準測試的執行緒數量都小於或等於單一核心的容量,我們用 numactl 將基準測試限制在單一 socket 作為「理想化」跨核心放置的對照組。

這帶來了我們整個研究中最引人注目的結果之一:標準 Linux 排程器與專用的 LAVD 排程器在我們的基準測試上表現大致相同,但使用 numactl 將執行緒排程限制在單一 socket 後,執行時間最多可改善 3 倍!

runtime_speedup_vs_eevdf_2s.png

Figure 1: 基準測試執行時間的標準化加速比

因此,如果您目前使用多 socket 機器以未滿載的方式執行工作負載,我建議至少嘗試使用 numactl 將工作負載固定在單一 LLC 域或一組核心上——您可以在多執行緒共享記憶體程式中獲得極大的加速。這主要是因為 Linux 負載平衡器會盡量確保所有 socket 的負載大致相等(只要執行緒數量允許),即使這樣會對宿主程式造成不利影響。

這個觀察的顯著例外是來自 DCPerf 的 mediawiki 基準測試——為什麼它的執行時間幾乎沒有變化?也許是因為快取命中率其實並沒有改善?

好的,我們可以檢查這是否屬實。讓我們看看 read-for-ownership 缺失(例如 L3 想要寫入某個資料但沒有該資料,因此要求其他核心讓它擁有資料)的數量是否有所改變。當存在強烈的寫入共享時,我們預期這個數字會上升,因為共享的快取行應該會在兩個 socket 的 L3 快取之間來回跳動。

l3_rfo_mpki_newdata.png

Figure 2: L3 快取 Read-for-Ownership 缺失(每千指令)

唉。現在我們有兩個謎團。

  1. 為什麼 mediawiki 即使 L3 RFO 有改善,執行時間卻完全沒有加速?
  2. perlbench 到底發生了什麼事?是什麼讓它加速這麼多?

可惜的是,我們在截止期限前都無法回答這兩個問題——事實上,我們在論文到期當天才發現這個關係。因此在嚴重睡眠不足且血液中充滿大量咖啡因的情況下,我們採取了長久以來的做法:忽略這個問題,在最終報告中寫明我們無法找出問題所在,然後繼續產生更多圖表。我們在晚上 11 點 45 分完成報告,匆忙趕到 Siebel 並盡快把報告從教授門縫塞進去。提交後,我認為我們之中沒有人再認真想過這件事。

轉眼到了學期結束幾週後,我正在清理這個專案使用的追蹤資料。出於某個後來已忘記的原因,我好奇每個基準測試的 MIPS 是多少。

mips_newdata.png

Figure 3: 以每秒百萬指令數 (MIPS) 測量的吞吐量

這……完全說不通。吞吐量大幅上升,但執行時間卻沒有改變?我們到底是用什麼指令執行基準測試的?

cd /home/pradyun/pg_work/dcperf/
sudo ./perf_collect_mw "$JOBNAME" ./benchpress_cli.py run oss_performance_mediawiki_mlp \
    -i '{"scale_out": 1, "client_threads": 32, "duration": "10m"}' &

……天啊。持續時間被固定為 10 分鐘。這個指令深埋在我們的測試基礎設施中,而且在整個專案期間被重複使用了這麼多次,以至於我們完全忘記它的存在。

因此,我們一舉犯下了好幾個錯誤。加速比的計算假設指令數量保持不變,但這個基準測試並非如此。我們不僅意外地把另一個單 socket 加速報告成「沒有變化」,還不知怎麼地掩蓋了讓 LAVD 看起來更好的那個資料點。對於後者,我的理論是 mediawiki 的特性適合 LAVD 原本設計的任務鏈模型。

附註:LAVD 是由 Changwoo Min 與 Igalia 的優秀夥伴們為了 Steam Deck 設計與最佳化的。他在 OSPM25 上有一場關於 LAVD 的精彩演講,您可以在這裡找到,強烈建議觀看!