本講座:基準測試/分析 + 編寫內核
總結
編程模型(PyTorch、Triton、PTX)與硬件之間的差距 => 性能奧秘
理解擴展的基準測試
用于理解 PyTorch 函數內部結構的分析(用內核觸底)
看 PTX 匯編,了解 CUDA 內核的內部結構
編寫函數的 5 種方法:manual、PyTorch、編譯、CUDA、Triton
GeLU(按元素)、softmax(按行)、matmul(復聚合)
關鍵原則:組織計算以盡量減少讀/寫
關鍵思想:內核融合(倉庫/工廠類比)、平鋪(共享內存)
自動編譯器(Triton、torch.compile)會隨著時間的推移而變得更好
硬件
計算:流式多處理器 (SM) [A100:108]
內存:
DRAM [A100: 80GB] - 大而慢
二級緩存 [A100: 40MB]
L1 緩存 [A100:每 SM 192KB] - 小、快
執行模型
線程:進程單個索引(即 f(i))
線程塊:(又名并發線程數組):在單個 SM 上調度
網格 :線塊的集合
為什么使用線程塊?共享內存。
直觀理解:將讀取相似數據的 f(i)分組在一起
線程塊內的線程擁有共享內存(速度與 L1 緩存相當)[A100: 164KB]
可以在塊內同步線程(用于讀取/寫入),但不能跨塊同步
硬件與執行相互作用
線程塊以波次形式調度到 SM 上。
問題:最后一波次線程塊較少,導致部分 SM 空閑(低占用率)。
波形量化:使線程塊數量能被 SM 數量整除。
經驗法則:線程塊的數量應該 >= 4 倍的 SM 數量
挑戰:執行模型中隱藏了一些硬件方面的特性(例如,調度、SM 數量)。
算術強度:每字節的浮點運算次數
如果數值較高,表示操作是計算密集型(好)
如果數值較低,表示操作是內存密集型(壞)
一般規則:矩陣乘法是計算密集型,其他操作都是內存密集型
重要提示:請測試和分析您的代碼!
您可以閱讀規格表(營銷材料)和論文
但性能取決于您的庫版本、硬件和工作負載
因此,測試和分析您的代碼是必不可少的。
示例計算:在多層感知機(MLP)上運行前向/后向傳播。
run_mlp(dim=128, num_layers=16, batch_size=128, num_steps=5)
benchmarking() # 用時多久?profiling() # 時間花在哪里?
每次做修改時,請務必進行基準測試/性能分析!
矩陣乘法基準測試
首先,我們來基準測試方形矩陣的矩陣乘法。
在1024和2048上,時間幾乎沒有增加
因為在執行時這些矩陣乘法時存在恒定因子的開銷,如從CPU傳送到GPU、啟動內核
讓我們測試一下我們的多層感知機!
每次MLP執行大約需要五秒鐘
性能分析
雖然基準測試關注的是端到端時間,但性能分析則關注時間的具體花費位置。
很明顯:性能分析可以幫助你了解時間具體花費在哪些地方。
更深入:性能分析幫助你了解(被調用的內容)。
PyTorch 有一個內置的性能分析器 https://pytorch.org/tutorials/recipes/recipes/profiler_recipe.html
讓我們對一些代碼進行性能分析,看看幕后發生了什么。
CUDA
CUDA 是 C/C++ 的擴展,包含管理 GPU 的 API。
我們將編寫一些函數f,當調用這個CUDA內核時,它將自動對向量或矩陣的所有元素調用f,然后我們將并行計算我們想要的一切
網格:
線程塊的集合:numBlocks = (2, 4),blockDim = (1, 8)
線程塊:
線程的集合:blockIdx = (0, 1)
線程:
單個操作單元:threadIdx = (0, 3)。
你編寫線程執行的代碼,并使用 (blockIdx, blockDim, threadIdx) 來確定要執行的操作。
設置 CUDA_LAUNCH_BLOCKING,以便如果有錯誤,CUDA 會告訴你哪里出了問題。
os.environ["CUDA_LAUNCH_BLOCKING"] = "1"
load_inline
函數使得編寫 CUDA 代碼并將其綁定到 Python 模塊變得方便,以便立即使用。
CUDA 代碼:包含完整的邏輯
實際內核所在,這將被發送到GPU,并進行計算,然后返回結果
這是一個包裝器,將協調內核的啟動,內核實際上存在于GPU中
TORCH_CHECK(x.device().is_cuda());//確保x存在于GPU設備中
TORCH_CHECK(x.is_contiguous());//確保x是連續的,位于連續的內存塊中
// Allocate empty tensor
torch::Tensor y = torch::empty_like(x);
總結
-
編程模型(如PyTorch、Triton、PTX)與硬件的“差距”為何會導致性能差異?
不同編程模型對硬件細節的抽象程度不同:PyTorch封裝了底層邏輯,易用但可能未充分利用硬件特性;Triton提供中間抽象,平衡靈活性與性能;PTX接近硬件指令,可控性最高但復雜度大。這種抽象差距導致對內存訪問、線程調度等硬件細節的優化程度不同,最終體現為性能差異。 -
GPU執行模型中,線程、線程塊、網格的關系及對硬件利用率的影響是什么?
線程是最小執行單元,負責單個索引操作;線程塊是線程的集合,在單個SM上調度,共享塊內共享內存;網格是線程塊的集合。線程塊數量需與SM數量匹配(建議≥4倍SM數),否則易因“最后一波次線程塊不足”導致SM空閑,降低硬件利用率。 -
算術強度的定義及對GPU性能的指導意義是什么?
算術強度指每字節數據傳輸對應的浮點運算次數。數值高表示計算密集型(如矩陣乘法),能充分利用GPU算力;數值低表示內存密集型(如多數逐元素操作),易受內存帶寬限制。優化需針對類型調整策略:計算密集型提升并行效率,內存密集型減少數據傳輸。 -
基準測試與性能分析的核心區別及各自作用?
基準測試關注端到端時間,用于評估整體性能(如矩陣乘法不同尺寸的耗時);性能分析聚焦時間分布,定位具體瓶頸(如哪部分內核、內存操作耗時最長)。二者結合:基準測試衡量優化效果,性能分析指導優化方向。 -
CUDA內核編寫中,線程塊設計需考慮哪些硬件特性?共享內存的關鍵作用是什么?
線程塊設計需匹配SM資源:受限于共享內存大小(如A100每塊164KB)、線程數上限,并需保證內存訪問合并(匹配DRAM突發傳輸)。共享內存位于SM內部,速度接近L1緩存,核心作用是緩存塊內重復訪問的數據,減少全局內存讀寫,提升數據重用效率。