戰(zhàn):MB/s 曲線定位 L1/L2/L3 和內(nèi)存帶寬)
簡介LLCbench 緩存性能測試工具包面向 Linux 平臺下從事處理器緩存層次分析、體系結(jié)構(gòu)實(shí)驗(yàn)與平臺性能對比的開發(fā)者、教師和學(xué)生核心集成 MPBench、CacheBench 與 BLASBench其中 CacheBench 側(cè)重考察不同緩存層級、工作集大小和訪存模式下的性能變化可用于課程實(shí)驗(yàn)、硬件選型、編譯優(yōu)化驗(yàn)證及論文實(shí)驗(yàn)數(shù)據(jù)采集等場景測試模塊可單獨(dú)編譯選用。壓縮包共 81 個(gè)文件、約 83KB以 C 源碼與頭文件承載測試主體配合多套 Makefile 及 sys 平臺配置模板適配不同 Linux/Unix 環(huán)境另有 shell 腳本、ps 與 gp 繪圖腳本、HTML 說明和 README 文檔目錄按 cachebench、mpbench、blasbench、doc 等模塊劃分源碼、配置與說明文檔相互配套體量輕便而結(jié)構(gòu)完整。已有 873 人學(xué)習(xí)下載適合希望獲得可編譯的緩存基準(zhǔn)代碼、復(fù)現(xiàn)緩存性能實(shí)驗(yàn)并借助腳本生成結(jié)果圖表的讀者參考通過結(jié)果圖表可直觀比較不同平臺和編譯選項(xiàng)的緩存表現(xiàn)幫助定位訪存瓶頸也能為性能測試方案設(shè)計(jì)提供可復(fù)用的配置與排錯(cuò)線索。1. 新機(jī)器跑分和老機(jī)器持平我為什么又翻出 cachebench 這包老代碼上個(gè)月給一臺新裝的機(jī)器做處理器性能測試單核浮點(diǎn)分?jǐn)?shù)漲了快一半可一跑真實(shí)業(yè)務(wù)里的查表邏輯吞吐幾乎沒動(dòng)。這種“算力漲了、訪存沒跟上”的場面見得太多了最后能一句話說清問題的還是 cachebench 這種老派 linux 開源工具——喂它一個(gè)從 1KB 到幾百 MB 的數(shù)組它把每一檔的讀寫帶寬用 MB/s 打出來拐點(diǎn)在哪、L3 有多大、內(nèi)存實(shí)際能喂多快一眼就能看出來。llcbench.tar.gz 就是這套東西的源碼包里頭不止 cachebench還帶著單機(jī)浮點(diǎn)算力和多進(jìn)程消息傳遞兩套測試。適合誰要給出機(jī)器性能結(jié)論的人、要做選型對比的人、以及懷疑自己程序被內(nèi)存卡住卻拿不出證據(jù)的人。2. 解包到跑通把 llcbench.tar.gz 編成三個(gè)可執(zhí)行文件的完整路徑2.1 先看骨架再動(dòng)手別上來就 make拿到 tar 包第一件事不是編譯是看結(jié)構(gòu)。這類九十年代風(fēng)格延續(xù)下來的 C 工程不同人打包時(shí)目錄層級會變有可能頂層就是一個(gè)llcbench/也有可能解壓出來直接是一堆并列的子目錄編譯入口可能是頂層Makefile加一堆Makefile.arch模板也可能是一個(gè)make.def讓你改。先花三十秒看清結(jié)構(gòu)比后面make報(bào)錯(cuò)十次再去猜要快得多。包里三個(gè)主要工具分工很明確我一般按“先 cachebench、需要時(shí)再補(bǔ)另外兩個(gè)”的順序推進(jìn)子目錄名稱以實(shí)際解包為準(zhǔn)測什么什么時(shí)候必須跑cachebench單一地址流下的讀、寫、拷貝、memset 帶寬按數(shù)組尺寸逐檔輸出每次都要跑是判斷緩存層級的核心blasbench單機(jī) BLAS 級別的浮點(diǎn)算力單線程/多線程各一份需要把“算力”和“訪存”拆開歸因時(shí)mpbench多進(jìn)程之間消息傳遞的延遲與帶寬多路機(jī)器懷疑跨節(jié)點(diǎn)擴(kuò)展性時(shí)才需要Makefile / make.def 之類各架構(gòu)編譯模板復(fù)制一份改成自己的別直接改原文件# 不解壓也能看結(jié)構(gòu)先確認(rèn)頂層目錄名叫什么 tar tzf llcbench.tar.gz | head -40 # 解壓到獨(dú)立目錄避免污染工作區(qū) mkdir -p ~/bench tar xzf llcbench.tar.gz -C ~/bench cd ~/bench ls -la # 找編譯入口頂層 Makefile、各架構(gòu)模板、以及可能的 make.def find . -maxdepth 2 \( -iname Makefile* -o -iname make.def -o -iname *.mk \) | sort這段的目的只有一個(gè)確定“編譯入口文件名”和“產(chǎn)物會落在哪個(gè)目錄”。tar tzf只列名字不落盤幾百 KB 的包秒出結(jié)果find的-maxdepth 2是刻意的這類老工程子目錄里常有一堆測試腳本也叫 Makefile搜太深反而干擾判斷。如果find出來的模板里有Makefile.linux基本就是它了只有make.def的話通常是先改make.def里的CC、CFLAGS再回頂層make。2.2 編譯選項(xiàng)定死為什么我把 -O3 換成了 -O2壓測程序最怕的事情是編譯器比你還聰明。cachebench 的讀測試本質(zhì)上就是“把數(shù)組從頭到尾讀一遍把結(jié)果攢起來”如果編譯器判定這個(gè)循環(huán)的最終結(jié)果沒人用它會直接刪掉——你測出來的“帶寬”其實(shí)是空循環(huán)的速度。這類工程內(nèi)部一般會用一些手段防止被優(yōu)化掉把結(jié)果寫進(jìn)全局變量、提供保留結(jié)果的開關(guān)但你的-O3會額外打開向量化和更激進(jìn)的循環(huán)變換讓讀循環(huán)一次搬 32 字節(jié)甚至 64 字節(jié)測出來的數(shù)就變成了“SIMD 寬度測試”跨機(jī)器一比全是噪聲。所以我的固定做法是統(tǒng)一-O2不用-marchnative把gcc --version和uname -a一起寫進(jìn)報(bào)告。-marchnative在同一臺機(jī)器上自測很爽但只要涉及兩臺以上機(jī)器對比編譯選項(xiàng)不一致數(shù)據(jù)就沒有可比性——這是血淚經(jīng)驗(yàn)不是潔癖。另外注意 32 位與 64 位構(gòu)建指針寬度變了數(shù)組內(nèi)部地址計(jì)算的指令序列也會變同一個(gè) size 下的帶寬能差出百分之十幾。# 環(huán)境快照先落盤后面寫報(bào)告直接引用省得回頭補(bǔ) { uname -a gcc --version | head -1 lscpu | grep -Ei model name|mhz|cache|numa free -g } ~/bench/env.txt # 編譯優(yōu)先用頂層入口失敗再退回模板 make clean /dev/null 21 make # 若提示需要指定架構(gòu)改成 make linux 或 make -f Makefile.linux # 產(chǎn)物一般在各子目錄下按可執(zhí)行權(quán)限篩一遍 find . -maxdepth 2 -type f -perm -ux -name *bench* -print這段里make clean先跑一遍很關(guān)鍵老工程的 Makefile 常常不寫頭文件依賴改過配置后不清理會鏈接到上一次的.o癥狀是“明明改了選項(xiàng)結(jié)果一點(diǎn)沒變”。find那條是按可執(zhí)行權(quán)限加名字雙重過濾比ls */靠譜因?yàn)椴糠职姹緯旬a(chǎn)物放在bin/或上一級。編譯失敗最常見的兩類原因一是模板里的編譯器名寫死成了某個(gè)廠商的cc改CCgcc即可二是 64 位環(huán)境下老代碼里的指針強(qiáng)轉(zhuǎn)報(bào) warning 被-Werror升級成 error去掉-Werror就好不用去修代碼。2.3 先跑一次 smoke test把用法抄下來不同打包版本的參數(shù)集合會有出入所以第一步永遠(yuǎn)是“不帶參數(shù)跑一次”讓它自己把 usage 打出來。把 usage 存進(jìn)文件后面寫腳本時(shí)對著抄比記 memo 靠譜。# 不帶參數(shù)多數(shù)版本會打印用法后退出把 usage 留檔 cd ~/bench/llcbench # 目錄名以實(shí)際解壓結(jié)果為準(zhǔn) ./cachebench 21 | head -30 | tee ~/bench/usage.txt # 最小一次可跑只做讀測試最大數(shù)組給 64MB taskset -c 2 ./cachebench -r -s 67108864 | tee ~/bench/read_smoke.txt-r是讀測試-s給的是“掃到多大為止”的上限而不是單點(diǎn)尺寸——這一點(diǎn)最容易誤解很多人以為-s是“測一個(gè) 64MB 的數(shù)組”結(jié)果看到輸出里一長串不同尺寸的行就懵了。taskset -c 2把進(jìn)程釘在 2 號核上是為了排除調(diào)度器在核間搬進(jìn)程帶來的抖動(dòng)具體綁哪個(gè)核要看lscpu -e盡量避開 0 號核很多系統(tǒng)中斷默認(rèn)落在 0 號。跑完先別急著看圖head -20 read_smoke.txt人工確認(rèn)兩件事第一列到底是不是字節(jié)數(shù)、最后一列到底是不是 MB/s。這個(gè)確認(rèn)動(dòng)作只花十秒但決定了后面解析腳本寫得對不對。3. cachebench 的四種訪存模式讀、寫、拷貝、memset 的掃參與落盤3.1 四種模式分別對應(yīng)什么真實(shí)負(fù)載讀、寫、拷貝、memset 這四種模式不是湊數(shù)的它們對應(yīng)四類完全不同的訪存路徑。讀測試走的是 load 通路最接近查表、遍歷、哈希探測這類邏輯寫測試走 store 通路現(xiàn)代 CPU 上寫往往是“寫分配”會先把 cache line 讀進(jìn)來再改所以寫帶寬經(jīng)常比讀低一截看到這個(gè)不要以為機(jī)器有問題拷貝測的是memcpy類路徑同時(shí)吃讀寫兩條通路還要看有沒有非臨時(shí)存儲指令參與memset 則是純寫、且可以整行覆蓋硬件能做寫合并通常跑出來是四個(gè)里最“好看”的一個(gè)。如果你只跑一個(gè)-r很容易得出“這臺機(jī)器內(nèi)存帶寬 20GB/s”的結(jié)論但這個(gè)數(shù)字換到 memcpy 密集的場景完全不成立。我的習(xí)慣是四個(gè)模式一起跑落成四份原始文本后面既畫單條曲線也畫四條疊在一起的對比圖。mkdir -p ~/bench/raw # 固定綁核 固定最大尺寸四個(gè)模式各跑一遍-x 是每個(gè)尺寸的重復(fù)次數(shù) for mode in r w b f; do taskset -c 2 ./cachebench -$mode -s 268435456 -x 3 \ ~/bench/raw/${mode}.txt 21 done # 順手把每份輸出的前幾行打出來確認(rèn)格式?jīng)]變 for f in ~/bench/raw/*.txt; do echo $f; head -5 $f; done-x 3是每個(gè)尺寸重復(fù)三次取其中的最好值或中位數(shù)不同版本策略不同看輸出里是不是出現(xiàn)多列。重復(fù)的意義在于壓制瞬時(shí)噪聲后臺的一次日志刷盤、一次中斷就能讓某一個(gè)尺寸點(diǎn)掉 15%單次采樣根本分不清是噪聲還是真實(shí)的層級跌落。-s 268435456給到 256MB是為了確保一定能跨過 L3 落到內(nèi)存——現(xiàn)在不少服務(wù)器 L3 就有 32MB 到 64MB上限給小了曲線只到 L3 就結(jié)束了看不到內(nèi)存那一段。注意-s的單位在不同版本里可能按字節(jié)、也可能按元素個(gè)數(shù)解釋。跑完先看輸出的尺寸列最大值是不是接近你給的數(shù)字對不上就按元素個(gè)數(shù)理解乘以元素大小換算回來。3.2 想要更細(xì)的采樣點(diǎn)就改進(jìn)尺寸遞增步長這類工具內(nèi)部通常是按 2 的冪往上翻1KB、2KB、4KB……一路翻到上限。粗粒度掃的時(shí)候夠用但拐點(diǎn)會被夾在兩個(gè)點(diǎn)中間你只能得出“L2 在 256KB 到 512KB 之間”這種模糊結(jié)論。更麻煩的是純 2 的冪會引入組相聯(lián)索引沖突這個(gè)在 4.2 節(jié)單獨(dú)說。想細(xì)化就去源碼里找那條遞增語句把翻倍改成 1.25 倍。# 定位尺寸遞增的位置常見寫法是 size 1 或 size * 2 grep -n -E | ?1|\* ?2|size ?\* cachebench/*.c | head -20改之前把原文件備份一份改完重新make。步長改成 1.25 倍之后采樣點(diǎn)數(shù)會從十幾個(gè)漲到六十幾個(gè)單次運(yùn)行時(shí)間大概是原來的三四倍但拐點(diǎn)位置能精確到一個(gè)窄區(qū)間值得。改完記得在報(bào)告里注明“步長經(jīng)過修改”否則別人拿原版跑出來的點(diǎn)數(shù)和你對不上會以為你數(shù)據(jù)有問題。3.3 把文本表格變成 CSV解析腳本與列校驗(yàn)原始輸出是給人看的要做對比就得轉(zhuǎn)成表。不同版本的列數(shù)和列序有差異所以解析腳本不能寫死列號我的做法是“取行內(nèi)第一個(gè)數(shù)字當(dāng)尺寸、最后一個(gè)數(shù)字當(dāng)帶寬”再加一道合理性校驗(yàn)異常行直接丟掉并在日志里計(jì)數(shù)。import re import csv import pathlib # 版本間列序有差異用寬松策略首個(gè)數(shù)字尺寸(字節(jié))末個(gè)數(shù)字帶寬(MB/s) NUM re.compile(r[-]?\d(?:\.\d)?) rows, dropped [], 0 for p in sorted(pathlib.Path(raw).glob(*.txt)): for line in p.read_text(errorsignore).splitlines(): s line.strip() if not s or s.startswith(#): # 跳過注釋與空行 continue nums NUM.findall(s) if len(nums) 2: dropped 1 continue size, bw float(nums[0]), float(nums[-1]) # 合理性校驗(yàn)尺寸遞增、帶寬為正且不超過 1e7 MB/s if size 0 or bw 0 or bw 1e7: dropped 1 continue rows.append({mode: p.stem, size_bytes: int(size), mb_per_s: bw}) rows.sort(keylambda r: (r[mode], r[size_bytes])) with open(cache_curve.csv, w, newline) as f: w csv.DictWriter(f, fieldnames[mode, size_bytes, mb_per_s]) w.writeheader() w.writerows(rows) print(f保留 {len(rows)} 行丟棄 {dropped} 行) for r in rows[:5]: # 人工核對前幾行 print(r)這段的邏輯說明errorsignore是因?yàn)閭€(gè)別版本會在輸出里混入非 UTF-8 的控制字符不忽略會直接拋異常nums[-1]取最后一個(gè)數(shù)字而不是固定列是為了兼容“帶寬前面還有一列耗時(shí)”的版本bw 1e7這個(gè)上限看著隨意實(shí)際非常有用——解析錯(cuò)位時(shí)經(jīng)常把行號或時(shí)間戳當(dāng)成帶寬讀進(jìn)來會出現(xiàn)上千萬的荒謬值用一條上限就能整行攔掉。最后打印前五行是強(qiáng)制的人工校驗(yàn)環(huán)節(jié)絕不能省。提示把dropped打進(jìn)日志。如果丟棄比例超過兩成說明輸出格式和你預(yù)期不符先回去看原始文件別硬跑。4. 處理器性能測試的讀數(shù)MB/s 曲線怎么對上 L1、L2、L3 和內(nèi)存4.1 拐點(diǎn)的判定方法以及各層的典型量級把size_bytes取對數(shù)做橫軸、mb_per_s做縱軸畫出來是一條階梯最左邊平、然后掉一檔、再平、再掉最后到內(nèi)存段變成一條緩坡。判拐點(diǎn)我不用肉眼用相鄰點(diǎn)的衰減比bw[i]/bw[i-1] 0.85就記一次“掉層”記錄下來那個(gè)尺寸區(qū)間再和lscpu報(bào)的 cache 容量對照。層級典型容量單線程讀帶寬量級曲線特征L132KB64KB數(shù)十 GB/s 到上百 GB/s全程最平最高的一段尾部開始抖L2256KB2MB幾十 GB/s第一次明顯跌落跌幅常在三成以上L38MB64MB部分型號更大十幾到幾十 GB/s第二次跌落坡度變緩但不消失內(nèi)存按實(shí)際裝機(jī)單線程常見幾 GB/s 到十幾 GB/s緩坡尺寸再翻倍帶寬也基本不動(dòng)要強(qiáng)調(diào)的是“單線程”這三個(gè)字。cachebench 是單線程的量出來的內(nèi)存帶寬只代表一個(gè)核能拉起來的部分跟廠商宣傳的整機(jī)峰值帶寬差好幾倍是正常的。拿這個(gè)數(shù)去和整機(jī)峰值比然后得出結(jié)論“內(nèi)存性能不達(dá)標(biāo)”是新手最容易犯的錯(cuò)。4.2 2 的冪尺寸會造出假拐點(diǎn)這是我在這個(gè)工具上踩過最隱蔽的坑。數(shù)組尺寸全部取 2 的冪而且起始地址又是對齊的那么步長和 cache 的組相聯(lián)索引之間就會形成固定關(guān)系地址低位截出來的組號總是落在很少的幾個(gè)組上L1 或 L2 里明明還有空間卻因?yàn)闆_突缺失被迫反復(fù)回下層取數(shù)。表現(xiàn)在曲線上就是“本該在 L2 段的位置提前掉了一檔”你會誤判 L2 只有一半大。識別方法很簡單把起始地址人為錯(cuò)開。要么改步長3.2 節(jié)的 1.25 倍遞增要么在數(shù)組前面多申請幾十個(gè)字節(jié)做偏移。做完這兩件事其中一個(gè)如果那一檔突然回升就確認(rèn)是沖突而不是容量。我現(xiàn)在的固定做法是步長用 1.25 倍并加偏移兩條都上省得事后懷疑。注意不要用-O3加手動(dòng)改源碼的方式“優(yōu)化”這個(gè)問題動(dòng)了訪存循環(huán)測出來的就不是同一件事了。4.3 和 perf、blasbench 交叉驗(yàn)證確認(rèn)你測的是緩存單一工具的數(shù)字再漂亮也只是自證。我的驗(yàn)證辦法是用perf stat掛在同一次運(yùn)行上看 LLC 相關(guān)的計(jì)數(shù)有沒有隨尺寸同步變化。# 在帶寬明顯跌落的尺寸上掛一次 perf看末級緩存缺失是否同步跳起 perf stat -e cycles,instructions,cache-references,cache-misses,LLC-loads,LLC-load-misses \ taskset -c 2 ./cachebench -r -s 33554432 # 算力側(cè)對照blasbench 拉一遍單線程浮點(diǎn)確認(rèn)算力不是瓶頸 cd ../blasbench taskset -c 2 ./blasbench 21 | tail -20判讀邏輯如果尺寸剛從 L3 跨到內(nèi)存LLC-load-misses占LLC-loads的比例應(yīng)該同步升高同時(shí) cycles 里等待訪存的比例上升。反過來如果帶寬掉了但 LLC 缺失率沒動(dòng)說明你測的可能不是緩存層級而是被別的因素頻率降了、頁沒駐留、進(jìn)程被搶干擾了。blasbench 那一側(cè)的作用是排除法算力分?jǐn)?shù)正常而 cachebench 曲線異常問題就落在訪存路徑上方向立刻收窄。5. 避坑與排查五個(gè)讓緩存測試數(shù)據(jù)白測的坑5.1 讀帶寬高得離譜而且整條曲線是平的現(xiàn)象-r跑出來每一檔都在同一個(gè)極高的數(shù)值上從 1KB 到 256MB 幾乎不掉看著像“這臺機(jī)器沒有緩存層級”。 原因讀循環(huán)被編譯器優(yōu)化掉了或者結(jié)果被判定為無用而整段刪除測到的是空循環(huán)速度。老代碼里防優(yōu)化手段有限遇上新一代編譯器就頂不住。 解決先降到-O2重編如果依然平看工具是否提供“保留結(jié)果”的開關(guān)usage 里常有一個(gè)類似的選項(xiàng)并打開再不行就手動(dòng)在循環(huán)末尾把累加結(jié)果賦給一個(gè)全局變量重編后再跑。驗(yàn)證方式是整條曲線的絕對值應(yīng)該落在合理量級而不是高出十倍。5.2 同一份二進(jìn)制兩次運(yùn)行差出百分之二十現(xiàn)象什么都沒改連跑兩遍內(nèi)存段的帶寬一次 12GB/s 一次 9GB/s。 原因多次運(yùn)行之間變量太多——CPU 頻率還在爬升、頁表還沒熱、進(jìn)程被調(diào)度到別的核、鄰居進(jìn)程在搶 LLC。第一次運(yùn)行尤其吃虧大量缺頁發(fā)生在測試過程中。 解決先空跑一輪作為預(yù)熱并丟棄結(jié)果用taskset把進(jìn)程釘死在一個(gè)核上把-x重復(fù)次數(shù)提高取重復(fù)里的穩(wěn)定值跑之前確認(rèn)機(jī)器上沒有別的重負(fù)載。做完這四件事兩次運(yùn)行的差異一般能壓到 5% 以內(nèi)。5.3 拐點(diǎn)位置和 lscpu 報(bào)的 cache 容量對不上現(xiàn)象lscpu顯示 L2 是 1MB曲線卻在 256KB 就掉了一檔。 原因多半是 4.2 節(jié)的索引沖突少數(shù)情況是透明大頁把相鄰頁合并改變了實(shí)際的地址映射或者數(shù)組沒有按大頁對齊。 解決先把步長改成 1.25 倍、給數(shù)組加偏移重測仍然對不上就臨時(shí)把透明大頁設(shè)為madvise并確認(rèn)進(jìn)程沒有主動(dòng)使用大頁最后檢查是不是把“每核 L2”看成了“整顆 L2”多核機(jī)器的lscpu輸出容易看串行。5.4 大尺寸段的帶寬低到不像內(nèi)存像磁盤現(xiàn)象數(shù)組開到 512MB 以上帶寬掉到幾百 MB/s且抖動(dòng)極大。 原因申請的數(shù)組超過了可用物理內(nèi)存或者第一次觸碰觸發(fā)大量缺頁測試過程里混進(jìn)了換頁和頁清零。 解決把-s的上限壓到物理內(nèi)存的一半以內(nèi)free -g先看一眼可用量測試前確認(rèn)vmstat 1里si/so一直是 0如果機(jī)器上還跑著別的服務(wù)換一臺干凈的機(jī)器測這種測量對內(nèi)存壓力非常敏感。5.5 多進(jìn)程那套工具起不來或者只跑了一個(gè)進(jìn)程現(xiàn)象想順手驗(yàn)證多核擴(kuò)展性結(jié)果進(jìn)程數(shù)始終是 1或者直接報(bào)連接錯(cuò)誤。 原因多進(jìn)程部分需要一個(gè)啟動(dòng)器來拉起多個(gè)實(shí)例單獨(dú)執(zhí)行可執(zhí)行文件只會跑單進(jìn)程另外 MPI 環(huán)境沒配對時(shí)進(jìn)程間握手會直接失敗。 解決先看該子目錄里有沒有配套的啟動(dòng)腳本按腳本里的參數(shù)格式傳進(jìn)程數(shù)確認(rèn)mpirun之類的前置環(huán)境可用且版本一致實(shí)在跑不通就先放棄它c(diǎn)achebench 的單線程結(jié)論已經(jīng)足夠支撐大部分判斷不必為了“跑全”卡在這里。6. 進(jìn)階把一次性的緩存測試做成可回歸的基線單次測量只能回答“現(xiàn)在是多少”真正省事的是把它變成一條能對比的基線。我的做法是把第 3 章的采集腳本和第 3.3 節(jié)的解析腳本串起來一次跑完輸出一個(gè)帶環(huán)境指紋的 CSV文件名里帶日期和 CPU 型號內(nèi)容第一行寫uname -r、gcc版本、-s上限、是否改過步長。以后任何人問“這臺機(jī)器緩存是不是變慢了”拿兩份 CSV 對齊size_bytes做差值就行不用重新組織測試。對比的時(shí)候必須設(shè)閾值否則你會被噪聲牽著走。我的經(jīng)驗(yàn)閾值是同一尺寸兩次測量差異在 5% 以內(nèi)視為一致5% 到 15% 之間需要復(fù)測一次再判斷超過 15% 才認(rèn)為發(fā)生了變化。這個(gè)閾值不是拍腦袋是按 5.2 節(jié)把預(yù)熱、綁核、重復(fù)次數(shù)都做到位之后實(shí)測殘留的波動(dòng)范圍定的。# 一鍵基線采集 解析 打指紋產(chǎn)物可直接進(jìn)版本庫 STAMP$(date %Y%m%d)-$(lscpu | awk -F: /Model name/{gsub(/^ /,,$2); print $2} | tr -d ) bash sweep.sh python3 parse.py mv cache_curve.csv baseline-${STAMP}.csv sha256sum baseline-${STAMP}.csv baseline.sha256這套東西跑順之后還有個(gè)額外收益換編譯選項(xiàng)、換內(nèi)核、換固件這類變更只要跑一遍基線就能看出訪存路徑有沒有受影響比等業(yè)務(wù)側(cè)反饋“最近好像慢了”要早得多。綁核那一步別偷懶我吃過虧——有次忘了taskset曲線整體平移了 8%排查了半天以為是驅(qū)動(dòng)問題最后發(fā)現(xiàn)是進(jìn)程被搬到了另一個(gè) NUMA 節(jié)點(diǎn)的核上。從那以后我每次跑基線都強(qiáng)制走一遍taskset綁核加一輪預(yù)熱丟棄腳本里寫成默認(rèn)行為不給自己留手動(dòng)省略的機(jī)會。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取