Skip to content

Less compilation for a kernel's first launch - #931

Merged
maleadt merged 4 commits into
mainfrom
tb/launch-cost-main
Oct 28, 2026
Merged

maleadt merged 4 commits into
mainfrom
tb/launch-cost-main

Conversation

@maleadt

@maleadt maleadt commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Four launch-cost improvements from the stack on #906 that also help main, where launches go through PoCL's queue (2 µs on a Ryzen 9950X, 6 µs on a Xeon E3). Changes that only save a few cycles per launch stay with #906.

  • Share size-only launch code. Strip singleton functions (including @kernel functions) before launch_setup, select_launch and launch_geometry, so that their code is shared by kernels with the same backend and static sizes. Stateful callables keep their type for custom hooks. Applies to every backend.
  • Compile PoCL argument generation once. The launch_converted generator uses vectors instead of tuples, so it isn't recompiled for each argument count.
  • Infer the kernel's type on backends other than PoCL. Since POCL: vectorizable kernels without bounds checks, and FMA for muladd #922, kernel lookups hid the kernel's type from inference so PoCL could share lookup code between checked and unchecked kernels. Restore inference on CUDA, AMDGPU, oneAPI, Metal and OpenCL to avoid dynamic dispatch on every launch.
  • Precompile a SPIR-V builtin call. The precompile kernel uses fma, which precompiles LLVM.jl's llvmcall generators.

First launches, ms, median of 3 interleaved runs (Ryzen 9950X, Julia 1.12, -t 4):

main this PR
session's first launch (axpy) 478 299
new kernel, shape launched before (1-d) 61 50
new kernel, shape launched before (3-d, partial work-groups) 68 52
new kernel with a new argument count (3-8) 132 93
CUDA (RTX 5080): new kernel, 1-d / 3-d 81 / 98 76 / 83

Oceananigans hydrostatic model (36x18x10, -t 4,0, median of 3): build 18.43 → 18.27 s, first time step 21.72 → 21.47 s.

Per-launch host cost on GPUs before and after inferring the kernel's type, ns, best of 200 batches of 64 launches, median of 6 interleaved runs (Julia 1.12):

before after raw launch
CUDA (RTX 5080), 1-d / 3-d 1749 / 1838 1598 / 1635 1468
AMDGPU (gfx1036), 1-d / 3-d 1364 / 1351 1121 / 1124 727

@maleadt maleadt added the latency Compile time, load time, time-to-first-execution, invalidations. label Oct 27, 2026
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Benchmark Results

Show table
main a2dd21b... main / a2dd21b...
const/@Const/Float32/262144 0.314 ± 0.018 ms 0.304 ± 0.015 ms 1.03 ± 0.078
const/@Const/Float32/65536 0.117 ± 0.007 ms 0.115 ± 0.01 ms 1.02 ± 0.11
const/@Const/Float64/262144 0.566 ± 0.022 ms 0.556 ± 0.02 ms 1.02 ± 0.053
const/@Const/Float64/65536 0.186 ± 0.0096 ms 0.181 ± 0.0096 ms 1.03 ± 0.076
const/unmarked/Float32/262144 0.438 ± 0.022 ms 0.537 ± 0.021 ms 0.816 ± 0.051
const/unmarked/Float32/65536 0.146 ± 0.004 ms 0.153 ± 0.014 ms 0.954 ± 0.089
const/unmarked/Float64/262144 0.827 ± 0.016 ms 0.832 ± 0.013 ms 0.994 ± 0.024
const/unmarked/Float64/65536 0.248 ± 0.015 ms 0.241 ± 0.014 ms 1.03 ± 0.085
dgsem/2D/Float32/fluxdiff/element 0.523 ± 0.022 ms 0.538 ± 0.033 ms 0.972 ± 0.072
dgsem/2D/Float32/fluxdiff/node E=8 1.47 ± 0.035 ms 1.52 ± 0.027 ms 0.97 ± 0.029
dgsem/2D/Float32/fluxdiff/node halfsweep E=8 0.567 ± 0.024 ms 0.566 ± 0.024 ms 1 ± 0.059
dgsem/2D/Float32/fluxdiff/node halfsweep v-last E=8 0.586 ± 0.034 ms 0.582 ± 0.013 ms 1.01 ± 0.063
dgsem/2D/Float32/weak/element 0.195 ± 0.02 ms 0.198 ± 0.013 ms 0.987 ± 0.12
dgsem/2D/Float32/weak/element private 0.352 ± 0.017 ms 0.35 ± 0.012 ms 1.01 ± 0.06
dgsem/2D/Float32/weak/node localmem E=1 0.168 ± 0.021 ms 0.187 ± 0.013 ms 0.898 ± 0.13
dgsem/2D/Float32/weak/node localmem E=8 0.161 ± 0.0032 ms 0.162 ± 0.02 ms 0.992 ± 0.13
dgsem/2D/Float32/weak/node localmem v-last E=1 0.252 ± 0.022 ms 0.237 ± 0.017 ms 1.06 ± 0.12
dgsem/2D/Float32/weak/node localmem v-last E=8 0.233 ± 0.02 ms 0.232 ± 0.017 ms 1 ± 0.11
dgsem/2D/Float32/weak/node recompute E=8 0.467 ± 0.019 ms 0.458 ± 0.018 ms 1.02 ± 0.058
dgsem/2D/Float64/fluxdiff/element 0.539 ± 0.022 ms 0.562 ± 0.022 ms 0.959 ± 0.054
dgsem/2D/Float64/fluxdiff/node E=8 1.71 ± 0.0088 ms 1.79 ± 0.033 ms 0.954 ± 0.018
dgsem/2D/Float64/fluxdiff/node halfsweep E=8 0.564 ± 0.017 ms 0.564 ± 0.024 ms 1 ± 0.052
dgsem/2D/Float64/fluxdiff/node halfsweep v-last E=8 0.629 ± 0.015 ms 0.62 ± 0.024 ms 1.02 ± 0.047
dgsem/2D/Float64/weak/element 0.206 ± 0.022 ms 0.228 ± 0.021 ms 0.901 ± 0.13
dgsem/2D/Float64/weak/element private 0.27 ± 0.013 ms 0.27 ± 0.014 ms 1 ± 0.069
dgsem/2D/Float64/weak/node localmem E=1 0.174 ± 0.024 ms 0.196 ± 0.023 ms 0.889 ± 0.16
dgsem/2D/Float64/weak/node localmem E=8 0.168 ± 0.02 ms 0.169 ± 0.015 ms 0.995 ± 0.15
dgsem/2D/Float64/weak/node localmem v-last E=1 0.255 ± 0.022 ms 0.257 ± 0.021 ms 0.991 ± 0.12
dgsem/2D/Float64/weak/node localmem v-last E=8 0.235 ± 0.013 ms 0.237 ± 0.021 ms 0.994 ± 0.1
dgsem/2D/Float64/weak/node recompute E=8 0.474 ± 0.013 ms 0.482 ± 0.015 ms 0.984 ± 0.041
dgsem/3D/Float32/fluxdiff/element 1.81 ± 0.027 ms 1.79 ± 0.0096 ms 1.01 ± 0.016
dgsem/3D/Float32/fluxdiff/node E=2 4.35 ± 0.019 ms 4.32 ± 0.026 ms 1.01 ± 0.0074
dgsem/3D/Float32/fluxdiff/node halfsweep E=1 1.69 ± 0.035 ms 1.69 ± 0.016 ms 1 ± 0.023
dgsem/3D/Float32/fluxdiff/node halfsweep v-last E=1 2 ± 0.0068 ms 1.97 ± 0.0074 ms 1.01 ± 0.0052
dgsem/3D/Float32/weak/element 0.638 ± 0.018 ms 0.656 ± 0.021 ms 0.971 ± 0.041
dgsem/3D/Float32/weak/element private 0.698 ± 0.015 ms 0.697 ± 0.016 ms 1 ± 0.032
dgsem/3D/Float32/weak/node localmem E=1 0.608 ± 0.02 ms 0.618 ± 0.018 ms 0.984 ± 0.043
dgsem/3D/Float32/weak/node localmem E=2 0.59 ± 0.028 ms 0.586 ± 0.017 ms 1.01 ± 0.056
dgsem/3D/Float32/weak/node localmem v-last E=1 0.831 ± 0.017 ms 0.822 ± 0.0074 ms 1.01 ± 0.023
dgsem/3D/Float32/weak/node localmem v-last E=2 0.907 ± 0.0064 ms 0.919 ± 0.0073 ms 0.986 ± 0.01
dgsem/3D/Float32/weak/node recompute E=2 1.93 ± 0.0071 ms 1.93 ± 0.008 ms 1 ± 0.0055
dgsem/3D/Float32/weak/slab E=1 0.83 ± 0.0094 ms 0.827 ± 0.013 ms 1 ± 0.019
dgsem/3D/Float32/weak/slab E=8 1.01 ± 0.017 ms 1.03 ± 0.0068 ms 0.981 ± 0.018
dgsem/3D/Float32/weak/slab double-buffered E=1 0.959 ± 0.032 ms 0.943 ± 0.0083 ms 1.02 ± 0.035
dgsem/3D/Float32/weak/slab double-buffered E=8 1.17 ± 0.023 ms 1.11 ± 0.018 ms 1.06 ± 0.026
dgsem/3D/Float32/weak/slab double-buffered v-last E=1 0.799 ± 0.023 ms 0.776 ± 0.016 ms 1.03 ± 0.037
dgsem/3D/Float32/weak/slab double-buffered v-last E=8 1.07 ± 0.016 ms 1.03 ± 0.0091 ms 1.04 ± 0.018
dgsem/3D/Float32/weak/slab v-last E=1 0.852 ± 0.027 ms 0.84 ± 0.018 ms 1.01 ± 0.039
dgsem/3D/Float32/weak/slab v-last E=8 1.06 ± 0.016 ms 1.04 ± 0.0098 ms 1.02 ± 0.018
dgsem/3D/Float64/fluxdiff/element 1.86 ± 0.024 ms 1.87 ± 0.017 ms 0.994 ± 0.016
dgsem/3D/Float64/fluxdiff/node E=2 8.05 ± 0.05 ms 8.05 ± 0.027 ms 1 ± 0.0071
dgsem/3D/Float64/fluxdiff/node halfsweep E=1 1.78 ± 0.021 ms 1.76 ± 0.016 ms 1.01 ± 0.015
dgsem/3D/Float64/fluxdiff/node halfsweep v-last E=1 2.1 ± 0.023 ms 2.09 ± 0.014 ms 1 ± 0.013
dgsem/3D/Float64/weak/element 0.701 ± 0.014 ms 0.705 ± 0.022 ms 0.994 ± 0.037
dgsem/3D/Float64/weak/element private 0.723 ± 0.024 ms 0.742 ± 0.024 ms 0.975 ± 0.045
dgsem/3D/Float64/weak/node localmem E=1 0.665 ± 0.014 ms 0.649 ± 0.02 ms 1.02 ± 0.038
dgsem/3D/Float64/weak/node localmem E=2 0.614 ± 0.021 ms 0.628 ± 0.026 ms 0.978 ± 0.053
dgsem/3D/Float64/weak/node localmem v-last E=1 0.827 ± 0.01 ms 0.825 ± 0.011 ms 1 ± 0.018
dgsem/3D/Float64/weak/node localmem v-last E=2 0.945 ± 0.009 ms 0.959 ± 0.014 ms 0.986 ± 0.017
dgsem/3D/Float64/weak/node recompute E=2 1.99 ± 0.015 ms 1.98 ± 0.0067 ms 1 ± 0.0084
dgsem/3D/Float64/weak/slab E=1 0.902 ± 0.005 ms 0.898 ± 0.022 ms 1 ± 0.025
dgsem/3D/Float64/weak/slab E=8 1.67 ± 0.018 ms 1.7 ± 0.014 ms 0.987 ± 0.013
dgsem/3D/Float64/weak/slab double-buffered E=1 0.873 ± 0.019 ms 0.885 ± 0.014 ms 0.986 ± 0.026
dgsem/3D/Float64/weak/slab double-buffered E=8 1.62 ± 0.02 ms 1.65 ± 0.013 ms 0.98 ± 0.014
dgsem/3D/Float64/weak/slab double-buffered v-last E=1 0.818 ± 0.026 ms 0.831 ± 0.0085 ms 0.984 ± 0.033
dgsem/3D/Float64/weak/slab double-buffered v-last E=8 1.55 ± 0.022 ms 1.57 ± 0.017 ms 0.984 ± 0.018
dgsem/3D/Float64/weak/slab v-last E=1 0.887 ± 0.016 ms 0.942 ± 0.0055 ms 0.942 ± 0.018
dgsem/3D/Float64/weak/slab v-last E=8 1.58 ± 0.013 ms 1.64 ± 0.019 ms 0.964 ± 0.013
launch/3D static workgroup, dynamic ndrange 10.7 ± 0.51 μs 10.7 ± 0.51 μs 0.999 ± 0.067
launch/3D static workgroup, static ndrange 10.6 ± 0.48 μs 10.6 ± 0.5 μs 1 ± 0.066
launch/dynamic workgroup, dynamic ndrange 11.7 ± 8.7 μs 12.1 ± 3.8 μs 0.962 ± 0.78
launch/dynamic workgroup, dynamic ndrange, workgroupsize given 16.2 ± 0.63 μs 14.5 ± 3.6 μs 1.12 ± 0.28
launch/static workgroup, dynamic ndrange 13.8 ± 0.19 μs 10.7 ± 0.31 μs 1.29 ± 0.041
launch/static workgroup, static ndrange 10.6 ± 0.47 μs 11.6 ± 4.3 μs 0.914 ± 0.34
narrow workgroup/masked/W=16 0.518 ± 0.02 ms 0.427 ± 0.019 ms 1.21 ± 0.07
narrow workgroup/masked/W=4 0.489 ± 0.019 ms 0.426 ± 0.017 ms 1.15 ± 0.063
narrow workgroup/unmasked/W=16 0.528 ± 0.02 ms 0.436 ± 0.021 ms 1.21 ± 0.074
narrow workgroup/unmasked/W=4 0.593 ± 0.019 ms 0.431 ± 0.019 ms 1.38 ± 0.075
partition/dynamic workgroup, dynamic ndrange 0.0591 ± 0.011 μs 0.0592 ± 0.0033 μs 0.999 ± 0.2
partition/static workgroup, dynamic ndrange 0.0639 ± 0.012 μs 0.0642 ± 0.012 μs 0.996 ± 0.26
partition/static workgroup, static ndrange 1.55 ± 0 ns 1.55 ± 0 ns 1 ± 0
saxpy/default/Float16/1024 9.47 ± 0.48 μs 9.54 ± 0.45 μs 0.993 ± 0.069
saxpy/default/Float16/1048576 0.206 ± 0.017 ms 0.216 ± 0.02 ms 0.957 ± 0.12
saxpy/default/Float16/16384 17.8 ± 6.7 μs 23.9 ± 1 μs 0.744 ± 0.28
saxpy/default/Float16/2048 15.1 ± 1.1 μs 14.7 ± 8.4 μs 1.02 ± 0.59
saxpy/default/Float16/256 11.1 ± 4.1 μs 15.2 ± 0.45 μs 0.727 ± 0.27
saxpy/default/Float16/262144 0.0894 ± 0.046 ms 0.0997 ± 0.014 ms 0.897 ± 0.48
saxpy/default/Float16/32768 20.1 ± 0.77 μs 20.9 ± 7.9 μs 0.962 ± 0.37
saxpy/default/Float16/4096 15.6 ± 7 μs 14.6 ± 2.5 μs 1.07 ± 0.51
saxpy/default/Float16/512 15 ± 0.47 μs 14.6 ± 2.9 μs 1.03 ± 0.21
saxpy/default/Float16/64 11.2 ± 0.51 μs 16.3 ± 1.9 μs 0.684 ± 0.087
saxpy/default/Float16/65536 26.3 ± 10 μs 26.9 ± 1.9 μs 0.978 ± 0.38
saxpy/default/Float32/1024 14.3 ± 7.7 μs 15 ± 4.6 μs 0.953 ± 0.59
saxpy/default/Float32/1048576 0.26 ± 0.042 ms 0.283 ± 0.039 ms 0.917 ± 0.2
saxpy/default/Float32/16384 19.4 ± 5.9 μs 20.3 ± 4.9 μs 0.956 ± 0.37
saxpy/default/Float32/2048 14.9 ± 7.6 μs 13.6 ± 1.2 μs 1.1 ± 0.57
saxpy/default/Float32/256 11 ± 0.29 μs 11.2 ± 0.58 μs 0.981 ± 0.057
saxpy/default/Float32/262144 0.104 ± 0.013 ms 0.105 ± 0.012 ms 0.992 ± 0.17
saxpy/default/Float32/32768 25.6 ± 5 μs 25.6 ± 6.1 μs 1 ± 0.31
saxpy/default/Float32/4096 15.3 ± 0.57 μs 16 ± 7.5 μs 0.958 ± 0.45
saxpy/default/Float32/512 9.75 ± 0.38 μs 10 ± 0.8 μs 0.974 ± 0.087
saxpy/default/Float32/64 11.2 ± 0.18 μs 11.3 ± 4.7 μs 0.988 ± 0.41
saxpy/default/Float32/65536 31 ± 4.5 μs 0.0331 ± 0.0047 ms 0.937 ± 0.19
saxpy/default/Float64/1024 15.2 ± 1.2 μs 15.3 ± 7.5 μs 0.994 ± 0.49
saxpy/default/Float64/1048576 0.549 ± 0.046 ms 0.684 ± 0.13 ms 0.802 ± 0.16
saxpy/default/Float64/16384 25.3 ± 5.8 μs 25.3 ± 4.8 μs 1 ± 0.3
saxpy/default/Float64/2048 15.7 ± 6.6 μs 16.2 ± 6.4 μs 0.968 ± 0.56
saxpy/default/Float64/256 14.8 ± 3.1 μs 12.6 ± 4.6 μs 1.18 ± 0.5
saxpy/default/Float64/262144 0.169 ± 0.016 ms 0.166 ± 0.022 ms 1.02 ± 0.17
saxpy/default/Float64/32768 0.0337 ± 0.0071 ms 0.0345 ± 0.0055 ms 0.978 ± 0.26
saxpy/default/Float64/4096 17 ± 5.3 μs 21.7 ± 5.4 μs 0.781 ± 0.31
saxpy/default/Float64/512 13.3 ± 4.6 μs 21 ± 1.1 μs 0.635 ± 0.22
saxpy/default/Float64/64 15.2 ± 0.38 μs 11.8 ± 4.7 μs 1.29 ± 0.51
saxpy/default/Float64/65536 0.0474 ± 0.0068 ms 0.0499 ± 0.0071 ms 0.951 ± 0.19
saxpy/static workgroup=(1024,)/Float16/1024 10 ± 4.5 μs 13.6 ± 2.7 μs 0.736 ± 0.36
saxpy/static workgroup=(1024,)/Float16/1048576 0.217 ± 0.019 ms 0.215 ± 0.019 ms 1.01 ± 0.12
saxpy/static workgroup=(1024,)/Float16/16384 23.8 ± 6.8 μs 17.2 ± 0.55 μs 1.38 ± 0.4
saxpy/static workgroup=(1024,)/Float16/2048 15.1 ± 2.4 μs 13.7 ± 4 μs 1.11 ± 0.37
saxpy/static workgroup=(1024,)/Float16/256 10.4 ± 2.9 μs 11.9 ± 4.3 μs 0.877 ± 0.4
saxpy/static workgroup=(1024,)/Float16/262144 0.0947 ± 0.036 ms 0.0944 ± 0.014 ms 1 ± 0.41
saxpy/static workgroup=(1024,)/Float16/32768 28.1 ± 8.3 μs 27.9 ± 7.7 μs 1.01 ± 0.41
saxpy/static workgroup=(1024,)/Float16/4096 15.8 ± 2.2 μs 15.8 ± 2.4 μs 1 ± 0.2
saxpy/static workgroup=(1024,)/Float16/512 16.3 ± 0.51 μs 11.9 ± 0.59 μs 1.37 ± 0.081
saxpy/static workgroup=(1024,)/Float16/64 11.7 ± 4.4 μs 11.6 ± 0.39 μs 1.01 ± 0.38
saxpy/static workgroup=(1024,)/Float16/65536 0.0351 ± 0.01 ms 26.3 ± 4 μs 1.34 ± 0.43
saxpy/static workgroup=(1024,)/Float32/1024 14.4 ± 7.9 μs 21 ± 1.1 μs 0.688 ± 0.38
saxpy/static workgroup=(1024,)/Float32/1048576 0.264 ± 0.042 ms 0.274 ± 0.036 ms 0.965 ± 0.2
saxpy/static workgroup=(1024,)/Float32/16384 19.7 ± 5 μs 19.4 ± 1.1 μs 1.01 ± 0.26
saxpy/static workgroup=(1024,)/Float32/2048 13.6 ± 1.3 μs 13.7 ± 1.5 μs 0.996 ± 0.15
saxpy/static workgroup=(1024,)/Float32/256 16.6 ± 0.57 μs 16.1 ± 0.51 μs 1.03 ± 0.048
saxpy/static workgroup=(1024,)/Float32/262144 0.106 ± 0.013 ms 0.103 ± 0.011 ms 1.02 ± 0.17
saxpy/static workgroup=(1024,)/Float32/32768 22.5 ± 2.3 μs 23.3 ± 2.1 μs 0.964 ± 0.13
saxpy/static workgroup=(1024,)/Float32/4096 22.7 ± 0.84 μs 22.5 ± 0.81 μs 1.01 ± 0.052
saxpy/static workgroup=(1024,)/Float32/512 10.4 ± 0.47 μs 14.8 ± 4.6 μs 0.704 ± 0.22
saxpy/static workgroup=(1024,)/Float32/64 12 ± 4.5 μs 11.9 ± 0.32 μs 1.01 ± 0.38
saxpy/static workgroup=(1024,)/Float32/65536 30.6 ± 5.1 μs 0.0345 ± 0.0049 ms 0.888 ± 0.19
saxpy/static workgroup=(1024,)/Float64/1024 15.6 ± 2.8 μs 15.9 ± 4.6 μs 0.981 ± 0.33
saxpy/static workgroup=(1024,)/Float64/1048576 0.541 ± 0.053 ms 0.606 ± 0.089 ms 0.893 ± 0.16
saxpy/static workgroup=(1024,)/Float64/16384 23 ± 2.6 μs 28.6 ± 4.7 μs 0.804 ± 0.16
saxpy/static workgroup=(1024,)/Float64/2048 21.7 ± 1.2 μs 16 ± 2.2 μs 1.36 ± 0.2
saxpy/static workgroup=(1024,)/Float64/256 10.4 ± 0.53 μs 15.2 ± 0.7 μs 0.688 ± 0.047
saxpy/static workgroup=(1024,)/Float64/262144 0.169 ± 0.018 ms 0.169 ± 0.023 ms 1 ± 0.17
saxpy/static workgroup=(1024,)/Float64/32768 0.0343 ± 0.0076 ms 0.0341 ± 0.0069 ms 1.01 ± 0.3
saxpy/static workgroup=(1024,)/Float64/4096 16.8 ± 0.54 μs 17.5 ± 5.7 μs 0.959 ± 0.31
saxpy/static workgroup=(1024,)/Float64/512 9.98 ± 4.3 μs 13.2 ± 4.6 μs 0.753 ± 0.42
saxpy/static workgroup=(1024,)/Float64/64 11.6 ± 0.22 μs 17.4 ± 0.23 μs 0.67 ± 0.015
saxpy/static workgroup=(1024,)/Float64/65536 0.0481 ± 0.0075 ms 0.052 ± 0.007 ms 0.926 ± 0.19
time_to_load 0.542 ± 0.012 s 0.55 ± 0.0095 s 0.985 ± 0.028
main a2dd21b... main / a2dd21b...
const/@Const/Float32/262144 0 allocs: 0 B 0 allocs: 0 B
const/@Const/Float32/65536 0 allocs: 0 B 0 allocs: 0 B
const/@Const/Float64/262144 0 allocs: 0 B 0 allocs: 0 B
const/@Const/Float64/65536 0 allocs: 0 B 0 allocs: 0 B
const/unmarked/Float32/262144 0 allocs: 0 B 0 allocs: 0 B
const/unmarked/Float32/65536 0 allocs: 0 B 0 allocs: 0 B
const/unmarked/Float64/262144 0 allocs: 0 B 0 allocs: 0 B
const/unmarked/Float64/65536 0 allocs: 0 B 0 allocs: 0 B
dgsem/2D/Float32/fluxdiff/element 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/2D/Float32/fluxdiff/node E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float32/fluxdiff/node halfsweep E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float32/fluxdiff/node halfsweep v-last E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float32/weak/element 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/2D/Float32/weak/element private 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/2D/Float32/weak/node localmem E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float32/weak/node localmem E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float32/weak/node localmem v-last E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float32/weak/node localmem v-last E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float32/weak/node recompute E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float64/fluxdiff/element 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/2D/Float64/fluxdiff/node E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float64/fluxdiff/node halfsweep E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float64/fluxdiff/node halfsweep v-last E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float64/weak/element 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/2D/Float64/weak/element private 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/2D/Float64/weak/node localmem E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float64/weak/node localmem E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float64/weak/node localmem v-last E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float64/weak/node localmem v-last E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/2D/Float64/weak/node recompute E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float32/fluxdiff/element 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/3D/Float32/fluxdiff/node E=2 4 allocs: 0.188 kB 4 allocs: 0.188 kB 1
dgsem/3D/Float32/fluxdiff/node halfsweep E=1 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
dgsem/3D/Float32/fluxdiff/node halfsweep v-last E=1 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
dgsem/3D/Float32/weak/element 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/3D/Float32/weak/element private 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/3D/Float32/weak/node localmem E=1 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
dgsem/3D/Float32/weak/node localmem E=2 4 allocs: 0.188 kB 4 allocs: 0.188 kB 1
dgsem/3D/Float32/weak/node localmem v-last E=1 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
dgsem/3D/Float32/weak/node localmem v-last E=2 4 allocs: 0.188 kB 4 allocs: 0.188 kB 1
dgsem/3D/Float32/weak/node recompute E=2 4 allocs: 0.188 kB 4 allocs: 0.188 kB 1
dgsem/3D/Float32/weak/slab E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float32/weak/slab E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float32/weak/slab double-buffered E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float32/weak/slab double-buffered E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float32/weak/slab double-buffered v-last E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float32/weak/slab double-buffered v-last E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float32/weak/slab v-last E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float32/weak/slab v-last E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float64/fluxdiff/element 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/3D/Float64/fluxdiff/node E=2 4 allocs: 0.188 kB 4 allocs: 0.188 kB 1
dgsem/3D/Float64/fluxdiff/node halfsweep E=1 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
dgsem/3D/Float64/fluxdiff/node halfsweep v-last E=1 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
dgsem/3D/Float64/weak/element 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/3D/Float64/weak/element private 1 allocs: 16 B 1 allocs: 16 B 1
dgsem/3D/Float64/weak/node localmem E=1 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
dgsem/3D/Float64/weak/node localmem E=2 4 allocs: 0.188 kB 4 allocs: 0.188 kB 1
dgsem/3D/Float64/weak/node localmem v-last E=1 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
dgsem/3D/Float64/weak/node localmem v-last E=2 4 allocs: 0.188 kB 4 allocs: 0.188 kB 1
dgsem/3D/Float64/weak/node recompute E=2 4 allocs: 0.188 kB 4 allocs: 0.188 kB 1
dgsem/3D/Float64/weak/slab E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float64/weak/slab E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float64/weak/slab double-buffered E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float64/weak/slab double-buffered E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float64/weak/slab double-buffered v-last E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float64/weak/slab double-buffered v-last E=8 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float64/weak/slab v-last E=1 1 allocs: 32 B 1 allocs: 32 B 1
dgsem/3D/Float64/weak/slab v-last E=8 1 allocs: 32 B 1 allocs: 32 B 1
launch/3D static workgroup, dynamic ndrange 4 allocs: 0.125 kB 4 allocs: 0.125 kB 1
launch/3D static workgroup, static ndrange 4 allocs: 0.125 kB 4 allocs: 0.125 kB 1
launch/dynamic workgroup, dynamic ndrange 0 allocs: 0 B 0 allocs: 0 B
launch/dynamic workgroup, dynamic ndrange, workgroupsize given 0 allocs: 0 B 0 allocs: 0 B
launch/static workgroup, dynamic ndrange 0 allocs: 0 B 0 allocs: 0 B
launch/static workgroup, static ndrange 0 allocs: 0 B 0 allocs: 0 B
narrow workgroup/masked/W=16 0 allocs: 0 B 0 allocs: 0 B
narrow workgroup/masked/W=4 0 allocs: 0 B 0 allocs: 0 B
narrow workgroup/unmasked/W=16 0 allocs: 0 B 0 allocs: 0 B
narrow workgroup/unmasked/W=4 0 allocs: 0 B 0 allocs: 0 B
partition/dynamic workgroup, dynamic ndrange 2 allocs: 0.0625 kB 2 allocs: 0.0625 kB 1
partition/static workgroup, dynamic ndrange 2 allocs: 32 B 2 allocs: 32 B 1
partition/static workgroup, static ndrange 0 allocs: 0 B 0 allocs: 0 B
saxpy/default/Float16/1024 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float16/1048576 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float16/16384 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float16/2048 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float16/256 0 allocs: 0 B 0 allocs: 0 B
saxpy/default/Float16/262144 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float16/32768 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float16/4096 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float16/512 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float16/64 0 allocs: 0 B 0 allocs: 0 B
saxpy/default/Float16/65536 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/1024 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/1048576 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/16384 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/2048 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/256 0 allocs: 0 B 0 allocs: 0 B
saxpy/default/Float32/262144 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/32768 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/4096 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/512 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float32/64 0 allocs: 0 B 0 allocs: 0 B
saxpy/default/Float32/65536 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/1024 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/1048576 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/16384 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/2048 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/256 0 allocs: 0 B 0 allocs: 0 B
saxpy/default/Float64/262144 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/32768 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/4096 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/512 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/default/Float64/64 0 allocs: 0 B 0 allocs: 0 B
saxpy/default/Float64/65536 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/1024 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/1048576 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/16384 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/2048 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/256 0 allocs: 0 B 0 allocs: 0 B
saxpy/static workgroup=(1024,)/Float16/262144 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/32768 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/4096 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/512 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float16/64 0 allocs: 0 B 0 allocs: 0 B
saxpy/static workgroup=(1024,)/Float16/65536 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/1024 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/1048576 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/16384 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/2048 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/256 0 allocs: 0 B 0 allocs: 0 B
saxpy/static workgroup=(1024,)/Float32/262144 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/32768 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/4096 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/512 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float32/64 0 allocs: 0 B 0 allocs: 0 B
saxpy/static workgroup=(1024,)/Float32/65536 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/1024 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/1048576 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/16384 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/2048 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/256 0 allocs: 0 B 0 allocs: 0 B
saxpy/static workgroup=(1024,)/Float64/262144 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/32768 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/4096 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/512 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
saxpy/static workgroup=(1024,)/Float64/64 0 allocs: 0 B 0 allocs: 0 B
saxpy/static workgroup=(1024,)/Float64/65536 3 allocs: 0.0469 kB 3 allocs: 0.0469 kB 1
time_to_load 0.2 k allocs: 11.8 kB 0.2 k allocs: 11.8 kB 1

Benchmark Plots

A plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR.
Go to "Actions"->"Benchmark a pull request"->[the most recent run]->"Artifacts" (at the bottom).

Strip singleton kernel functions before `launch_setup`, `select_launch`
and `launch_geometry` so they compile once per backend and static sizes.
Preserve stateful callables for custom hooks.

First launch with a previously launched shape (Julia 1.12): PoCL 1-d
61 -> 48 ms, 3-d with partial work-groups 68 -> 49 ms; CUDA (RTX 5080)
81 -> 76 ms and 98 -> 83 ms.
Build `launch_converted` argument expressions with a vector-based helper
so it compiles once across argument counts. The first kernel with a new
argument count (3 to 8) launches in 94 instead of 120 ms (Julia 1.12).
Use `fma` in the precompile kernel to precompile LLVM.jl's `llvmcall`
generators for OpenCL builtins. A session's first axpy launch drops from
425 to 299 ms (Julia 1.12).
Since #922, `launch_kernel` looks up every kernel through
`kernel_function_as`, which hides the kernel's type from inference so
that PoCL compiles the lookup once for both checked and unchecked
kernels. On other backends, this made every launch dispatch
dynamically. Call `KI.kernel_function` directly there; the branch on
`skip_bounds_check` folds at compile time.

Per-launch host cost, ns (Julia 1.12, best of 200 batches of 64
launches, median of 6 interleaved runs): CUDA (RTX 5080) 1749 -> 1598
for a 1-d kernel and 1838 -> 1635 for a 3-d kernel (raw `@cuda`: 1468);
AMDGPU (gfx1036) 1364 -> 1121 and 1351 -> 1124 (raw `@roc`: 727). A
new kernel's first launch takes 6-17 ms less.
@maleadt
maleadt force-pushed the tb/launch-cost-main branch from a2dd21b to 7b9b007 Compare October 27, 2026 20:16
@github-actions

Copy link
Copy Markdown
Contributor

Benchmark results

Compared with 466ad8a16, the merge base.
Times are the minimum time of every benchmark. A change is unusual when it exceeds both three times the benchmark's typical change between consecutive runs on the default branch and a minimum per category (performance 3%, latency 5%, applications 3%, other 3%). A benchmark that starts or stops allocating is listed however few the allocations. A single run can still be a fluke, so rerun before drawing conclusions.

Geometric mean Unusual: slower Unusual: faster Compared
Performance +0.6% 0 0 32
Latency −2.1% 0 0 13
Applications +12.0% 0 0 5

No unusual changes.

All benchmarks
Benchmark Baseline This PR Change
Performance
julia/serial/arithmetic/muladd-reference 655 µs 655 µs −0.0% noise unknown
julia/serial/math/exp-f64-reference 284 µs 284 µs +0.0% noise unknown
julia/serial/narrow/masked-reference 708 µs 720 µs +1.7% noise unknown
pocl/parallel/atomics/histogram 5.14 ms 5.14 ms −0.0% noise unknown
pocl/parallel/groupreduce/add-f64 1.66 ms 1.65 ms −0.2% noise unknown
pocl/parallel/groupreduce/fallback 18.7 ms 18.1 ms −3.1% noise unknown
pocl/parallel/groupscan/add-i32 2.52 ms 2.53 ms +0.2% noise unknown
pocl/parallel/indexing/cartesian3d 78.7 µs 79.3 µs +0.7% noise unknown
pocl/parallel/indexing/linear3d 82.1 µs 76.9 µs −6.4% noise unknown
pocl/parallel/indexing/ntuple3d 78.6 µs 82.5 µs +4.9% noise unknown
pocl/parallel/indexing/ntuple4d 159 µs 174 µs +9.0% noise unknown
pocl/parallel/localmem/transpose-naive 461 µs 456 µs −1.1% noise unknown
pocl/parallel/localmem/transpose-tiled 98.7 µs 106 µs +7.6% noise unknown
pocl/parallel/private/across-barrier 165 µs 164 µs −0.4% noise unknown
pocl/parallel/scheduling/default 181 µs 181 µs +0.1% noise unknown
pocl/parallel/scheduling/explicit 181 µs 184 µs +1.4% noise unknown
pocl/parallel/stream/copy/cache 749 ns 680 ns −9.2% noise unknown
pocl/parallel/stream/copy/dram 6.34 ms 6.38 ms +0.5% noise unknown
pocl/parallel/stream/triad/cache 6.53 µs 6.85 µs +4.8% noise unknown
pocl/parallel/stream/triad/dram 9.26 ms 9.23 ms −0.3% noise unknown
pocl/parallel/util/copyto-small 24.0 ns 25.0 ns +4.2% noise unknown
pocl/parallel/util/zeros-large 995 µs 1.01 ms +1.3% noise unknown
pocl/parallel/util/zeros-small 637 ns 645 ns +1.3% noise unknown
pocl/serial/arithmetic/contract 1.91 ms 1.90 ms −0.3% noise unknown
pocl/serial/arithmetic/muladd 731 µs 770 µs +5.3% noise unknown
pocl/serial/const/marked 731 µs 729 µs −0.3% noise unknown
pocl/serial/const/unmarked 922 µs 920 µs −0.2% noise unknown
pocl/serial/math/exp-f64 146 µs 147 µs +0.6% noise unknown
pocl/serial/math/sin-f64 194 µs 190 µs −2.0% noise unknown
pocl/serial/narrow/masked 836 µs 824 µs −1.4% noise unknown
pocl/serial/narrow/unmasked 778 µs 788 µs +1.2% noise unknown
pocl/serial/stream/triad/dram 8.58 ms 8.68 ms +1.2% noise unknown
Latency
host/import/ka 511 ms 513 ms +0.3% noise unknown
host/precompile/ka 44.4 s 44.7 s +0.7% noise unknown
host/startup 131 ms 131 ms +0.3% noise unknown
pocl/parallel/launch/dynamic-default 4.41 µs 4.38 µs −0.7% noise unknown
pocl/parallel/launch/groupreduce-f64 6.66 µs 5.95 µs −10.7% noise unknown
pocl/parallel/launch/many-args 11.8 µs 12.2 µs +3.4% noise unknown
pocl/parallel/launch/partial-workgroup 3.99 µs 3.90 µs −2.3% noise unknown
pocl/serial/compile/groupreduce 2.02 s 1.88 s −6.8% noise unknown
pocl/serial/compile/localmem-barrier 797 ms 707 ms −11.2% noise unknown
pocl/serial/compile/new-kernel 126 ms 118 ms −6.1% noise unknown
pocl/serial/compile/new-workgroupsize 13.6 ms 13.8 ms +1.6% noise unknown
pocl/serial/first-kernel/cold-cache 2.03 s 2.05 s +0.7% noise unknown
pocl/serial/first-kernel/warm-cache 367 ms 388 ms +5.8% noise unknown
Applications
pocl/parallel/dgsem/fluxdiff-node 287 µs 403 µs +40.4% noise unknown
pocl/parallel/dgsem/weak-element 160 µs 208 µs +29.7% noise unknown
pocl/parallel/dgsem/weak-node-localmem 117 µs 119 µs +1.5% noise unknown
pocl/parallel/stencil/timestep 8.37 ms 8.09 ms −3.3% noise unknown
pocl/serial/stencil/timestep 11.4 ms 11.2 ms −1.5% noise unknown

History: https://juliagpu.org/perf/?pkg=kernelabstractions

@maleadt
maleadt merged commit c10ff8c into main Oct 28, 2026
23 of 24 checks passed
@maleadt
maleadt deleted the tb/launch-cost-main branch October 28, 2026 07:23
@codecov

codecov Bot commented Oct 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.76%. Comparing base (466ad8a) to head (7b9b007).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #931      +/-   ##
==========================================
- Coverage   83.30%   82.76%   -0.54%     
==========================================
  Files          31       31              
  Lines        2569     2524      -45     
==========================================
- Hits         2140     2089      -51     
- Misses        429      435       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

latency Compile time, load time, time-to-first-execution, invalidations.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant