From c270eaf06c85b25f075a8966ab18b57b8dd0cb81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Fri, 11 Sep 2026 17:32:23 -0500 Subject: [PATCH 1/2] Benchmark kernel launch overhead and partitioning The benchmark suite only measured saxpy throughput, so changes to the host side of a launch went unnoticed: a warm CPU launch allocated about 50 times and the per-PR benchmark comment could not show it. Add a `launch` group timing a single-workgroup launch for the static and dynamic workgroup-size and ndrange combinations in one and three dimensions, with the kernel constructed in the setup and the backend synchronized in the teardown so that only the launch is measured, and a `partition` group timing the host-side partitioning of the iteration space on its own. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01VHciC8x39gm97sABrSvBkt --- benchmark/benchmarks.jl | 69 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index b153f60ee..2ab88641d 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -65,3 +65,72 @@ let default = BenchmarkGroup() end SUITE["saxpy"]["default"] = default end + +# Launch overhead: a problem of a single workgroup, so that the time is dominated by the +# host-side work of a launch. The kernel is constructed in the setup and the backend +# synchronized in the teardown, so only the launch itself is measured. +@kernel function scale_kernel!(A, @Const(B)) + I = @index(Global) + @inbounds A[I] = 2 * B[I] +end + +@kernel function scale_kernel_3d!(A, @Const(B)) + i, j, k = @index(Global, NTuple) + @inbounds A[i, j, k] = 2 * B[i, j, k] +end + +SUITE["launch"] = BenchmarkGroup() + +let launch = BenchmarkGroup(), n = 16, dims = (4, 4, 4) + launch["dynamic workgroup, dynamic ndrange"] = @benchmarkable kernel(A, B, ndrange = $n) setup = ( + kernel = scale_kernel!($BACKEND); + A = KernelAbstractions.zeros($BACKEND, Float64, $n); + B = rand!(KernelAbstractions.zeros($BACKEND, Float64, $n)) + ) teardown = KernelAbstractions.synchronize($BACKEND) + + launch["dynamic workgroup, dynamic ndrange, workgroupsize given"] = @benchmarkable kernel(A, B, ndrange = $n, workgroupsize = $n) setup = ( + kernel = scale_kernel!($BACKEND); + A = KernelAbstractions.zeros($BACKEND, Float64, $n); + B = rand!(KernelAbstractions.zeros($BACKEND, Float64, $n)) + ) teardown = KernelAbstractions.synchronize($BACKEND) + + launch["static workgroup, dynamic ndrange"] = @benchmarkable kernel(A, B, ndrange = $n) setup = ( + kernel = scale_kernel!($BACKEND, $n); + A = KernelAbstractions.zeros($BACKEND, Float64, $n); + B = rand!(KernelAbstractions.zeros($BACKEND, Float64, $n)) + ) teardown = KernelAbstractions.synchronize($BACKEND) + + launch["static workgroup, static ndrange"] = @benchmarkable kernel(A, B) setup = ( + kernel = scale_kernel!($BACKEND, $n, $n); + A = KernelAbstractions.zeros($BACKEND, Float64, $n); + B = rand!(KernelAbstractions.zeros($BACKEND, Float64, $n)) + ) teardown = KernelAbstractions.synchronize($BACKEND) + + launch["3D static workgroup, dynamic ndrange"] = @benchmarkable kernel(A, B, ndrange = $dims) setup = ( + kernel = scale_kernel_3d!($BACKEND, $dims); + A = KernelAbstractions.zeros($BACKEND, Float64, $dims...); + B = rand!(KernelAbstractions.zeros($BACKEND, Float64, $dims...)) + ) teardown = KernelAbstractions.synchronize($BACKEND) + + launch["3D static workgroup, static ndrange"] = @benchmarkable kernel(A, B) setup = ( + kernel = scale_kernel_3d!($BACKEND, $dims, $dims); + A = KernelAbstractions.zeros($BACKEND, Float64, $dims...); + B = rand!(KernelAbstractions.zeros($BACKEND, Float64, $dims...)) + ) teardown = KernelAbstractions.synchronize($BACKEND) + + SUITE["launch"] = launch +end + +# Host-side partitioning of the iteration space, independent of the backend. +let partition = BenchmarkGroup(), ndrange = Ref((1024,)), workgroupsize = Ref((16,)) + partition["dynamic workgroup, dynamic ndrange"] = @benchmarkable KernelAbstractions.partition(kernel, $ndrange[], $workgroupsize[]) setup = ( + kernel = scale_kernel!($BACKEND) + ) + partition["static workgroup, dynamic ndrange"] = @benchmarkable KernelAbstractions.partition(kernel, $ndrange[], nothing) setup = ( + kernel = scale_kernel!($BACKEND, 16) + ) + partition["static workgroup, static ndrange"] = @benchmarkable KernelAbstractions.partition(kernel, nothing, nothing) setup = ( + kernel = scale_kernel!($BACKEND, 16, 1024) + ) + SUITE["partition"] = partition +end From 00c47a3b8ad3e408a4f5da94240c0fde52952f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Fri, 11 Sep 2026 17:37:56 -0500 Subject: [PATCH 2/2] Report allocations in the PR benchmark comment The launch benchmarks measure a launch that the CPU backend executes synchronously, so their time is dominated by the OpenCL runtime and has a spread of about 30%, while the host-side work of a launch shows up as allocations: a regression there changes the count by tens of allocations per launch and is invisible in the time table. `benchpkgtable` can print the allocation and memory table next to the time table. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01VHciC8x39gm97sABrSvBkt --- .github/workflows/benchmark_pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark_pr.yml b/.github/workflows/benchmark_pr.yml index e7c68410e..3ce87582c 100644 --- a/.github/workflows/benchmark_pr.yml +++ b/.github/workflows/benchmark_pr.yml @@ -49,7 +49,7 @@ jobs: path: plots - name: Create markdown table from benchmarks run: | - benchpkgtable ${{ steps.extract-package-name.outputs.package_name }} --rev="${{github.event.repository.default_branch}},${{github.event.pull_request.head.sha}}" --input-dir=results/ --ratio > table.md + benchpkgtable ${{ steps.extract-package-name.outputs.package_name }} --rev="${{github.event.repository.default_branch}},${{github.event.pull_request.head.sha}}" --input-dir=results/ --ratio --mode=time,memory > table.md echo '### Benchmark Results' > body.md echo '' >> body.md echo '
' >> body.md