Back to Onnxruntime

CUDA Kernel Workspace Buffer Inventory

docs/annotated_partitioning/cuda_kernel_workspace_inventory.md

1.28.016.4 KB
Original Source

CUDA Kernel Workspace Buffer Inventory

This document catalogs all CUDA kernels in ONNX Runtime that allocate temporary/workspace buffers via GetScratchBuffer or GetTransientScratchBuffer. For each kernel, it identifies what information is needed to compute the workspace size and whether that information is available at GetCapability() time (for the workspace estimation function design).

Classification Key

SymbolMeaning
Fully determinable from shapes + attributes + device properties
✅*Determinable via cuDNN/cuBLAS API call (needs handle, available on EP)
⚠️Requires profiling/tactic selection (deterministic but costly at planning time)

Core CUDA Providers (onnxruntime/core/providers/cuda/)

1. Attention (LLM — Opset 23/24)

File: llm/attention.cc

Buffers allocated:

BufferSize formulaDepends on
softmax_lse_bufferB * S_q * num_heads * sizeof(float)shapes
softmax_lse_accum_bufferFrom get_num_splits_and_buffer_sizes()shapes + multiProcessorCount
out_accum_bufferFrom get_num_splits_and_buffer_sizes()shapes + multiProcessorCount
q_bsnh_bufferB * S_q * num_heads * head_size * element_sizeshapes + dtype
out_bsnh_bufferSame as Qshapes + dtype
k_bsnh_buffer / v_bsnh_bufferB * S_kv * num_kv_heads * head_size * element_sizeshapes + dtype
seqlens_k_bufferB * sizeof(int)batch size
past_seqlens_bufferB * sizeof(int)batch size
k_expand_buffer / v_expand_bufferB * num_heads * S_kv * head_size * element_size (GQA expansion)shapes + dtype
converted_mask_bufferB * S_q * S_kv * sizeof(float)shapes
present_k_scratch / present_v_scratchpresent KV cache sizeshapes
workspace_buffer (math attention)B * S_q * num_heads * sizeof(float)shapes

What's needed to compute: Input shapes, num_heads attribute, head_size, dtype, device_prop.multiProcessorCount.

Static determinability: ✅ All pure arithmetic on shapes + device SM count.


2. Conv (cuDNN Frontend)

File: nn/conv.cc, nn/conv.h

Buffers allocated:

BufferSize formulaDepends on
workspaces_.cudnn_fe_graph->get_workspace_size()cuDNN plan selection
memory_for_cudnn_conv_results (Conv8 only)y_dims_with_adjusted_pads.Size() * element_sizeoutput shape + padding

What's needed to compute: Input shapes (NCHW), weight shapes, pads/strides/dilations attributes, cuDNN handle (for build_plans()).

Static determinability: ✅* — Requires calling build_plans(handle) with HEUR_MODE_A. The handle is on the EP. All tensor shapes and conv params come from node attributes.

Note: GetTransientScratchBuffer (32MB) used for algorithm search in Conv8 — this is a one-time cost during first Compute, not a per-run workspace.


3. ConvTranspose

File: nn/conv_transpose.h, nn/conv_transpose_8.h

Buffers allocated:

BufferSize formulaDepends on
workspaces_.workspace_bytes (from cuDNN FE or algo selection)Same as Conv
AlgoSearchWorkspaceSize (Conv8 path)32MB constantN/A

Static determinability: ✅* — Same as Conv.


4. DeformConv

File: nn/deform_conv.cc

Buffers allocated:

BufferSize formulaDepends on
col_bufferC * kernel_size * col_stride * sizeof(T) where col_stride = n_parallel_imgs * output_image_sizeInput shapes, kernel_size, device_prop.totalGlobalMem (for chunk sizing)

What's needed: Input shape (N,C,H,W), kernel dims, output_image_size, totalGlobalMem (determines n_parallel_imgs via GetNParallelImgs).

Static determinability: ✅ — Pure arithmetic on shapes + device memory size.


5. BatchNorm

File: nn/batch_norm.cc

Buffers allocated:

BufferSize formulaDepends on
f_scale, f_B, f_mean, f_varC * sizeof(float) eachChannel dim (shape[1] or shape[3])

What's needed: Channel dimension C from input shape.

Static determinability: ✅ — Trivial: 4 * C * sizeof(float).


6. InstanceNorm

File: nn/instance_norm.cc

Buffers allocated:

BufferSize formulaDepends on
mean, varianceN * C * sizeof(T)Batch × channels
unused_scale, unused_biasN * C * sizeof(T)Same
scale_data_fp32, bias_data_fp32C * sizeof(float) (if fp16)Channel dim + dtype

What's needed: Input shape (N, C), dtype.

Static determinability: ✅ — Pure arithmetic on shapes.


7. Dropout

File: nn/dropout.cc

Buffers allocated:

BufferSize formulaDepends on
mask bufferelement_count * sizeof(bool) or element_count / 16 * sizeof(BitmaskElementType)Input element count, bitmask mode

Static determinability: ✅ — Input element count.


8. Reduction Ops (ReduceSum, ReduceMax, ReduceMean, etc.)

File: reduction/reduction_ops.cc

Buffers allocated:

BufferSize formulaDepends on
workspace_cudacudnnGetReductionWorkspaceSize()cuDNN handle, input/output tensor descriptors
indices_cudacudnnGetReductionIndicesSize()Same
temp_Xinput_count * sizeof(float) (type cast)Input size
input_data_bufferinput_count * sizeof(T) (for calculate_sqt_)Input size
exp_result_bufferinput_count * sizeof(T) (for log_sum_exp_)Input size
log_sum_result_bufferoutput_count * sizeof(T)Output size
temp_outputoutput_count * sizeof(float)Output size

What's needed: Input/output shapes, reduction axes, op variant (LogSumExp, L2, etc.), cuDNN handle.

Static determinability: ✅* — cuDNN workspace query needs handle + tensor descriptors (constructible from shapes).


9. RNN (LSTM/GRU)

File: rnn/cudnn_rnn_base.cc

Buffers allocated:

BufferSize formulaDepends on
workspace_cudacudnnGetRNNTempSpaceSizes(fwdInference)RNN descriptor, seq_length, batch_size
reservespace_cudacudnnGetRNNTempSpaceSizes(training)Same
reorganized_w_dataw_size * sizeof(T)hidden_size, num_layers, input_size, direction
x_reversed_dataseq_length * batch_size * input_size * sizeof(T)Shapes (bidirectional case)
y_alloc_dataoutput_size * sizeof(T)Shapes
state_buffer_RNN state size from cuDNNcuDNN descriptor

What's needed: seq_length, batch_size, input_size, hidden_size, num_layers, direction attribute, cuDNN handle.

Static determinability: ✅* — cuDNN API queries, all inputs available from node attributes/shapes.


10. TopK

File: math/topk_impl.cuh

Buffers allocated:

BufferSize formulaDepends on
input_key_bufferdimension * sizeof(T)Last-axis dimension
output_key_bufferdimension * sizeof(T)Same
input_value_bufferdimension * sizeof(int64_t)Same
output_value_bufferdimension * sizeof(int64_t)Same
temp_storage_bufferFrom cub::DeviceRadixSort::SortPairs querydimension

What's needed: Dimension (last axis size), k, dtype.

Static determinability: ✅ — CUB temp storage query is deterministic given size.


11. MatMulInteger

File: math/matmul_integer.cc

Buffers allocated:

BufferSize formulaDepends on
a_row_buf(output_size / N) * sizeof(int32_t)M dimension
b_col_buf(output_size / M) * sizeof(int32_t)N dimension

What's needed: M, N dimensions from MatMul shapes.

Static determinability: ✅ — Pure arithmetic.


12. IntegerGemm (int8 alignment padding)

File: integer_gemm.cc

Buffers allocated:

BufferSize formulaDepends on
a_paddedm * roundoff(lda, 32) * sizeof(int8_t) (only if lda not 32-aligned)M, K dims
b_paddedk * roundoff(ldb, 32) * sizeof(int8_t) (only if ldb not 32-aligned)K, N dims

What's needed: M, K, N dimensions + alignment check.

Static determinability: ✅ — Pure arithmetic.


13. Compress

File: tensor/compress.cc

Buffers allocated:

BufferSize formulaDepends on
condition_cumulative_sum_buffervalid_condition_length * sizeof(int32_t)Condition tensor size
temp_bufferCUB DeviceScan::InclusiveSum temp storageCondition size

Static determinability: ✅ — Condition shape determines everything.


14. GatherND

File: tensor/gather_nd.cc

Buffers allocated:

BufferSize formulaDepends on
sizes_from_slice_dims_buffernum_slice_dims * sizeof(int64_t)Indices shape
input_slice_offsets_buffernum_slices * sizeof(int64_t)Indices shape[:-1] product

Static determinability: ✅ — Indices shape.


15. NonZero

File: tensor/nonzero_op.cc

Buffers allocated:

BufferSize formulaDepends on
prefix_buffernumber_of_blocks * sizeof(int)Input element count / block_size
temp_bufferCUB temp storageInput element count

Static determinability: ✅ — Input element count.


16. Upsample/Resize

File: tensor/upsample.cc

Buffers allocated:

BufferSize formulaDepends on
temp buffer (via lambda)Varies by resize modeInput/output shapes, mode
dims_mapping_buffertemp_buffer_size (coordinate mapping)Output shape

Static determinability: ✅ — Input/output shapes + mode attribute.


17. NonMaxSuppression

File: object_detection/non_max_suppression.cc

Buffers allocated:

BufferSize formulaDepends on
Various (via lambda)Determined by CUB DeviceSelect internalsnum_boxes, num_classes

What's needed: boxes shape (num_batches, num_boxes, 4), scores shape.

Static determinability: ✅ — CUB queries are deterministic given sizes.


Contrib CUDA Operators (onnxruntime/contrib_ops/cuda/)

18. Attention / MultiHeadAttention (Contrib)

File: bert/attention.cc, bert/multihead_attention.cc

Buffers: Uses GetAttentionWorkspaceSize() helper function.

Size formula: Depends on attention algorithm (Flash, MemoryEfficient, FusedRunner, Default):

  • Flash: qkv_size (Q+K+V projection)
  • MemoryEfficient: qkv_size + output_accum (float)
  • Default (unfused): qkv_size + 2 * attention_scratch_size

What's needed: B, S_q, S_kv, num_heads, head_size, dtype, which attention algorithm is selected.

Static determinability: ✅ — Algorithm selection depends on shapes + SM version (available from device_prop).


19. MOE (Mixture of Experts)

File: moe/moe.cc, moe/moe_quantization.cc

Buffers allocated:

BufferSize formulaDepends on
workspacemoe_runner->getWorkspaceSize(num_rows, hidden, inter, experts, k)Shapes + tactic
expert_scalesnum_rows * k * sizeof(float)Shapes
expert_indicesnum_rows * k * sizeof(int)Shapes
permutation_row_mapnum_rows * k * sizeof(int)Shapes

What's needed: num_rows, hidden_size, inter_size, num_experts, k, activation_type, SM version, selected tactic.

Static determinability: ⚠️ — getWorkspaceSize() depends on profiled best tactic (CUTLASS config). Could use worst-case across tactics as upper bound.


20. MatMulNBits (Quantized MatMul)

File: quantization/matmul_nbits.cc

Buffers allocated:

BufferSize formulaDepends on
workspace_bufferweightOnlyGemmRunner_->getWorkspaceSize(m, n, k)Dims + tactic
packed_transposed_weight_spacepacked_weight_bytes (transient)Weight shape
permutation_map_buffer32 * sizeof(int32_t) (transient)Constant

What's needed: M, N, K dimensions, quantization bits, SM version.

Static determinability: ⚠️ — Runner workspace depends on profiled tactic. Could use upper bound.


21. fpA_intB GEMM (FP×INT quantized)

File: llm/fpA_intB_gemm/

Buffers: virtual size_t getWorkspaceSize(m, n, k).

What's needed: M, N, K + CUTLASS template specialization.

Static determinability: ⚠️ — Depends on selected CUTLASS config/tactic.


22. Inverse (Matrix Inversion)

File: inverse.cc

Buffers allocated:

BufferSize formulaDepends on
input_workspaceinput_count * sizeof(T)Matrix dimensions
matrix_ptrsn_batches * sizeof(T*)Batch count
output_ptrsn_batches * sizeof(T*)Batch count
ml_float_outputinput_count * sizeof(float) (if fp16→fp32)Dims + dtype

Static determinability: ✅ — Pure arithmetic on matrix dimensions.


23. Generation (Beam Search / Sampling)

File: transformers/generation_device_helper.cc

Buffers: Various pinned + device buffers for beam state.

What's needed: batch_size, beam_width, max_length, vocab_size.

Static determinability: ✅ — All from session/model config.


Summary: Coverage Analysis

Workspace estimation approach validation

Category# KernelsEstimation feasibilityNotes
Shapes only12✅ Exact, trivialBatchNorm, InstanceNorm, Dropout, TopK, MatMulInteger, IntegerGemm, Compress, GatherND, NonZero, Upsample, Inverse, Generation
Shapes + device properties3✅ ExactAttention (SM count), DeformConv (totalGlobalMem), Contrib Attention (SM version)
Shapes + cuDNN/cuBLAS handle4✅* Exact via API queryConv, ConvTranspose, Reduction, RNN
Shapes + tactic profiling3⚠️ Upper bound onlyMOE, MatMulNBits, fpA_intB_GEMM

Key takeaways

  1. ~75% of kernels (19/25) can produce exact workspace estimates at GetCapability() time using only shapes + attributes + device properties (+ cuDNN handle for API queries).

  2. ~12% of kernels (3/25) require tactic profiling (CUTLASS/CUB autotuning). For these, options are:

    • Use worst-case workspace across all tactics (safe upper bound)
    • Run tactic selection eagerly at estimation time (expensive but exact)
    • Accept 1.5x multiplier for these few kernels
  3. The cuDNN handle requirement affects only 4 kernel types (Conv, ConvTranspose, Reduction, RNN). All are standard cuDNN API queries that are fast and deterministic given the handle + tensor descriptors.

  4. No kernel requires actual GPU execution to determine workspace size — even tactic-based kernels select tactics via CPU-side profiling/heuristics, not by running GPU code.

  5. Largest workspace consumers in practice:

    • Attention (Flash): dominates in LLM workloads. Exact estimation possible.
    • Conv (cuDNN): dominates in vision workloads. Exact via build_plans().
    • MOE: significant in MoE models. Upper bound via worst-case tactic.

What the estimation function needs access to (API requirements):

Access neededHow accessedKernels that require it
Node_GetInputShape()OrtEpApi (generic)All 25 kernels
Node_GetAttributeInt/Ints()OrtEpApi (generic)Conv, Attention, RNN, MOE
device_prop.multiProcessorCountCast OrtEp* to concrete EP typeAttention, DeformConv
device_prop.totalGlobalMemCast OrtEp* to concrete EP typeDeformConv
cuDNN handleCast OrtEp* to concrete EP typeConv, ConvTranspose, Reduction, RNN
Tactic profiler state (or worst-case constant)Cast OrtEp* to concrete EP typeMOE, MatMulNBits, fpA_intB

API surface: Only Node_GetInputShape and Node_GetAttributeInt/Ints need to be added to OrtEpApi (generic, EP-agnostic). All device-specific state (cuDNN handles, device properties, profiler state) is accessed by casting OrtEp* to the EP's concrete type — no public API needed since the estimation function is EP-specific code.