Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert skill for CUDA Graph capture and optimization for reduced launch overhead. Capture CUDA operations into graphs, instantiate and execute graph instances, update graph node parameters, profile graph vs stream execution, design graph-friendly kernel patterns, and optimize launch latency for inference.
.claude/skills/a5c-ai-cuda-graphs/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 16% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 155% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 150% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 44% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 112% | 0% |
You are cuda-graphs - a specialized skill for CUDA Graph capture and optimization. This skill provides expert capabilities for reducing kernel launch overhead and optimizing execution patterns through graph-based workflows.
This skill enables AI-powered CUDA Graph operations including:
Capture stream operations into a graph: domains: domain:scientific-computing] specializations: specialization:gpu-programming] skillAreas: skill-area:cuda-kernels, skill-area:compute-shaders, skill-area:profiling-cuda] roles: role:computational-scientist, role:ml-engineer]
cuda#include <cuda_runtime.h> cudaGraph_t graph; cudaGraphExec_t graphExec; cudaStream_t stream; cudaStreamCreate(&stream); // Begin stream capture cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal); // Record operations to be captured kernel1<<<grid1, block1, 0, stream>>>(args1); kernel2<<<grid2, block2, 0, stream>>>(args2); kernel3<<<grid3, block3, 0, stream>>>(args3); // End capture and create graph cudaStreamEndCapture(stream, &graph); // Instantiate the graph for execution cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0); // Execute the graph (much lower overhead than individual launches) for (int i = 0; i < iterations; i++) { cudaGraphLaunch(graphExec, stream); } cudaStreamSynchronize(stream); // Cleanup cudaGraphExecDestroy(graphExec); cudaGraphDestroy(graph); cudaStreamDestroy(stream);
Build graphs programmatically:
cudacudaGraph_t graph; cudaGraphCreate(&graph, 0); // Create kernel nodes cudaKernelNodeParams kernelParams1 = {0}; kernelParams1.func = (void*)kernel1; kernelParams1.gridDim = grid1; kernelParams1.blockDim = block1; kernelParams1.sharedMemBytes = 0; kernelParams1.kernelParams = kernelArgs1; cudaKernelNodeParams kernelParams2 = {0}; kernelParams2.func = (void*)kernel2; kernelParams2.gridDim = grid2; kernelParams2.blockDim = block2; kernelParams2.sharedMemBytes = 0; kernelParams2.kernelParams = kernelArgs2; cudaGraphNode_t node1, node2; // Add first kernel (no dependencies) cudaGraphAddKernelNode(&node1, graph, NULL, 0, &kernelParams1); // Add second kernel (depends on first) cudaGraphNode_t dependencies[] = {node1}; cudaGraphAddKernelNode(&node2, graph, dependencies, 1, &kernelParams2); // Instantiate and execute cudaGraphExec_t graphExec; cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0); cudaGraphLaunch(graphExec, stream);
cuda// Memory copy node cudaMemcpy3DParms copyParams = {0}; // ... configure copy parameters cudaGraphNode_t copyNode; cudaGraphAddMemcpyNode(©Node, graph, NULL, 0, ©Params); // Memset node cudaMemsetParams memsetParams = {0}; memsetParams.dst = d_array; memsetParams.value = 0; memsetParams.pitch = 0; memsetParams.elementSize = sizeof(int); memsetParams.width = N; memsetParams.height = 1; cudaGraphNode_t memsetNode; cudaGraphAddMemsetNode(&memsetNode, graph, NULL, 0, &memsetParams); // Host function node cudaHostNodeParams hostParams = {0}; hostParams.fn = hostCallback; hostParams.userData = userData; cudaGraphNode_t hostNode; cudaGraphAddHostNode(&hostNode, graph, dependencies, numDeps, &hostParams); // Event record/wait nodes (CUDA 11.1+) cudaEvent_t event; cudaEventCreate(&event); cudaGraphNode_t eventRecordNode, eventWaitNode; cudaGraphAddEventRecordNode(&eventRecordNode, graph, deps, numDeps, event); cudaGraphAddEventWaitNode(&eventWaitNode, graph, deps, numDeps, event); // Empty node (for dependencies only) cudaGraphNode_t emptyNode; cudaGraphAddEmptyNode(&emptyNode, graph, deps, numDeps);
Update graph parameters without rebuilding:
cudacudaGraph_t graph; cudaGraphExec_t graphExec; // Initial capture cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal); myKernel<<<grid, block, 0, stream>>>(d_input, d_output, N); cudaStreamEndCapture(stream, &graph); cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0); // Execute initial graph cudaGraphLaunch(graphExec, stream); cudaStreamSynchronize(stream); // Update the graph with new capture cudaGraph_t newGraph; cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal); myKernel<<<grid, block, 0, stream>>>(d_input2, d_output2, N); // Different pointers cudaStreamEndCapture(stream, &newGraph); // Update executable graph cudaGraphExecUpdateResult updateResult; cudaGraphExecUpdate(graphExec, newGraph, NULL, &updateResult); if (updateResult == cudaGraphExecUpdateSuccess) { // Graph updated successfully cudaGraphLaunch(graphExec, stream); } else { // Need to reinstantiate cudaGraphExecDestroy(graphExec); cudaGraphInstantiate(&graphExec, newGraph, NULL, NULL, 0); cudaGraphLaunch(graphExec, stream); } cudaGraphDestroy(newGraph);
cuda// Get kernel node from graph cudaGraphNode_t* nodes; size_t numNodes; cudaGraphGetNodes(graph, NULL, &numNodes); nodes = new cudaGraphNode_t[numNodes]; cudaGraphGetNodes(graph, nodes, &numNodes); // Find and update kernel node for (size_t i = 0; i < numNodes; i++) { cudaGraphNodeType nodeType; cudaGraphNodeGetType(nodes[i], &nodeType); if (nodeType == cudaGraphNodeTypeKernel) { cudaKernelNodeParams params; cudaGraphKernelNodeGetParams(nodes[i], ¶ms); // Update parameters void* newArgs[] = {&newInput, &newOutput, &newN}; params.kernelParams = newArgs; // Set new parameters cudaGraphExecKernelNodeSetParams(graphExec, nodes[i], ¶ms); } } delete[] nodes;
cudavoid benchmarkGraphVsStreams(int numKernels, int iterations) { cudaEvent_t start, stop; cudaEventCreate(&start); cudaEventCreate(&stop); // Benchmark stream-based execution cudaEventRecord(start); for (int i = 0; i < iterations; i++) { for (int k = 0; k < numKernels; k++) { smallKernel<<<grid, block, 0, stream>>>(d_data, N); } } cudaEventRecord(stop); cudaEventSynchronize(stop); float streamTime; cudaEventElapsedTime(&streamTime, start, stop); // Benchmark graph-based execution cudaGraph_t graph; cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal); for (int k = 0; k < numKernels; k++) { smallKernel<<<grid, block, 0, stream>>>(d_data, N); } cudaStreamEndCapture(stream, &graph); cudaGraphExec_t graphExec; cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0); cudaEventRecord(start); for (int i = 0; i < iterations; i++) { cudaGraphLaunch(graphExec, stream); } cudaEventRecord(stop); cudaEventSynchronize(stop); float graphTime; cudaEventElapsedTime(&graphTime, start, stop); printf("Stream execution: %.3f ms\n", streamTime); printf("Graph execution: %.3f ms\n", graphTime); printf("Speedup: %.2fx\n", streamTime / graphTime); cudaGraphExecDestroy(graphExec); cudaGraphDestroy(graph); }
cudaclass InferenceGraphPipeline { private: cudaGraph_t graph; cudaGraphExec_t graphExec; cudaStream_t stream; // Model weights (constant during inference) float* d_weights1; float* d_weights2; // Buffers (reused per inference) float* d_input; float* d_hidden; float* d_output; public: void initGraph() { cudaStreamCreate(&stream); // Capture inference operations cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal); // Layer 1: Input -> Hidden matmulKernel<<<grid1, block1, 0, stream>>>(d_input, d_weights1, d_hidden, M, K, N1); reluKernel<<<(N1 + 255)/256, 256, 0, stream>>>(d_hidden, N1); // Layer 2: Hidden -> Output matmulKernel<<<grid2, block2, 0, stream>>>(d_hidden, d_weights2, d_output, M, N1, N2); softmaxKernel<<<M, 256, 0, stream>>>(d_output, N2); cudaStreamEndCapture(stream, &graph); cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0); } void infer(float* h_input, float* h_output, int batchSize) { // Copy input to device cudaMemcpyAsync(d_input, h_input, batchSize * inputSize * sizeof(float), cudaMemcpyHostToDevice, stream); // Execute inference graph - very low overhead! cudaGraphLaunch(graphExec, stream); // Copy output to host cudaMemcpyAsync(h_output, d_output, batchSize * outputSize * sizeof(float), cudaMemcpyDeviceToHost, stream); cudaStreamSynchronize(stream); } void updateInputBuffer(float* newInput) { // Update graph to use new input buffer // ... graph update code } };
cuda// CUDA 12.0+ conditional graph execution cudaGraph_t graph; cudaGraphCreate(&graph, 0); // Create conditional node cudaGraphConditionalHandle conditionalHandle; cudaGraphConditionalHandleCreate(&conditionalHandle, graph, 0, 0); // Create condition check node cudaGraphNode_t conditionNode; cudaKernelNodeParams condParams = {0}; condParams.func = (void*)checkConditionKernel; // ... configure params cudaGraphAddKernelNode(&conditionNode, graph, NULL, 0, &condParams); // Create conditional body graph cudaGraph_t bodyGraph; cudaGraphCreate(&bodyGraph, 0); // ... add nodes to body graph // Add conditional node cudaGraphNodeParams nodeParams = {0}; nodeParams.type = cudaGraphNodeTypeConditional; nodeParams.conditional.handle = conditionalHandle; nodeParams.conditional.type = cudaGraphCondTypeIf; nodeParams.conditional.size = 1; nodeParams.conditional.phGraph_out = &bodyGraph; cudaGraphNode_t conditionalNode; cudaGraphAddNode(&conditionalNode, graph, &conditionNode, 1, &nodeParams);
cuda// Export graph to DOT format for visualization void exportGraphToDot(cudaGraph_t graph, const char* filename) { cudaGraphDebugDotPrint(graph, filename, cudaGraphDebugDotFlagsVerbose); } // Get graph statistics void printGraphStats(cudaGraph_t graph) { cudaGraphNode_t* nodes; size_t numNodes; cudaGraphGetNodes(graph, NULL, &numNodes); nodes = new cudaGraphNode_t[numNodes]; cudaGraphGetNodes(graph, nodes, &numNodes); int kernelCount = 0, memcpyCount = 0, memsetCount = 0; for (size_t i = 0; i < numNodes; i++) { cudaGraphNodeType nodeType; cudaGraphNodeGetType(nodes[i], &nodeType); switch (nodeType) { case cudaGraphNodeTypeKernel: kernelCount++; break; case cudaGraphNodeTypeMemcpy: memcpyCount++; break; case cudaGraphNodeTypeMemset: memsetCount++; break; } } printf("Graph Statistics:\n"); printf(" Total nodes: %zu\n", numNodes); printf(" Kernel nodes: %d\n", kernelCount); printf(" Memcpy nodes: %d\n", memcpyCount); printf(" Memset nodes: %d\n", memsetCount); delete[] nodes; }
| Use Case | Benefit | |----------|---------| | Many small kernels | Reduces launch overhead | | Repeated execution patterns | Amortize capture cost | | ML inference | Consistent low latency | | Batch processing | Efficient repeated execution |
| Scenario | Traditional | With Graph | Speedup | |----------|-------------|------------|---------| | 10 small kernels | ~20-50us overhead | ~10us overhead | 2-5x | | 100 small kernels | ~200-500us overhead | ~10us overhead | 20-50x | | Inference pipeline | Variable | Consistent | Lower latency variance |
This skill integrates with the following processes:
cuda-stream-concurrency.js - Stream optimizationml-inference-optimization.js - Inference pipelinesdynamic-parallelism-implementation.js - Execution patternsWhen executing operations, provide structured output:
json{ "operation": "capture-graph", "status": "success", "graph": { "nodes": 15, "kernels": 10, "memcpys": 3, "memsets": 2, "dependencies": 14 }, "performance": { "capture_time_ms": 0.5, "instantiate_time_ms": 1.2, "launch_overhead_us": 8.5, "traditional_overhead_us": 45.0, "speedup": "5.3x" }, "recommendations": [ "Graph suitable for repeated execution", "Consider batching memcpy nodes" ], "artifacts": ["graph_debug.dot", "graph_stats.json"] }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 38,099 | 32,141 | -16% | 1 | 1 | 0% | 8,273 | 9,606 | +16% | 0 | 0 | — |
case-02 | fail→fail | 39,084 | 27,739 | -29% | 1 | 1 | 0% | 8,274 | 10,406 | +26% | 0 | 0 | — |
case-03 | pass→pass | 28,349 | 23,601 | -17% | 1 | 1 | 0% | 6,593 | 9,511 | +44% | 0 | 0 | — |
case-04 | fail→pass | 16,197 | 17,394 | +7% | 1 | 1 | 0% | 2,863 | 7,293 | +155% | 0 | 0 | — |
case-05 | pass→pass | 23,828 | 20,881 | -12% | 1 | 1 | 0% | 3,540 | 7,498 | +112% | 0 | 0 | — |
case-06 | pass→pass | 31,907 | 14,045 | -56% | 1 | 1 | 0% | 5,110 | 6,428 | +26% | 0 | 0 | — |
case-07 | pass→pass | 19,503 | 14,646 | -25% | 1 | 1 | 0% | 3,144 | 6,345 | +102% | 0 | 0 | — |
case-08 | pass→pass | 18,230 | 11,010 | -40% | 1 | 1 | 0% | 3,611 | 6,266 | +74% | 0 | 0 | — |
case-09 | pass→pass | 19,128 | 11,097 | -42% | 1 | 1 | 0% | 2,455 | 6,127 | +150% | 0 | 0 | — |
case-10 | pass→pass | 15,769 | 15,268 | -3% | 1 | 1 | 0% | 2,268 | 6,209 | +174% | 0 | 0 | — |
case-11 | pass→pass | 12,848 | 10,179 | -21% | 1 | 1 | 0% | 2,853 | 6,047 | +112% | 0 | 0 | — |
case-12 | pass→pass | 25,221 | 15,367 | -39% | 1 | 1 | 0% | 3,776 | 6,937 | +84% | 0 | 0 | — |
case-13 | pass→pass | 26,992 | 21,404 | -21% | 1 | 1 | 0% | 6,114 | 8,761 | +43% | 0 | 0 | — |
case-14 | fail→pass | 40,107 | 27,077 | -32% | 1 | 1 | 0% | 3,355 | 8,402 | +150% | 0 | 0 | — |
case-15 | pass→pass | 17,833 | 17,675 | -1% | 1 | 1 | 0% | 3,963 | 6,699 | +69% | 0 | 0 | — |
case-16 | pass→pass | 18,543 | 17,724 | -4% | 1 | 1 | 0% | 2,976 | 6,844 | +130% | 0 | 0 | — |
case-17 | pass→pass | 6,560 | 7,034 | +7% | 1 | 1 | 0% | 1,151 | 5,120 | +345% | 0 | 0 | — |
case-18 | pass→pass | 5,001 | 5,960 | +19% | 1 | 1 | 0% | 649 | 4,719 | +627% | 0 | 0 | — |
case-19 | pass→pass | 5,808 | 7,223 | +24% | 1 | 1 | 0% | 943 | 4,969 | +427% | 0 | 0 | — |
case-20 | pass→pass | 22,382 | 46,308 | +107% | 1 | 1 | 0% | 4,355 | 8,648 | +99% | 0 | 0 | — |
case-21 | pass→pass | 17,411 | 11,887 | -32% | 1 | 1 | 0% | 2,684 | 6,156 | +129% | 0 | 0 | — |
case-22 | pass→pass | 11,809 | 12,034 | +2% | 1 | 1 | 0% | 1,779 | 5,933 | +234% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.