@@ -36,6 +36,36 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
3636file (GLOB BENCHMARK_ONNX_MODELS
3737 "${CMAKE_CURRENT_SOURCE_DIR } /models/*.onnx" )
3838
39+ # Exclude the base/template models used by specialize_models.py to generate
40+ # fixed-size variants (e.g. gnn_h32_k2_n100_e500.onnx). The base models
41+ # have symbolic dim_param input dimensions that SOFIE cannot resolve during
42+ # code generation:
43+ # • GetTensorShape() throws "unspecified dimension parameter" for input
44+ # tensors whose shapes were not concretised by ONNX shape inference.
45+ # • GetTensorShape() throws "is a dynamic tensor" when an operator tries
46+ # to read a concrete size from an intermediate tensor that remained
47+ # dynamic because its input shapes were unknown.
48+ # The concrete specialised variants produced by specialize_models.py are
49+ # benchmarked instead.
50+ list (FILTER BENCHMARK_ONNX_MODELS EXCLUDE REGEX
51+ "/(gnn_h32_k2|gnn_h64_k4|punet_h32_k2_heads4_layers2|punet_h64_k4_heads4_layers2|transformer_d32_h2_L6_ff32)\\ .onnx$" )
52+
53+ # Exclude models that exceed GPU memory (cudaErrorMemoryAllocation) or trigger
54+ # a cuBLAS EXECUTION_FAILED error (status 13) on the available hardware
55+ # (RTX 2070 SUPER, 8 GB VRAM). They stay on disk for reference but are not
56+ # included in the benchmark binary.
57+ list (FILTER BENCHMARK_ONNX_MODELS EXCLUDE REGEX
58+ "/(punet_h32_k2_heads4_layers2_n10000_e50000\
59+ |punet_h32_k2_heads4_layers2_n1000_e5000\
60+ |punet_h32_k2_heads4_layers2_n3000_e15000\
61+ |punet_h64_k4_heads4_layers2_n10000_e50000\
62+ |punet_h64_k4_heads4_layers2_n100_e500\
63+ |punet_h64_k4_heads4_layers2_n300_e1500\
64+ |punet_h64_k4_heads4_layers2_n3000_e15000\
65+ |transformer_L1000_B100\
66+ |transformer_L100_B100\
67+ |transformer_L8000_B1)\\ .onnx$" )
68+
3969if (NOT BENCHMARK_ONNX_MODELS)
4070 message (STATUS
4171 "SOFIE Benchmark: No .onnx models found in benchmark/models/. "
@@ -142,21 +172,13 @@ set(_EMIT_BLOCK
142172}\n\
143173" )
144174
145- set (_RUN_BLOCK
146- " Benchmark_@3@(warmup, iterations, weightsDir);\n\
147- " )
148-
149- # ORT call: passes the full ONNX path directly (no SOFIE weights needed)
150- set (_ORT_RUN_BLOCK
151- "#ifdef SOFIE_BENCHMARK_ORT\n\
152- if (run_ort) BenchmarkORT_GPU(\" @1@\" , \" @2@\" , warmup, iterations);\n\
153- #endif\n\
154- " )
155-
156- set (BENCHMARK_EMIT_CAPTURES "" )
157- set (BENCHMARK_BENCH_HEADERS "" )
158- set (BENCHMARK_RUN_CALLS "" )
159- set (GENERATED_HEADERS "" )
175+ set (BENCHMARK_EMIT_CAPTURES "" )
176+ set (BENCHMARK_BENCH_HEADERS "" )
177+ set (BENCHMARK_FWD_DECLS "" )
178+ set (BENCHMARK_SINGLE_MODEL_CASES "" )
179+ set (BENCHMARK_SPAWN_CALLS "" )
180+ set (GENERATED_HEADERS "" )
181+ set (BENCHMARK_MODEL_CU_SRCS "" )
160182
161183foreach (ONNX_FILE ${BENCHMARK_ONNX_MODELS} )
162184 get_filename_component (MODEL_NAME "${ONNX_FILE} " NAME_WE )
@@ -167,21 +189,52 @@ foreach(ONNX_FILE ${BENCHMARK_ONNX_MODELS})
167189 set (GEN_BENCH "${CMAKE_CURRENT_BINARY_DIR } /${MODEL_NAME} _bench.hxx" )
168190 list (APPEND GENERATED_HEADERS "${GEN_HXX} " "${GEN_BENCH} " )
169191
170- string (REPLACE "@1@" "${ONNX_FILE} " _emit_cap "${_EMIT_BLOCK} " )
171- string (REPLACE "@2@" "${MODEL_NAME} " _emit_cap "${_emit_cap} " )
192+ string (REPLACE "@1@" "${ONNX_FILE} " _emit_cap "${_EMIT_BLOCK} " )
193+ string (REPLACE "@2@" "${MODEL_NAME} " _emit_cap "${_emit_cap} " )
172194 string (APPEND BENCHMARK_EMIT_CAPTURES "${_emit_cap} " )
173195
174- # SOFIE Alpaka call
175- string (REPLACE "@3@" "${MODEL_CPPNAME} " _run_cap "${_RUN_BLOCK} " )
176- string (APPEND BENCHMARK_RUN_CALLS "${_run_cap} " )
177-
178- # ORT call (guarded by #ifdef at compile time + run_ort flag at runtime)
179- string (REPLACE "@1@" "${ONNX_FILE} " _ort_cap "${_ORT_RUN_BLOCK} " )
180- string (REPLACE "@2@" "${MODEL_NAME} " _ort_cap "${_ort_cap} " )
181- string (APPEND BENCHMARK_RUN_CALLS "${_ort_cap} " )
182-
183196 string (APPEND BENCHMARK_BENCH_HEADERS
184197 "#include \" ${MODEL_NAME} _bench.hxx\"\n " )
198+
199+ # Forward declaration for the main TU (function defined in per-model .cu)
200+ string (APPEND BENCHMARK_FWD_DECLS
201+ "void Benchmark_${MODEL_CPPNAME} (int warmup, int iterations, const std::string& weightsDir);\n " )
202+
203+ # Single-model dispatch: one if-branch per model (used in --single-model mode)
204+ string (APPEND BENCHMARK_SINGLE_MODEL_CASES
205+ " if (model == \" ${MODEL_CPPNAME} \" ) {\n "
206+ " Benchmark_${MODEL_CPPNAME} (warmup, iterations, weightsDir);\n "
207+ "#ifdef SOFIE_BENCHMARK_ORT\n "
208+ " if (run_ort) BenchmarkORT_GPU(\" ${ONNX_FILE} \" , \" ${MODEL_NAME} \" , warmup, iterations);\n "
209+ "#endif\n "
210+ " return 0;\n "
211+ " }\n "
212+ )
213+
214+ # Subprocess spawn: run this binary as a child with --single-model <name>.
215+ # Each child gets a fresh CUDA context so GPU memory from the previous
216+ # model is completely freed before the next one allocates.
217+ string (APPEND BENCHMARK_SPAWN_CALLS
218+ " {\n "
219+ " std::string cmd = std::string(argv[0])\n "
220+ " + \" --single-model ${MODEL_CPPNAME} \"\n "
221+ " + commonArgs;\n "
222+ " int rc = std::system(cmd.c_str());\n "
223+ " if (rc != 0) {\n "
224+ " std::fprintf(stderr, \" [ERROR] ${MODEL_NAME} : subprocess exited %d\\ n\" , rc);\n "
225+ " ++totalFailed;\n "
226+ " }\n "
227+ " }\n "
228+ )
229+
230+ # Per-model compilation unit: include one bench.hxx → one .cu file
231+ set (_model_cu "${CMAKE_CURRENT_BINARY_DIR } /${MODEL_NAME} _bench.cu" )
232+ configure_file (
233+ "${CMAKE_CURRENT_SOURCE_DIR } /src/ModelBench.cu.in"
234+ "${_model_cu} "
235+ @ONLY
236+ )
237+ list (APPEND BENCHMARK_MODEL_CU_SRCS "${_model_cu} " )
185238endforeach ()
186239
187240################################################################################
@@ -194,7 +247,9 @@ configure_file(
194247 @ONLY
195248)
196249
197- set (RUNNER_SRC "${CMAKE_CURRENT_BINARY_DIR } /BenchmarkRunner_all.cu" )
250+ # Main runner: only main() + forward declarations (no model headers).
251+ # Each model is compiled in its own _bench.cu TU (see BENCHMARK_MODEL_CU_SRCS).
252+ set (RUNNER_SRC "${CMAKE_CURRENT_BINARY_DIR } /BenchmarkRunner_main.cpp" )
198253configure_file (
199254 "${CMAKE_CURRENT_SOURCE_DIR } /src/BenchmarkRunner.cxx.in"
200255 "${RUNNER_SRC} "
@@ -247,9 +302,13 @@ add_custom_target(sofie_benchmark_headers
247302# Benchmark runner (compiled as .cu, same as the test suite)
248303################################################################################
249304
250- set_source_files_properties ("${RUNNER_SRC} " PROPERTIES LANGUAGE CUDA)
305+ # Mark every per-model bench.cu as CUDA so nvcc processes the device kernels.
306+ set_source_files_properties (${BENCHMARK_MODEL_CU_SRCS} PROPERTIES LANGUAGE CUDA)
251307
252- add_executable (sofie_benchmark "${RUNNER_SRC} " )
308+ # The main runner is plain C++ (no CUDA kernels, just calls forward-declared fns).
309+ set_source_files_properties ("${RUNNER_SRC} " PROPERTIES LANGUAGE CXX)
310+
311+ add_executable (sofie_benchmark "${RUNNER_SRC} " ${BENCHMARK_MODEL_CU_SRCS} )
253312
254313add_dependencies (sofie_benchmark sofie_benchmark_headers )
255314
@@ -261,9 +320,22 @@ target_include_directories(sofie_benchmark PRIVATE
261320 "${CUDAToolkit_INCLUDE_DIRS} "
262321)
263322
323+ # Default to the native GPU architecture so we compile only one arch (saves
324+ # memory and time). The previous "70;75;80;86;89;90" multi-arch list caused
325+ # nvcc to OOM-kill when processing all models. Override via
326+ # cmake -DSOFIE_BENCHMARK_CUDA_ARCH="75;86" .
327+ if (NOT DEFINED SOFIE_BENCHMARK_CUDA_ARCH OR SOFIE_BENCHMARK_CUDA_ARCH STREQUAL "" )
328+ if (CMAKE_CUDA_ARCHITECTURES AND NOT CMAKE_CUDA_ARCHITECTURES STREQUAL "" )
329+ set (SOFIE_BENCHMARK_CUDA_ARCH "${CMAKE_CUDA_ARCHITECTURES} " )
330+ else ()
331+ set (SOFIE_BENCHMARK_CUDA_ARCH "75" ) # RTX 2070 SUPER default
332+ endif ()
333+ endif ()
334+ message (STATUS "SOFIE Benchmark: CUDA architectures = ${SOFIE_BENCHMARK_CUDA_ARCH} " )
335+
264336set_target_properties (sofie_benchmark PROPERTIES
265337 CUDA_SEPARABLE_COMPILATION OFF
266- CUDA_ARCHITECTURES "70;75;80;86;89;90 "
338+ CUDA_ARCHITECTURES "${SOFIE_BENCHMARK_CUDA_ARCH} "
267339 CUDA_STANDARD 20
268340 CUDA_STANDARD_REQUIRED ON
269341 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR } "
@@ -280,8 +352,17 @@ target_compile_options(sofie_benchmark PRIVATE
280352 --extended -lambda
281353 --expt -relaxed -constexpr
282354 --use_fast_math
283- -O2
355+ -O1
284356 -Wno -deprecated -gpu -targets
357+ # Suppress "variable was declared but never referenced " (#177-D ).
358+ # Generated Expand /Where kernels compute per -dimension indices for
359+ # broadcast dimensions ; when a dimension has size 1 the index variable
360+ # is always 0 and goes unused . Without this suppress the CUDA device
361+ # compiler counts them against its error -budget and stops compilation .
362+ -diag -suppress 177
363+ # Limit device register usage to reduce per -kernel memory footprint
364+ # during compilation of the large all -models TU .
365+ --maxrregcount =64
285366 >
286367 $<$<COMPILE_LANGUAGE :CXX >:
287368 -O2
0 commit comments