深入 NVIDIA cuDNN Graph API:透過 cuDNN 前端實現融合、自動調優與計畫重用
In this tutorial, we work through the cuDNN Frontend‘s graph API from below the framework: we describe a computation as a graph of operations, let cuDNN pick an engine to run it, and then take control of that choice ourselves.
Every kernel we build here is expressed the same way: we declare tensors by their dimensions and strides, chain operations onto them, run the five-step build pipeline of validate, build operation graph, create execution plans, check support, and build plans, and then execute against a variant pack of pointers.
We run it all on a single Colab GPU, checking each result against a PyTorch reference so we can see both that the fusion is correct and what it costs.
The topics build on each other, moving from a single fused convolution to autotuning across engine configs, FP8-style epilogues, attention, plan serialization, dynamic shapes, and CUDA graph capture.
Copy CodeCopiedUse a different Browserimport os import sys import glob import math import time import ctypes import traceback import subprocess RESULTS = {} def banner(title): print("\n" + "=" 78) print(title) print("=" 78) def section(name): def wrap(fn): def run(a, kw): banner(name) try: out = fn(a, kw) RESULTS[name] = out if isinstance(out, str) else "ok" return out except Exception as e: RESULTS[name] = f"SKIPPED / FAILED -> {type(e).
name}: {e}" print(f"\n[!] {name} did not complete: {type(e).name}: {e}") traceback.printexc(limit=3) return None return run return wrap banner("0.Install nvidia-cudnn-frontend and locate libcudnn") subprocess.run( [sys.
executable, "-m", "pip", "install", "-q", "nvidia-cudnn-frontend"], check=True, ) import torch assert torch.cuda.isavailable(), "No GPU.Runtime -> Change runtime type -> GPU." torch.backends.cudnn.enabled = True = torch.nn.functional.conv2d( torch.randn(1, 1, 8, 8, device="cuda"), torch.
randn(1, 1, 3, 3, device="cuda") ) torch.cuda.synchronize() try: import nvidia.cudnn libdir = os.path.join(os.path.dirname(nvidia.cudnn.file), "lib") os.environ["CUDNNPATH"] = os.path.dirname(nvidia.cudnn.file) os.environ["LDLIBRARYPATH"] = libdir + ":" + os.environ.
get("LDLIBRARYPATH", "") for so in sorted(glob.glob(os.path.join(libdir, "libcudnn.so"))): try: ctypes.CDLL(so, mode=ctypes.
RTLDGLOBAL) except OSError: pass except Exception as e: print(f" (no pip cuDNN package found, relying on system cuDNN: {e})") import cudnn print(" cuDNN frontend imported successfully.") banner("1.Environment") DEV = torch.device("cuda") MAJOR, MINOR = torch.cuda.
getdevicecapability() SM = MAJOR 10 + MINOR CUDNNVER = cudnn.backendversion() print(f" GPU : {torch.cuda.getdevicename(0)}") print(f" Compute capability : sm{SM}") print(f" Torch / CUDA : {torch.version} / {torch.version.
cuda}") print(f" cuDNN backend : {CUDNNVER}") try: print(f" cuDNN version str : {cudnn.backendversionstring()}") except Exception: pass DTYPE = torch.bfloat16 if SM >= 80 else torch.float16 HASSDPA = SM >= 80 print(f" Working dtype : {DTYPE}") print(f" Fused SDPA usable : {HASSDPA}") HANDLE = cudnn.
createhandle() TORCH2CUDNN = { torch.float16: cudnn.datatype.HALF, torch.bfloat16: cudnn.datatype.BFLOAT16, torch.float32: cudnn.datatype.FLOAT, torch.int32: cudnn.datatype.INT32, torch.int64: cudnn.datatype.INT64, torch.int8: cudnn.datatype.INT8, torch.uint8: cudnn.datatype.
UINT8, } def tensorof(graph, t, name): return graph.tensor( name=name, dim=list(t.size()), stride=list(t.stride()), datatype=TORCH2CUDNN[t.dtype], ) def scalarof(graph, name): return graph.tensor( name=name, dim=[1, 1, 1], stride=[1, 1, 1], datatype=cudnn.datatype.
FLOAT, ispassbyvalue=True, ) def build(graph, heur=None, policy=None): heur = heur or [cudnn.heurmode.A, cudnn.heurmode.FALLBACK] graph.validate() graph.buildoperationgraph() graph.createexecutionplans(heur) graph.checksupport() if policy is None: graph.buildplans() else: graph.
buildplans(policy) return graph def workspacefor(graph): n = graph.getworkspacesize() return torch.empty(max(n, 1), device=DEV, dtype=torch.uint8) def bench(fn, warmup=10, iters=50): for in range(warmup): fn() torch.cuda.synchronize() s, e = torch.cuda.Event(True), torch.cuda.Event(True) s.
record() for in range(iters): fn() e.record() torch.cuda.synchronize() return s.elapsedtime(e) / iters def tflops(flops, ms): return flops / (ms 1e-3) / 1e12 def report(tag, ms, flops=None): extra = f" ({tflops(flops, ms):7.2f} TFLOP/s)" if flops else "" print(f" {tag:<34s} {ms:8.
3f} ms{extra}") We start by installing nvidia-cudnn-frontend and solving the problem that trips up most first runs: making libcudnn.so visible to the frontend’s dynamic loader.
We force PyTorch to load its bundled cuDNN first and then preload the shared objects explicitly, so the frontend’s own dlopen resolves against a library already resident in the process.
We then report the compute capability, pick bfloat16 or float16 accordingly, create the cuDNN handle, and define the helpers for tensor description, graph building, workspace allocation, and event-based benchmarking that the rest of the notebook reuses.
Copy CodeCopiedUse a different BrowserN, C, H, W = 32, 128, 56, 56 K, R, S = 256, 3, 3 PAD, STR, DIL = 1, 1, 1 P = (H + 2 PAD - DIL (R - 1) - 1) // STR + 1 Q = (W + 2 PAD - DIL (S - 1) - 1) // STR + 1 CONVFLOPS = 2 N K P Q C R S CONVSTATE = {} @section("2.
Fused Conv -> Bias -> ReLU") def convfusion(): x = torch.randn(N, C, H, W, device=DEV, dtype=DTYPE).to(memoryformat=torch.channelslast) w = torch.randn(K, C, R, S, device=DEV, dtype=DTYPE).to(memoryformat=torch.channelslast) b = torch.randn(1, K, 1, 1, device=DEV, dtype=DTYPE) y = torch.
empty(N, K, P, Q, device=DEV, dtype=DTYPE).to(memoryformat=torch.channelslast) g = cudnn.pygraph( handle=HANDLE, name="convbiasrelu", iodatatype=TORCH2CUDNN[DTYPE], intermediatedatatype=cudnn.datatype.FLOAT, computedatatype=cudnn.datatype.
FLOAT, ) X = tensorof(g, x, "X") Wt = tensorof(g, w, "W") Bt = tensorof(g, b, "bias") conv = g.convfprop( image=X, weight=Wt, padding=[PAD, PAD], stride=[STR, STR], dilation=[DIL, DIL], computedatatype=cudnn.datatype.FLOAT, ) biased = g.bias(input=conv, bias=Bt) Y = g.relu(input=biased) Y.
setoutput(True).setdatatype(TORCH2CUDNN[DTYPE]) Y.setdim(list(y.size())).setstride(list(y.stride())) t0 = time.perfcounter() build(g) buildms = (time.perfcounter() - t0) 1e3 ws = workspacefor(g) pack = {X: x, Wt: w, Bt: b, Y: y} g.execute(pack, ws) torch.cuda.synchronize() ref = torch.relu(torch.nn.
functional.conv2d(x, w, bias=b.flatten(), padding=PAD)) err = (y.float() - ref.float()).abs().max().item() scale = ref.float().abs().max().item() print(f" problem : N{N} C{C} {H}x{W} -> K{K} {R}x{S} ({DTYPE})") print(f" build : {buildms:.1f} ms workspace: {ws.numel()/1024:.
1f} KiB") print(f" max |err|: {err:.4f} (ref max {scale:.2f}, rel {err/max(scale,1e-9):.2e})") assert err / max(scale, 1e-9) < 5e-2, "numerical mismatch vs PyTorch" mscudnn = bench(lambda: g.execute(pack, ws)) mstorch = bench(lambda: torch.relu( torch.nn.functional.conv2d(x, w, bias=b.
flatten(), padding=PAD))) print() report("cuDNN FE (single fused kernel)", mscudnn, CONVFLOPS) report("PyTorch (conv+bias, then relu)", mstorch, CONVFLOPS) print(f" speedup: {mstorch/mscudnn:.2f}x") CONVSTATE.update(graph=g, pack=pack, ws=ws, x=x, w=w, b=b, y=y) return f"{mscudnn:.
3f} ms, {tflops(CONVFLOPS, mscudnn):.1f} TFLOP/s" convfusion() We build our first graph, a convolution followed by a bias add and a ReLU, all fused into a single kernel.
We keep every tensor in channels_last because that is what gives cuDNN the NHWC strides its tensor-core engines want, and we pin the output dimensions and strides explicitly so the result is written back in the same layout.We validate the output against torch.nn.functional.
conv2d, then benchmark the fused graph against PyTorch running the convolution and activation as separate kernels.Copy CodeCopiedUse a diffe
Related
相關文章
國內首個半導體工藝保護流體材料重大突破
本文作者: 徐咪 2026-11-03 17:41 導語:上海凱銳恩將持續加大研發與產業化驗證投入,深化與國內半導體設備企業的協同合作,將半導體工藝保護流體成型技術推向全球市場。歷時近七年自主攻關,上海凱銳恩半導體科技有限公司成功開發出應用於晶圓級工藝保護的流體成型材料及配套熱壓成型設備,建立起完整的知識產權體系,實現“材料+設備+工藝”三位一體。
預售24.98萬起,全系搭載寧德電池,LS8 NEO能否撬動25萬級市場?
25萬級SUV的市場正在呈現鮮明的對比:增程賽道的競爭日漸旺盛,而純電市場回暖增長。在4月發佈的LS8增程版本基礎上,智己順勢推出了LS8 NEO,卡位純電競爭。預售權益價為24.98萬起,新車定於11月5日正式上市。在這臺25萬級的SUV上,全系標配了寧德時代電池、全線控靈蜥底盤、後輪轉向和800V高壓平臺。
xTool 的「快」與「難」:硬門檻、多線程研發與生態擴張
一個女孩在生成器裡輸入了第一次約會的日期和城市。那天是農曆十六,他們在海邊,月亮又大又圓。系統調取 NASA 的真實月相數據,還原了當晚月亮的樣子。她把它雕刻出來,做成一盞可以捧在手裡的月亮燈。這輪月亮,在任何一個模型網站上都找不到。因為那是隻屬於她的月亮。
阿里達摩院玄鐵 C950 榮獲“中國芯”年度重大創新突破產品獎
11月2日,以“智啟芯途 協同共生”為主題的 2026年“中國芯”集成電路產業促進大會暨第二十一屆“中國芯”優秀產品徵集結果發佈儀式在珠海橫琴舉行。大會匯聚集成電路領域院士、專家與產業代表,共同交流技術創新與產業協同發展的新路徑。本屆大會上,阿里巴巴達摩院旗下玄鐵 C950 榮獲“年度重大創新突破產品獎”,表彰玄鐵 C950 在高性能 RISC-V 處理器領域取得的實質性技術創新突破。
速賣通雙11開門紅!品牌成交同比大漲200%
海外雙11於11月1日正式開賣,記者從阿里旗下跨境電商平臺速賣通獲悉,開賣首日,速賣通Brand+品牌生意火爆,和去年11月1日大促相比,成交大漲超200%,滲透率突破50%。 在稅改、監管變動的大背景下,速賣通搶先佈局品牌化+本地化,成為品牌出海全新主場。多個國家市場的品牌成交均實現三位數增長,其中歐盟逆勢大漲181%,美國、韓國增長超4倍,巴西也實現翻倍增長。 海外雙11首日還湧現出一批新的百萬美金商家。除手機品牌小米、POCO、榮耀外,儲能品牌Oukitel、電氣設備品牌Zendure、清潔家電品牌Laresar等,首日成交額均突破100萬美元。 (多個速賣通品牌商家表示雙11銷量超預期) “銷量超預期!”“賣得最快的一次!”是速賣通品牌商家們對這次雙11的普遍感受。在Brand+計劃的護航之下,速賣通多個品類在多個國家市場實現全面開花。 電氣設備品牌EcoFlow橫掃歐美。今年9月,EcoFlow在柏林國際消費電子展上與速賣通Brand+達成合作,隨後即在雙11大促中迎來相比日銷150倍的爆發,拿下德國市場家裝類目單品Top1。 家居體脂秤品牌沃萊合作速賣通網紅帶貨,“平均1分鐘出3單!”雙11首日銷售額相當於近一個月總和的3倍。 清潔電器品牌Uwant聯合Brand+在韓國新品首發集塵站吸塵器V600,首日銷售額超10萬美金;1200臺除蟎儀40分鐘售罄;品牌銷量首日同比去年增長218%。 咖啡用具品牌1ZPRESSO,通過Brand+提前鎖定價格和庫存,強勢綁定韓國網紅測評及團購資源,爆款單品的首日銷量追平8月大促全週期。 騎行運動品牌RAMYPOCKET及ELITEWHEELS也雙雙在歐洲“拿到大結果”。通過提前在速賣通歐洲官方倉備貨超萬臺,跑步機品牌RAMYPOCKET首日賣出近2000臺跑步機,自行車輪組品牌ELITEWHEELS銷售額同比增長超300%。
中國市場賣爆了,蘋果年淨利8800億!新CEO回應iPhone Duo裂屏
作者 | 楊京麗 編輯 | 雲鵬 11月3日報道,今天凌晨,蘋果公佈2026財年第四季度(對應自然年2026年第三季度)及全年財務數據。財報發佈後,新任CEO約翰·特努斯(John Ternus)首次主持蘋果財報電話會。 本季度,蘋果營收達到1144.77億美元(約合人民幣7611億元),同比增長11.7%;淨利潤達到307.11億美元(約合人民幣2042億元),同比增長11.8%;攤薄後每股收益為2.10美元(約合人民幣13.96元),同比增長約14%。