算能力與CUDA架構(gòu):編譯框架防翻車(chē)指南)
編譯過(guò)深度學(xué)習(xí)框架的人十有八九都被“計(jì)算能力Compute Capability”這個(gè)東西卡過(guò)。要不就是編譯時(shí)刷出一排Unsupported gpu architecture要不就是同一個(gè)安裝包在這塊卡上跑得好好的換到另一塊卡上直接報(bào)no kernel image is available for execution on the device。我第一次在 caffe 上踩這個(gè)坑時(shí)硬生生折騰到凌晨?jī)牲c(diǎn)最后才發(fā)現(xiàn)問(wèn)題就出在一個(gè)小數(shù)字上——GPU 計(jì)算能力。這篇就把這個(gè)數(shù)字的前因后果講透順手給出 caffe / pytorch / tensorflow 里設(shè)置計(jì)算能力的具體寫(xiě)法和翻車(chē)應(yīng)急預(yù)案。適合所有需要從源碼構(gòu)建框架的開(kāi)發(fā)者、算法工程師以及被 CUDA 環(huán)境逼到懷疑人生的人。1. 先搞懂“計(jì)算能力”這個(gè)數(shù)字它就是 GPU 的架構(gòu)版本號(hào)1.1 數(shù)字拆解主版本號(hào)是“代”次版本號(hào)是“檔”計(jì)算能力Compute Capability寫(xiě)出來(lái)長(zhǎng)這樣8.6、7.5、9.0。它本質(zhì)上就是 GPU 硬件架構(gòu)的版本號(hào)前一位代表大代際后一位代表同代際里的細(xì)分檔位。比如8.6意味著這是 Ampere 大架構(gòu)下的消費(fèi)卡檔位RTX 3090 就是 8.6。而 A100 同樣是 Ampere 架構(gòu)卻是8.0。同一個(gè)架構(gòu)代際內(nèi)不同芯片的指令集和硬件特性有差異所以才有8.0、8.6、8.9這種細(xì)分編號(hào)。在編譯參數(shù)里這個(gè)數(shù)字還會(huì)變成兩種寫(xiě)法sm_86SASS 目標(biāo)架構(gòu)生成的就是這塊卡直接執(zhí)行的機(jī)器碼。compute_86PTX 目標(biāo)架構(gòu)生成的是中間表示后續(xù)可以由驅(qū)動(dòng)現(xiàn)場(chǎng)編譯到具體 GPU。我見(jiàn)過(guò)不少新手把sm_75和compute_75當(dāng)成一回事其實(shí)它們對(duì)應(yīng)的是兩種不同形態(tài)的產(chǎn)物這也是后面絕大多數(shù)編譯坑的根源。1.2 一張表看懂各大架構(gòu)的計(jì)算能力區(qū)間我把這些年主流的 GPU 架構(gòu)按計(jì)算能力梳理了一下方便對(duì)號(hào)入座架構(gòu)代際計(jì)算能力典型 GPU 型號(hào)大概年代Fermi2.0 / 2.1GTX 4xx / 5xx2010 左右Kepler3.0 / 3.5 / 3.7GTX 680 / Tesla K402012 左右Maxwell5.0 / 5.2 / 5.3GTX 750 / GTX 9802014 左右Pascal6.0 / 6.1Tesla P100 / GTX 1080 Ti2016 左右Volta7.0Tesla V1002017 左右Turing7.5RTX 2080 Ti / RTX 20602018 左右Ampere8.0 / 8.6A100 / RTX 30902020 左右Ada Lovelace8.9RTX 40902022 左右Hopper9.0H1002023 左右2024 年之后的新架構(gòu)計(jì)算能力已經(jīng)突破 10.x后續(xù)還會(huì)有新編號(hào)具體以廠商官方規(guī)格頁(yè)為準(zhǔn)。記住這張表的核心規(guī)律不同計(jì)算能力的 GPU它們的 SASS 機(jī)器碼是不通用的但 PTX 有更強(qiáng)的跨代兼容能力。1.3 計(jì)算能力決定了哪些實(shí)實(shí)在在的事情這個(gè)數(shù)字不只是一個(gè)“型號(hào)標(biāo)簽”它背后綁定了一整套硬件特性能力。具體來(lái)說(shuō)指令集版本不同sm_86 能用的某些匯編指令sm_75 是不認(rèn)的。編譯器要針對(duì)具體計(jì)算能力生成對(duì)應(yīng)指令。Tensor Core 等特性不同8.0 以上的 Ampere 架構(gòu)對(duì) Tensor Core 的支持方式、WMMAwarp matrix multiply-accumulate指令版本都不一樣新框架的某些算子會(huì)針對(duì)高版本計(jì)算能力做專(zhuān)門(mén)優(yōu)化。CUDA Toolkit 版本兼容范圍不同老版本的 CUDA Toolkit 不一定認(rèn)識(shí)新計(jì)算能力。比如 CUDA 10.x 去編譯sm_86的代碼基本直接報(bào)錯(cuò)因?yàn)槟莻€(gè)年代還沒(méi)有 Ampere 的編譯器支持。運(yùn)行時(shí)行為不同程序跑起來(lái)后驅(qū)動(dòng)需要根據(jù) GPU 的計(jì)算能力加載對(duì)應(yīng)內(nèi)核匹配不上就崩。所以在深度學(xué)習(xí)框架的編譯場(chǎng)景里計(jì)算能力直接決定了“你編出來(lái)的這個(gè)庫(kù)到底能跑在哪些 GPU 上”。2. 編譯框架時(shí)為什么要跟這個(gè)數(shù)字較勁2.1 GPU 代碼的“提前編譯”與“運(yùn)行時(shí)兜底”很多框架PyTorch、TensorFlow、Caffe在安裝時(shí)都會(huì)把 CUDA 算子源碼提前編譯成二進(jìn)制而不是每次運(yùn)行都現(xiàn)場(chǎng)編譯。這個(gè)模式叫 AOTAhead Of Time提前編譯它和純 JITJust In Time運(yùn)行時(shí)編譯不同。AOT 的問(wèn)題是你得在編譯時(shí)告訴編譯器“請(qǐng)按哪一代 GPU 的架構(gòu)來(lái)生成代碼”。如果你寫(xiě)死了sm_86那生成的就是 RTX 30 系列認(rèn)識(shí)的機(jī)器碼一旦把編譯產(chǎn)物拿到計(jì)算能力 7.5 的 RTX 2080 上跑驅(qū)動(dòng)根本沒(méi)有辦法執(zhí)行這份 SASS 代碼因?yàn)樗鼔焊徽J(rèn)識(shí)。現(xiàn)代 CUDA 編譯有個(gè)兜底機(jī)制可以在生成 SASS 機(jī)器碼的同時(shí)也生成一份 PTX 中間代碼。PTX 像“跨架構(gòu)的應(yīng)急預(yù)案”當(dāng)驅(qū)動(dòng)發(fā)現(xiàn)當(dāng)前 GPU 沒(méi)有匹配的 SASS 時(shí)會(huì)嘗試把 PTX 現(xiàn)場(chǎng) JIT 成當(dāng)前計(jì)算能力能跑的代碼。但前提是你在編譯時(shí)確實(shí)附上了 PTX很多人恰恰漏了這一手。2.2 參數(shù)設(shè)高、設(shè)低、干脆不設(shè)分別是啥后果我把三種常見(jiàn)情況可能引發(fā)的后果列一下設(shè)得太高例如只寫(xiě)8.9在老 GPU 上直接no kernel image。編譯產(chǎn)物像是專(zhuān)門(mén)給新卡寫(xiě)的一封信老卡連拆信的機(jī)會(huì)都沒(méi)有。設(shè)得太低例如只寫(xiě)5.0幾乎所有 GPU 都能兼容但新架構(gòu)的新指令全用不上。Tensor Core 加速等于擺設(shè)推理和訓(xùn)練性能會(huì)明顯偏低。干脆不設(shè)置這里的風(fēng)險(xiǎn)最大。不同構(gòu)建腳本的默認(rèn)值五花八門(mén)有的默認(rèn)只編當(dāng)前機(jī)器上探測(cè)到的架構(gòu)有的默認(rèn)編一個(gè)非常保守的老架構(gòu)。你在這臺(tái)機(jī)器上編譯時(shí)可能一切正常換到另一張卡上就跑不了。我見(jiàn)過(guò)一個(gè)最經(jīng)典的場(chǎng)景某人在一臺(tái) RTX 4090 機(jī)器上編譯完 PyTorch看起來(lái)很順利部署到一臺(tái) T4 推理機(jī)上直接崩。后來(lái)我把編譯日志翻出來(lái)發(fā)現(xiàn)TORCH_CUDA_ARCH_LIST是空的構(gòu)建腳本默認(rèn)抓到了 4090 的 8.9老卡當(dāng)然顆粒無(wú)收。2.3 誰(shuí)在讀取這個(gè)設(shè)置CMake、nvcc 與框架腳本的協(xié)作從底層邏輯看設(shè)置計(jì)算能力最終都會(huì)轉(zhuǎn)化成 nvcc 的-gencode參數(shù)。比如-gencode archcompute_86,codesm_86 -gencode archcompute_86,codecompute_86第一行生成針對(duì) 8.6 的 SASS第二行生成 8.6 版本的 PTX。不同框架只是把“如何拼這串參數(shù)”包裝成了變量PyTorch 用環(huán)境變量TORCH_CUDA_ARCH_LISTTensorFlow 用TF_CUDA_ARCHSCaffe 用 CMake 的CUDA_ARCH_BIN和CUDA_ARCH_PTX現(xiàn)代 CMake 工程用CMAKE_CUDA_ARCHITECTURES所以你理解了底層原理后再去看這些變量名就會(huì)覺(jué)得它們只是同一個(gè)東西的“外號(hào)”。3. 查自己 GPU 計(jì)算能力的三條路線3.1 命令行直接查最省事的方法是用 nvidia-smi 的查詢(xún)模式。普通nvidia-smi只能看到型號(hào)、顯存、驅(qū)動(dòng)信息看不到計(jì)算能力但只要加兩個(gè)字段就能直接輸出nvidia-smi --query-gpuname,compute_cap --formatcsv輸出大概是這樣NVIDIA GeForce RTX 3090, 8.6這個(gè)辦法適合快速確認(rèn)手頭機(jī)器到底是什么架構(gòu)。3.2 官方規(guī)格表對(duì)號(hào)入座如果手頭沒(méi)有機(jī)器或者想查一批 GPU 的規(guī)格直接去 GPU 廠商的 CUDA 支持矩陣頁(yè)面。這個(gè)頁(yè)面會(huì)按計(jì)算能力從小到大列出所有支持 CUDA 的 GPU 型號(hào)。比如你想知道 Tesla T4 是多少搜一下就能看到是 7.5A100 是 8.0H100 是 9.0。查表的另一種方式是直接用 CUDA Toolkit 自帶的 deviceQuery 示例/usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery運(yùn)行后最后幾行會(huì)輸出CUDA Capability Major/Minor version number: 8.6。3.3 在代碼里動(dòng)態(tài)獲取如果你要在程序里動(dòng)態(tài)判斷 GPU 能力可以直接問(wèn) CUDA Runtime。C 語(yǔ)言寫(xiě)法是用cudaGetDevicePropertiesPython 里如果用 PyTorch 則更簡(jiǎn)潔import torch # 返回一個(gè)元組比如 (8, 6) print(torch.cuda.get_device_capability(0))這個(gè)我在寫(xiě)需要自適應(yīng)選 kernel 的代碼時(shí)經(jīng)常用先查計(jì)算能力再?zèng)Q定走哪個(gè)優(yōu)化分支避免在運(yùn)行時(shí)調(diào)用不支持的算子。3.4 常用 GPU 速查參考表個(gè)人常備版我把自己平時(shí)頻繁用到的型號(hào)整理成了參考表編譯時(shí)填參數(shù)基本都用得上GPU 型號(hào)計(jì)算能力備注GTX 1080 Ti6.1老但還能跑Tesla T47.5推理常見(jiàn)RTX 2080 Ti7.5Turing 代表V1007.0Volta適合老 CUDA 環(huán)境A1008.0Ampere 數(shù)據(jù)中心卡RTX 30908.6Ampere 消費(fèi)卡RTX 40908.9AdaH1009.0Hopper只要把這張表記在心里編譯時(shí)選架構(gòu)就有了基本盤(pán)。4. PyTorch 源碼編譯TORCH_CUDA_ARCH_LIST 怎么填4.1 環(huán)境變量的填法與格式PyTorch 從源碼編譯時(shí)讀取的是環(huán)境變量TORCH_CUDA_ARCH_LIST。格式是空格分隔的列表export TORCH_CUDA_ARCH_LIST7.5 8.0 8.6編譯時(shí)它會(huì)逐個(gè)生成對(duì)應(yīng)的 SASS 代碼。比如包含 7.5 和 8.6就等于給 nvcc 傳了兩組-gencode參數(shù)最終產(chǎn)出的動(dòng)態(tài)庫(kù)里嵌入了兩套機(jī)器碼。這里有個(gè)容易忽略的點(diǎn)如果你是用官方編譯好的 PyTorch wheel 包是不需要管這個(gè)變量的官方包默認(rèn)包含了常見(jiàn)計(jì)算能力的支持自己搞反而容易出問(wèn)題。只有當(dāng)你需要從源碼定制編譯或者要編譯自定義 CUDA 擴(kuò)展算子時(shí)才需要真正關(guān)心它。4.2 多架構(gòu)與“PTX”的組合策略在 PyTorch 的環(huán)境變量里單個(gè)架構(gòu)后面可以加PTX例如export TORCH_CUDA_ARCH_LIST7.5 8.0 8.6PTX意思是為 8.6 生成 SASS 機(jī)器碼同時(shí)額外生成一份 compute_86 的 PTX 中間代碼。這個(gè) PTX 是給“未來(lái)可能出現(xiàn)的更高計(jì)算能力 GPU”準(zhǔn)備的。比如某臺(tái)機(jī)器是計(jì)算能力 8.9 的新卡驅(qū)動(dòng)看到 8.6 的 SASS 沒(méi)法用但發(fā)現(xiàn)有 compute_86 的 PTX就會(huì)現(xiàn)場(chǎng)把它 JIT 成 8.9 能跑的代碼。雖然性能不一定是最優(yōu)但至少能跑起來(lái)。我建議的策略很直接把你手頭最老的目標(biāo)卡、當(dāng)前主力卡都寫(xiě)上最后一個(gè)檔位加PTX兜底。例如開(kāi)發(fā)機(jī)器是 RTX 3090但部署機(jī)器還有 RTX 2080 Tiexport TORCH_CUDA_ARCH_LIST7.5 8.6PTX這樣既照顧了老卡又保留了新卡的優(yōu)化空間。4.3 一次標(biāo)準(zhǔn)編譯實(shí)操示例一個(gè)比較典型的源碼編譯流程是這樣conda create -n build_env python3.10 conda activate build_env conda install cmake ninja export TORCH_CUDA_ARCH_LIST7.5 8.0 8.6PTX git clone --recursive https://github.com/pytorch/pytorch cd pytorch python setup.py develop編譯結(jié)束后可以用 CUDA 自帶工具檢查動(dòng)態(tài)庫(kù)里到底嵌入了哪些架構(gòu)cuobjdump --list-elf torch/lib/libtorch_cuda.so | grep sm_正常情況下會(huì)看到類(lèi)似sm_75、sm_80、sm_86的字樣。這一步能幫你確認(rèn)設(shè)置是否真的生效非常值得養(yǎng)成習(xí)慣。5. TensorFlow 源碼編譯TF_CUDA_ARCHS 與 bazel 參數(shù)5.1 官方 configure 階段和 bazel action_envTensorFlow 從源碼編譯用的是 bazel在 configure 階段會(huì)生成一份配置。其中控制計(jì)算能力的變量是TF_CUDA_ARCHS。老版本 TensorFlow 的 configure 腳本會(huì)讓你手動(dòng)輸入架構(gòu)例如Please specify a list of comma-separated CUDA compute capabilities you want to build with.你也可以在編譯時(shí)直接通過(guò)環(huán)境變量覆蓋TF_CUDA_ARCHS7.5 8.0 8.6 bazel build --configcuda //tensorflow/tools/pip_package:build_pip_package注意這里同樣是空格分隔。最終 bazel 會(huì)把架構(gòu)列表解析成 nvcc 的 gencode 參數(shù)。5.2 老參數(shù) -arch / -gencode 其實(shí)還在用有些老教程里寫(xiě)的不是TF_CUDA_ARCHS而是往 bazel 編譯選項(xiàng)中直接塞 nvcc 參數(shù)--copt-archsm_75或者更完整的--copt-gencodearchcompute_75,codesm_75 --copt-gencodearchcompute_75,codecompute_75這種寫(xiě)法在特殊環(huán)境下依然有效因?yàn)樗苯幼饔糜?nvcc。不過(guò)維護(hù)成本高切到新架構(gòu)要改一堆地方還是用TF_CUDA_ARCHS更省心。5.3 TF 場(chǎng)景下容易忽略的兩件事第一件TensorFlow 官方預(yù)編譯包的架構(gòu)范圍是固定的。如果你是自己從源碼構(gòu)建定制版別把架構(gòu)范圍設(shè)得太窄否則模型在另一種 GPU 上加載時(shí)會(huì)直接報(bào)找不到內(nèi)核。第二件TensorFlow 對(duì) PTX 的處理在各版本間有變化。新版框架總體上是按架構(gòu)列表生成 SASS但對(duì)“未來(lái)架構(gòu)兜底”的支持不像 PyTorch 那樣直白。如果拿不準(zhǔn)穩(wěn)妥做法是把目標(biāo)機(jī)器范圍內(nèi)所有可能用到的計(jì)算能力都列進(jìn)去哪怕稍微增大一點(diǎn)編譯產(chǎn)物體積。我遇到過(guò)有人圖省事只寫(xiě)了8.0結(jié)果 A100 上跑得好好的換到 T4 上模型加載直接掛。這就是典型的“只考慮當(dāng)下機(jī)器、沒(méi)考慮部署環(huán)境”的翻車(chē)現(xiàn)場(chǎng)。6. Caffe 與通用 CMake 工程CMAKE_CUDA_ARCHITECTURES6.1 Caffe 的老式寫(xiě)法CUDA_ARCH_BIN 和 CUDA_ARCH_PTXCaffe 的構(gòu)建腳本有一套歷史悠久的變量體系CUDA_ARCH_BIN負(fù)責(zé)寫(xiě) SASS 要覆蓋哪些計(jì)算能力CUDA_ARCH_PTX負(fù)責(zé)寫(xiě) PTX 兜底檔位。編譯時(shí)這樣設(shè)cmake .. \ -DCUDA_ARCH_BIN75 86 \ -DCUDA_ARCH_PTX75這條命令的意思是生成 7.5 和 8.6 兩份 SASS 代碼同時(shí)為 7.5 準(zhǔn)備一份 PTX。如果你的目標(biāo)是只對(duì)當(dāng)前 GPU 優(yōu)化不想管其他型號(hào)也可以把CUDA_ARCH_BIN設(shè)成你機(jī)器對(duì)應(yīng)的計(jì)算能力數(shù)字。老項(xiàng)目的坑在于不同版本的 Caffe 對(duì)這個(gè)變量的解析格式不完全一致有的版本要求寫(xiě)成7.5有的版本要求寫(xiě)成75。遇到編譯不過(guò)時(shí)先檢查是不是數(shù)字格式的問(wèn)題。6.2 現(xiàn)代 CMake 寫(xiě)法CMAKE_CUDA_ARCHITECTURES如果你用的是較新的 CMake 和自定義 CUDA 工程推薦直接使用標(biāo)準(zhǔn)變量CMAKE_CUDA_ARCHITECTURES。這個(gè)變量從 CMake 3.18 開(kāi)始支持分號(hào)分隔多個(gè)架構(gòu)cmake .. -DCMAKE_CUDA_ARCHITECTURES75;80;86注意這里的分號(hào)和前面環(huán)境變量的空格分隔不一樣。忘了帶引號(hào)的話(huà)shell 可能把分號(hào)理解成命令分隔符會(huì)直接裂開(kāi)。CMake 3.24 以上版本還支持幾個(gè)關(guān)鍵字native直接用當(dāng)前機(jī)器探測(cè)到的計(jì)算能力適合只在本機(jī)部署。all支持所有已知架構(gòu)靈活但編譯時(shí)間極其感人。all-major覆蓋每個(gè)大代際里的主力架構(gòu)比 all 輕量不少。6.3 三個(gè)實(shí)用檔位native、多架構(gòu)組合、全家族覆蓋根據(jù)實(shí)際用途我一般推薦三檔只在本機(jī)跑-DCMAKE_CUDA_ARCHITECTURESnative最省事編出來(lái)也只認(rèn)當(dāng)前卡。要換機(jī)器、架構(gòu)范圍明確-DCMAKE_CUDA_ARCHITECTURES75;80;86速度適中兼容性兼顧。要交付給多人/多卡環(huán)境、架構(gòu)不確定保守起見(jiàn)寫(xiě)61;75;80;86;89;90。代價(jià)是編譯時(shí)間和動(dòng)態(tài)庫(kù)體積都會(huì)上升換來(lái)的是“裝上就能跑”的踏實(shí)感。我記得 CMake 官方對(duì)這個(gè)變量的默認(rèn)值還鬧過(guò)一陣子如果不顯式設(shè)置新版本 CMake 在啟用 CUDA 語(yǔ)言時(shí)會(huì)直接警告甚至報(bào)錯(cuò)要求你必須顯式指定架構(gòu)。這不是 CMake 矯情而是它發(fā)現(xiàn)太多人因?yàn)槟J(rèn)值不明確而在換機(jī)器后翻車(chē)了。7. 編譯和運(yùn)行階段的高頻翻車(chē)現(xiàn)場(chǎng)7.1 編譯期Unsupported gpu architecture這個(gè)報(bào)錯(cuò)通常長(zhǎng)這樣Unsupported gpu architecture compute_86出現(xiàn)原因一般有兩個(gè)第一CUDA Toolkit 版本太老編譯器根本不知道sm_86/compute_86這個(gè)架構(gòu)。比如老版本 CUDA 10.x 就認(rèn)不出 8.6。解決辦法是升級(jí) CUDA Toolkit或者把目標(biāo)架構(gòu)改成當(dāng)前工具鏈認(rèn)識(shí)的舊架構(gòu)。第二CMake 的架構(gòu)列表里有拼寫(xiě)錯(cuò)誤。比如手滑把86寫(xiě)成了6CMake 會(huì)一臉懵地表示不認(rèn)識(shí)。查一遍變量值里有沒(méi)有漏數(shù)字、多標(biāo)點(diǎn)就好。7.2 編譯期ptxas fatal error我遇得比較多的是這種ptxas fatal : Unsupported version or architecture這個(gè)信息比上一條更底層。ptxas 是 CUDA 工具鏈里負(fù)責(zé)把 PTX 編譯成 SASS 的程序它對(duì)架構(gòu)的接受范圍更敏感。最常見(jiàn)的場(chǎng)景是你設(shè)置了很高的計(jì)算能力編號(hào)例如寫(xiě)了compute_90但實(shí)際 CUDA Toolkit 版本還不夠新ptxas 不認(rèn) 9.0于是當(dāng)場(chǎng)報(bào)錯(cuò)。解決辦法也很直接查看工具鏈支持的架構(gòu)上限把目標(biāo)架構(gòu)調(diào)低或者升級(jí) CUDA Toolkit兩者必有一項(xiàng)。7.3 運(yùn)行期no kernel image is available for execution on the device這個(gè)錯(cuò)誤幾乎全是在換機(jī)器后才出現(xiàn)RuntimeError: CUDA error: no kernel image is available for execution on the device原因是當(dāng)前 GPU 的計(jì)算能力在程序編譯的 SASS 列表里找不到對(duì)應(yīng)項(xiàng)而且程序里也沒(méi)有附帶能 JIT 到當(dāng)前架構(gòu)的 PTX。比如用 RTX 4090 編譯出的庫(kù)只帶了 8.9 的 SASS放到 RTX 30908.6上跑8.9 的機(jī)器碼它不認(rèn)又沒(méi)有 8.6 的 PTX 兜底自然直接崩。碰到這個(gè)報(bào)錯(cuò)不要急著懷疑代碼邏輯先回去看編譯時(shí)架構(gòu)列表范圍把目標(biāo)部署機(jī)器的計(jì)算能力加進(jìn)去重編一次。7.4 高頻問(wèn)題速查表我把常見(jiàn)情況整理成表方便直接對(duì)照現(xiàn)象常見(jiàn)原因處理思路編譯報(bào) Unsupported gpu architectureCUDA 版本太老 / 拼寫(xiě)錯(cuò)誤升級(jí)工具鏈或修正架構(gòu)編號(hào)ptxas fatal: Unsupported version目標(biāo)架構(gòu)超出工具鏈支持范圍降檔或升級(jí) CUDA Toolkit運(yùn)行報(bào) no kernel imageSASS 和 PTX 都與當(dāng)前卡不匹配把目標(biāo)架構(gòu)加入編譯范圍重編CMake 報(bào) CUDA_ARCHITECTURES 相關(guān)錯(cuò)誤變量未設(shè)置或格式不對(duì)顯式指定架構(gòu)列表用引號(hào)包裹換機(jī)器后部分算子極慢只編了最低兼容架構(gòu)新卡特性沒(méi)啟用增加新計(jì)算能力檔位重新編譯排查這類(lèi)問(wèn)題時(shí)我還有個(gè)習(xí)慣先跑一行nvidia-smi --query-gpuname,compute_cap --formatcsv把當(dāng)前機(jī)器的計(jì)算能力確認(rèn)掉再去看編譯日志里實(shí)際生成了哪些架構(gòu)兩步就能定位九成問(wèn)題。8. 折騰這么多次我最后想說(shuō)的幾件事第一件編譯框架前一定要先想清楚“這個(gè)庫(kù)到底要在哪些機(jī)器上跑”。只服務(wù)于本機(jī)直接native或者寫(xiě)當(dāng)前計(jì)算能力就行要交付部署就必須把目標(biāo)機(jī)器范圍拉出來(lái)逐個(gè)核對(duì)架構(gòu)列表。第二件多架構(gòu)列表確實(shí)會(huì)讓編譯變慢、包體變大但這是值得的。一個(gè)包含75;80;86;89;90的庫(kù)體積可能多個(gè)幾百 MB換來(lái)的是在所有主流量級(jí) GPU 上“裝好即跑”遠(yuǎn)比在生產(chǎn)環(huán)境里重編框架省時(shí)間。第三件學(xué)會(huì)用cuobjdump --list-elf檢查編譯產(chǎn)物。不管是 PyTorch 的.so還是 Caffe 的動(dòng)態(tài)庫(kù)編譯完以后花十秒看一下里面嵌了哪些 sm 架構(gòu)就能確認(rèn)設(shè)置到底有沒(méi)有生效。這一步看著不起眼卻幫我避過(guò)無(wú)數(shù)個(gè)“以為配好了其實(shí)是默認(rèn)值在撐場(chǎng)面”的坑。最后再分享一個(gè)小經(jīng)驗(yàn)團(tuán)隊(duì)多人共用構(gòu)建機(jī)時(shí)架構(gòu)列表最好統(tǒng)一寫(xiě)進(jìn)構(gòu)建腳本或 Dockerfile 里別依賴(lài)某個(gè)人手動(dòng) export。否則一個(gè)人改環(huán)境變量、另一個(gè)人不知道編譯出的包帶回各自的機(jī)器上互相甩鍋的場(chǎng)景你就等著吧。