前言
在分布式推理中,多設備(如GPU、CPU)之間的數據傳輸(通信)是連接計算的“橋梁”。如果通信效率低下,即使單設備計算能力再強,整體性能也會大打折扣。想象一下:如果工廠之間的物流卡車跑得比生產速度還慢,再多的工廠也無法提高整體產量。
本節將從最基礎的單設備內通信講起,逐步擴展到多設備、多節點,甚至不同類型硬件(如GPU和國產芯片)的協同通信,最后介紹邊緣設備與云端的通信優化。每個環節都會結合具體問題和解決方法,幫助你徹底理解“如何讓數據跑得更快”。
一、設備內通信:GPU與CPU的“對話”
1. 為什么GPU和CPU需要通信?
在推理流程中,CPU和GPU的分工不同:
- CPU:負責“前期準備”和“后期處理”,比如接收用戶輸入、文本分詞(把句子拆成token)、整理輸出結果等;
- GPU:負責“核心計算”,比如Transformer模型的矩陣乘法、注意力計算等。
因此,數據必須在兩者之間傳遞:
- CPU → GPU:把分詞后的token(轉換成數字張量)傳給GPU,讓GPU進行推理;
- GPU → CPU:把推理生成的結果(如文本token)傳回CPU,由CPU整理成自然語言返回給用戶。
這種“對話”的速度,直接影響整個推理的響應時間(比如用戶從輸入問題到看到回答的延遲)。
2. 通信的“高速公路”:PCIe總線
CPU和GPU之間通過PCIe總線連接,這是一條專門用于設備間數據傳輸的“高速公路”。目前主流的是PCIe 4.0,理論帶寬約32GB/s(雙向),新一代的PCIe 5.0可達64GB/s。
但這條“高速公路”有個特點:傳輸小數據時效率低。比如傳輸1KB的數據,實際耗時可能比傳輸1MB數據的1/1000還多。這是因為每次傳輸都需要附加“頭部信息”(類似快遞單),小數據的“快遞單”占比太高。
3. 優化方法1:用“專用車道”——頁鎖定內存
普通情況下,CPU的內存(RAM)可能被操作系統“臨時挪動”(比如內存不足時換出到硬盤),GPU無法直接訪問。此時數據傳輸需要CPU先把數據“搬到”一塊臨時的固定內存,再傳給GPU,相當于多了一次“中轉”,耗時增加。
頁鎖定內存(Pin Memory) 是解決辦法:它像“專用車道”,一旦分配就不會被操作系統挪動,GPU可以直接讀取,省去中轉步驟。
import torch
import time# 生成1個批次的輸入數據(形狀:[16, 512],16條文本,每條512個token)
data_size = (16, 512)# 普通內存(可能被系統挪動)
cpu_data_normal = torch.randint(0, 10000, data_size) # CPU上的普通張量
# 頁鎖定內存(固定位置,不被挪動)
cpu_data_pinned = torch.randint(0, 10000, data_size).pin_memory() # 標記為頁鎖定# 測試傳輸速度:普通內存 → GPU
start = time.time()
gpu_data = cpu_data_normal.cuda() # 傳輸到GPU
torch.cuda.synchronize() # 等待傳輸完成
print(f"普通內存傳輸耗時:{time.time() - start:.4f}秒") # 約0.0012秒# 測試傳輸速度:頁鎖定內存 → GPU
start = time.time()
gpu_data_pinned = cpu_data_pinned.cuda() # 傳輸到GPU
torch.cuda.synchronize()
print(f"頁鎖定內存傳輸耗時:{time.time() - start:.4f}秒") # 約0.0007秒(提速40%)
效果:頁鎖定內存傳輸速度通常比普通內存快30%~50%,尤其適合高頻傳輸場景(如高并發推理)。
4. 優化方法2:“邊生產邊運輸”——異步傳輸
默認情況下,數據傳輸(CPU→GPU)和GPU計算是“串行”的:必須等數據完全傳到GPU,才能開始計算。就像“快遞沒到,工廠不能開工”。
異步傳輸可以讓兩者“并行”:GPU在計算當前批次時,CPU提前把下一批次數據傳到GPU, overlapping(重疊)傳輸和計算時間。
# 異步傳輸:用CUDA流(Stream)實現并行
stream = torch.cuda.Stream() # 創建一個獨立的"任務流"