在 DGX Spark 上定位 CUDA 越界存取並驗證修復

在 DGX Spark 上定位 CUDA 越界存取並驗證修復

CUDA 核心已經啟動,錯誤卻到結果傳回時才出現;同一段程式縮小輸入後又能執行。遇到這類現象,應先檢查裝置記憶體存取,而不是立刻調整驅動程式。在 DGX Spark 上,可以用獨立的小程式建立完整的診斷流程:刻意觸發越界、定位原始碼行、修正索引,再確認錯誤偵測並未被停用。整個過程不需要模型權重,也不涉及正式環境服務。

一、固定環境並界定測試範圍

TcpNode 的 DGX Spark 採用 NVIDIA GB10 Grace Blackwell、ARM64 架構與 NVIDIA DGX OS,配備 128GB LPDDR5x 統一記憶體。統一記憶體由 CPU 與 GPU 共用,但這不表示任何指標都能跨越配置範圍。以下使用 cudaMalloc 配置裝置記憶體,明確界定合法的位址範圍。

付款後,在控制台查看連線憑證,透過 SSH 登入。測試應放在獨立的工作目錄中,以一般使用者身分執行,不要在正在處理請求的推論程序中注入錯誤。

mkdir -p "$HOME/cuda-access-check"
cd "$HOME/cuda-access-check"
uname -m
nvidia-smi
command -v nvcc
command -v compute-sanitizer
nvcc --version
compute-sanitizer --version

架構應顯示為 aarch64。記錄工具版本與驅動程式資訊,後續複驗時使用相同環境。已預先安裝 CUDA 軟體堆疊,不代表每個工具都已加入目前的 PATH;若找不到命令,應先核對已安裝的工具鏈與安裝路徑,不要直接更換整套驅動程式。

越界範例只能在可隨時終止並捨棄的獨立程序中執行。發生非法記憶體存取後,應結束該程序並重新執行,不能將同一 CUDA 上下文中後續產生的結果視為有效證據。

二、建立只改變一個變因的越界範例

將以下內容儲存為 repro.cu。程式只配置一個整數的空間,合法索引為零;編譯時透過 TEST_INDEX 切換寫入位置。這樣錯誤版與修復版就能共用原始碼,避免同時變更輸入、配置大小與執行路徑。

#include <cuda_runtime.h>
#include <cstdio>
#include <cstdlib>

static void checked(cudaError_t status) {
    if (status != cudaSuccess) {
        std::fprintf(stderr, "%s\n", cudaGetErrorString(status));
        std::exit(EXIT_FAILURE);
    }
}

__global__ void write_value(int* data) {
    data[TEST_INDEX] = 7;
}

int main() {
    int* data = nullptr;
    int result = 0;
    checked(cudaMalloc(reinterpret_cast<void**>(&data), sizeof(int)));
    checked(cudaMemset(data, 0, sizeof(int)));
    write_value<<<1, 1>>>(data);
    checked(cudaGetLastError());
    checked(cudaDeviceSynchronize());
    checked(cudaMemcpy(&result, data, sizeof(int), cudaMemcpyDeviceToHost));
    checked(cudaFree(data));
    std::printf("result=%d\n", result);
    return result == 7 ? EXIT_SUCCESS : EXIT_FAILURE;
}

分別編譯兩個程式:

nvcc -arch=native -lineinfo -DTEST_INDEX=1 repro.cu -o repro-bad
nvcc -arch=native -lineinfo -DTEST_INDEX=0 repro.cu -o repro-good
sha256sum repro.cu repro-bad repro-good > build.sha256

-arch=native 會針對目前可見的 GPU 編譯,適合在本機驗證,不適合用來產生跨裝置通用的執行檔。若編譯器不支援目前的裝置,應先核對工具鏈相容性,不要任意套用其他架構參數。-lineinfo 會保留裝置端原始碼的行號資訊;初次定位時,不必啟用會明顯改變執行特性的完整裝置端偵錯模式。

為什麼要同時檢查啟動與同步

cudaGetLastError 用來檢查啟動相關錯誤,但核心通常以非同步方式執行。cudaDeviceSynchronize 會等待先前的裝置端工作完成,執行期間的錯誤才有機會在這裡傳回。兩者都必須分別檢查,不能只看程式是否印出結果。

三、讓偵測結果反映在可判讀的結束代碼中

先執行錯誤版,並分別儲存標準輸出與偵測日誌:

if compute-sanitizer --tool memcheck --error-exitcode 86 \
    ./repro-bad > bad.stdout 2> bad.stderr; then
    status=0
else
    status=$?
fi
printf 'exit=%s\n' "$status"
cat bad.stdout bad.stderr

預期會看到非法全域記憶體寫入的報告,並定位到 write_value 中的寫入操作。報告通常也會包含存取大小、執行緒位置與位址關係;實際格式會隨工具版本而異,不應將某段固定文字當作唯一的判斷依據。

--error-exitcode 86 能讓偵測到的錯誤影響自動化流程的結果;若受測程式本身已經失敗,也可能取得程式傳回的其他非零值。因此,驗收條件應是「結束代碼非零,且報告指向預期的越界」,而不是強制所有故障都傳回 86。

不要直接將命令接到一般的日誌管線後,只讀取最後一個日誌命令的狀態。上面的重新導向與條件分支能保留真正的執行結果,也適用於已啟用遇錯即退出策略的指令碼。

四、根據位址範圍修正,而非掩蓋症狀

範例只配置了一個整數的空間,寫入索引卻是一,已經超出合法範圍。在實際程式中,需要將報告中的位址對應到所屬的記憶體配置,再檢查索引計算、元素型別、步幅與批次偏移量。

常見問題包括:配置參數以位元組數計算,索引卻以元素數解讀;最後一個執行緒區塊的有效資料不足一整個區塊;二維陣列的列步幅與邏輯寬度不同;已釋放的指標又被另一項工作使用。擴大配置空間可能暫時掩蓋錯誤,卻不能證明演算法正確。

對一維工作,通常應在存取前檢查 index < count,同時確認索引計算所用的整數型別足以容納乘積。對使用共用記憶體協作的核心,不能任意讓部分執行緒提早返回,再讓其餘執行緒進入區塊層級的同步屏障,否則可能引入新的同步問題。

若錯誤只出現在大型工作流程中,可以暫時在可疑階段後加入同步檢查,逐段縮小範圍。每次只新增一個檢查點,保留第一個錯誤;不要在錯誤發生後繼續提交工作,造成大量衍生錯誤。

五、以正反向測試完成修復驗收

執行修復版:

if compute-sanitizer --tool memcheck --error-exitcode 86 \
    ./repro-good > good.stdout 2> good.stderr; then
    status=0
else
    status=$?
fi
printf 'exit=%s\n' "$status"
cat good.stdout good.stderr

本例應同時滿足三個條件:結束代碼為零、偵測報告沒有記憶體存取錯誤、輸出為 result=7。只看「沒有報錯」並不足夠;核心未執行,或結果未真正寫入,也可能讓測試表面上通過。

接著重新執行錯誤版,確認它仍然失敗。這項反向測試能找出偵測命令遭刪除、測試誤用舊的執行檔,或管線吞掉失敗狀態等問題。實際核心還應涵蓋最小有效輸入、完整區塊邊界,以及尾端不足一個完整區塊的輸入;若介面允許空輸入,也應明確定義處理方式。

最後,將原始碼、編譯命令、版本資訊、校驗和及兩次執行日誌一併歸檔。偵測器會改變執行開銷,因此不能直接以偵測期間的耗時作為效能結論;通過 memcheck 也不代表已排除資料競爭與同步問題。可靠的收尾方式,是讓存取合法性、計算結果與失敗狀態傳遞各自都有可供複查的證據,而不只是留下一張成功的螢幕擷圖。

常見問題

CUDA 核心啟動後沒有立即報錯,是否代表存取正確?

不能。核心通常以非同步方式執行,啟動檢查不能取代執行結果檢查。應檢查 cudaGetLastError 與 cudaDeviceSynchronize,再以 Compute Sanitizer 驗證裝置記憶體存取。

memcheck 沒有報錯,是否可以直接部署?

不能只憑一次掃描決定。仍須驗證輸出內容、邊界輸入與失敗結束代碼;memcheck 通過也不代表已排除資料競爭或同步錯誤。

EPYC 7543P 專屬實體主機,每月 $472.5 起

設定 TcpNode AI開發主機 查看 TcpNode 月費

實體主機,依方案清單選配

AMD EPYC 7543P 整機專屬使用。設定前可先確認月費、選配項目與各計費週期的應付金額。

設定 TcpNode AI開發主機