Skip to content

Run full workgroups without the bounds mask on the CPU backend - #849

Closed
vchuravy wants to merge 1 commit into
mainfrom
vc/unmasked-full-groups
Closed

vchuravy wants to merge 1 commit into
mainfrom
vc/unmasked-full-groups

Conversation

@vchuravy

@vchuravy vchuravy commented Oct 6, 2026

Copy link
Copy Markdown
Member

@kernel tests __validindex in every work-item and runs the body under that mask, also in workgroups that lie entirely inside the ndrange. On the CPU backend that costs about 2x for cheap kernel bodies (#845): PoCL vectorizes over the work-items of a workgroup and keeps the mask inside the vectorized loop.

This emits each region of the body twice: without the mask when the whole workgroup lies inside the ndrange, and masked otherwise. The test is the same for all work-items of a workgroup, so PoCL's work-item loop only runs the unmasked copy for full workgroups. It is the single-launch form of #449: it covers padded ranges and tuned workgroup sizes, without a second launch for the remainder (which costs several µs on this backend).

__fullgroup(ctx) decides. It is false by default, so the unmasked copy is removed when compiling and GPU back-ends are unaffected; the PoCL backend overrides it with __fullgroup_check(ctx). Other backends can opt in the same way if the mask is expensive for them.

Alternatives I tried

  • __validindex returning full_group | validindex: depends on LLVM unswitching PoCL's work-item loop. It did for a tuned (128, 32, 1) workgroup (983 → 456 µs) but not for (64, 64, 1), which got 33% slower than main.
  • A barrier in each arm of the uniform branch, to force PoCL to build a work-item loop per arm: results stay correct, but it is 3–13x slower.
  • Unchecked launches for exact partitions only (given workgroup size that tiles the range): helps the 64 case most, but not tuned sizes or padded ranges.

Measurements

7-point stencil (r[I] = x[I±e₁] + x[I±e₂] + x[I±e₃] - 6x[I]) on a 128³ or 130³ interior, Julia 1.13, Ryzen 9 5950X, 1 thread pinned to fixed cores (other jobs were running on the machine), best of 3 interleaved runs of best-of-7, µs per launch:

ndrange, workgroupsize main this PR KA 0.9.43
128³, tuned 1096 452 475
130³, tuned 1235 549 1686
128³, 64 1324 769 876
130³, 64 2015 1622 2869
128³, (64, 64) 1082 549 505
130³, (64, 64) 1821 1218 3507

Results checked against a host reference for exact and padded ranges.

WaterLily v1.6.1 at 4 threads (Julia 1.13, seconds per 25 steps, measured with the same change before restricting it to PoCL, on top of #844, which doesn't change anything on Julia 1.13):

case main this PR
tgv 2^6 0.626 0.553
sphere 2^3 0.585 0.503
jelly 2^5 0.592 0.606
tgv 2^7 3.576 3.253

Compile time (WaterLily's constructor and first step) changed within noise, despite the duplicated body.

Tests

  • New codegen check: a dynamic ndrange gives two copies of the body on PoCL, a static one (where every workgroup is full) one. It fails on main.
  • The @print codegen check now uses a static ndrange, since a dynamic one has two printf calls (one per copy). It still checks that a @print lowers to a single call.
  • The full test suite passes locally on Julia 1.12.
  • Check the bounds of a partial workgroup without a branch per dimension #844's codegen check that the bounds check branches once will need adjusting if both go in: this adds the uniform full-workgroup branch.

Closes #845. Related: #449.

🤖 Generated with Claude Code

`@kernel` tests `__validindex` in every work-item and runs the body
under that mask, even in workgroups that lie entirely inside the
ndrange. PoCL vectorizes over the work-items of a workgroup and keeps
the mask in the vectorized loop, which made cheap kernel bodies about
twice as slow as without it (#845).

Emit each region of the body twice: without the mask when the whole
workgroup lies inside the ndrange, which is the same for all of its
work-items, and masked otherwise. `__fullgroup` decides, and only the
PoCL backend enables it; elsewhere it is `false`, and the unmasked copy
is removed when compiling. This is the single-launch form of #449.

Assisted-by: Claude Code
Comment thread src/launch.jl
Comment on lines +279 to +286
@inline full_group(ctx, launch::Launch, iterspace, ndrange) =
if builtin(iterspace) && ndrange isa CartesianIndices
T = index_type(launch)
groupsize = narrow(T, size(workitems(iterspace)))
reduce(&, map((g, w, n) -> g * w <= n, group_position(ctx, launch), groupsize, narrow(T, size(ndrange))); init = true)
else
false
end

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write this as function full_group

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results

Show table
main d04d294... main / d04d294...
const/@Const/Float32/262144 0.527 ± 0.017 ms 0.305 ± 0.012 ms 1.73 ± 0.088
const/@Const/Float32/65536 0.217 ± 0.0067 ms 0.119 ± 0.0047 ms 1.82 ± 0.092
const/@Const/Float64/262144 0.894 ± 0.017 ms 0.558 ± 0.017 ms 1.6 ± 0.057
const/@Const/Float64/65536 0.369 ± 0.018 ms 0.185 ± 0.0076 ms 1.99 ± 0.13
const/unmarked/Float32/262144 2.26 ± 0.018 ms 2.26 ± 0.016 ms 0.999 ± 0.011
const/unmarked/Float32/65536 0.597 ± 0.016 ms 0.593 ± 0.017 ms 1.01 ± 0.041
const/unmarked/Float64/262144 3.11 ± 0.014 ms 3.11 ± 0.0084 ms 1 ± 0.0054
const/unmarked/Float64/65536 0.811 ± 0.013 ms 0.811 ± 0.013 ms 1 ± 0.023
launch/3D static workgroup, dynamic ndrange 10.6 ± 0.61 μs 10.4 ± 0.67 μs 1.01 ± 0.088
launch/3D static workgroup, static ndrange 11.7 ± 43 μs 11.5 ± 0.32 μs 1.02 ± 3.7
launch/dynamic workgroup, dynamic ndrange 11.6 ± 8 μs 12.3 ± 41 μs 0.941 ± 3.2
launch/dynamic workgroup, dynamic ndrange, workgroupsize given 12.3 ± 41 μs 11.9 ± 0.72 μs 1.03 ± 3.4
launch/static workgroup, dynamic ndrange 10.4 ± 0.25 μs 10.5 ± 0.5 μs 0.988 ± 0.053
launch/static workgroup, static ndrange 11.5 ± 1.1 μs 11.4 ± 0.33 μs 1 ± 0.097
partition/dynamic workgroup, dynamic ndrange 0.0621 ± 0.00096 μs 0.0591 ± 0.0016 μs 1.05 ± 0.033
partition/static workgroup, dynamic ndrange 0.0565 ± 0.012 μs 0.058 ± 0.013 μs 0.974 ± 0.29
partition/static workgroup, static ndrange 1.55 ± 0.01 ns 1.55 ± 0.01 ns 1 ± 0.0091
saxpy/default/Float16/1024 0.0542 ± 0.0055 ms 13.9 ± 1.2 μs 3.91 ± 0.52
saxpy/default/Float16/1048576 1.47 ± 0.023 ms 0.222 ± 0.023 ms 6.62 ± 0.68
saxpy/default/Float16/16384 0.0745 ± 0.012 ms 0.0543 ± 0.0097 ms 1.37 ± 0.33
saxpy/default/Float16/2048 0.0578 ± 0.0046 ms 0.0515 ± 0.039 ms 1.12 ± 0.85
saxpy/default/Float16/256 11.4 ± 40 μs 10.9 ± 41 μs 1.05 ± 5.4
saxpy/default/Float16/262144 0.406 ± 0.019 ms 0.095 ± 0.01 ms 4.28 ± 0.49
saxpy/default/Float16/32768 0.0944 ± 0.0099 ms 0.0541 ± 0.0067 ms 1.74 ± 0.28
saxpy/default/Float16/4096 0.0636 ± 0.0065 ms 0.0488 ± 0.039 ms 1.3 ± 1.1
saxpy/default/Float16/512 11.4 ± 0.38 μs 10.6 ± 0.24 μs 1.07 ± 0.043
saxpy/default/Float16/64 11.1 ± 0.29 μs 10.9 ± 0.19 μs 1.02 ± 0.032
saxpy/default/Float16/65536 0.137 ± 0.0078 ms 0.0608 ± 0.007 ms 2.24 ± 0.29
saxpy/default/Float32/1024 21.8 ± 41 μs 15.5 ± 40 μs 1.41 ± 4.5
saxpy/default/Float32/1048576 0.773 ± 0.072 ms 0.322 ± 0.03 ms 2.4 ± 0.32
saxpy/default/Float32/16384 0.0633 ± 0.0066 ms 0.0536 ± 0.0063 ms 1.18 ± 0.19
saxpy/default/Float32/2048 0.0556 ± 0.005 ms 0.0531 ± 0.038 ms 1.05 ± 0.76
saxpy/default/Float32/256 11.5 ± 43 μs 11.5 ± 43 μs 0.998 ± 5.3
saxpy/default/Float32/262144 0.218 ± 0.034 ms 0.117 ± 0.011 ms 1.86 ± 0.34
saxpy/default/Float32/32768 0.0726 ± 0.0066 ms 0.057 ± 0.0042 ms 1.27 ± 0.15
saxpy/default/Float32/4096 0.0586 ± 0.0056 ms 0.0549 ± 0.0088 ms 1.07 ± 0.2
saxpy/default/Float32/512 14.9 ± 9 μs 13.5 ± 4.9 μs 1.1 ± 0.78
saxpy/default/Float32/64 11.1 ± 0.34 μs 11 ± 0.5 μs 1.01 ± 0.056
saxpy/default/Float32/65536 0.0963 ± 0.0099 ms 0.0663 ± 0.0061 ms 1.45 ± 0.2
saxpy/default/Float64/1024 0.0564 ± 0.0089 ms 15.6 ± 1.3 μs 3.61 ± 0.65
saxpy/default/Float64/1048576 1.31 ± 0.27 ms 0.652 ± 0.063 ms 2.01 ± 0.46
saxpy/default/Float64/16384 0.0662 ± 0.0064 ms 0.0575 ± 0.0049 ms 1.15 ± 0.15
saxpy/default/Float64/2048 0.0582 ± 0.0056 ms 0.0536 ± 0.0094 ms 1.09 ± 0.22
saxpy/default/Float64/256 14.2 ± 1.8 μs 15.2 ± 42 μs 0.936 ± 2.6
saxpy/default/Float64/262144 0.28 ± 0.049 ms 0.187 ± 0.02 ms 1.5 ± 0.31
saxpy/default/Float64/32768 0.0813 ± 0.013 ms 0.0675 ± 0.0067 ms 1.2 ± 0.23
saxpy/default/Float64/4096 0.0553 ± 0.0085 ms 0.0521 ± 0.0078 ms 1.06 ± 0.23
saxpy/default/Float64/512 0.0521 ± 0.04 ms 15.2 ± 24 μs 3.42 ± 6.1
saxpy/default/Float64/64 11 ± 0.34 μs 10.9 ± 0.25 μs 1.01 ± 0.039
saxpy/default/Float64/65536 0.115 ± 0.017 ms 0.0846 ± 0.0077 ms 1.36 ± 0.24
saxpy/static workgroup=(1024,)/Float16/1024 0.0557 ± 0.004 ms 13.9 ± 4.2 μs 4.02 ± 1.3
saxpy/static workgroup=(1024,)/Float16/1048576 1.48 ± 0.018 ms 0.228 ± 0.027 ms 6.49 ± 0.77
saxpy/static workgroup=(1024,)/Float16/16384 0.0722 ± 0.011 ms 0.0534 ± 0.007 ms 1.35 ± 0.27
saxpy/static workgroup=(1024,)/Float16/2048 0.0571 ± 0.0042 ms 16.3 ± 32 μs 3.5 ± 6.9
saxpy/static workgroup=(1024,)/Float16/256 11.6 ± 43 μs 11.6 ± 0.3 μs 1 ± 3.7
saxpy/static workgroup=(1024,)/Float16/262144 0.41 ± 0.02 ms 0.0953 ± 0.0096 ms 4.31 ± 0.48
saxpy/static workgroup=(1024,)/Float16/32768 0.0956 ± 0.0081 ms 0.054 ± 0.0066 ms 1.77 ± 0.26
saxpy/static workgroup=(1024,)/Float16/4096 0.0602 ± 0.0055 ms 0.0542 ± 0.0099 ms 1.11 ± 0.23
saxpy/static workgroup=(1024,)/Float16/512 0.044 ± 0.041 ms 11.6 ± 38 μs 3.79 ± 13
saxpy/static workgroup=(1024,)/Float16/64 11.4 ± 0.29 μs 11.4 ± 0.23 μs 1 ± 0.032
saxpy/static workgroup=(1024,)/Float16/65536 0.138 ± 0.0072 ms 0.0619 ± 0.0069 ms 2.23 ± 0.27
saxpy/static workgroup=(1024,)/Float32/1024 0.0514 ± 0.042 ms 0.052 ± 0.041 ms 0.988 ± 1.1
saxpy/static workgroup=(1024,)/Float32/1048576 0.778 ± 0.062 ms 0.32 ± 0.034 ms 2.43 ± 0.33
saxpy/static workgroup=(1024,)/Float32/16384 0.0628 ± 0.0068 ms 0.0539 ± 0.0046 ms 1.17 ± 0.16
saxpy/static workgroup=(1024,)/Float32/2048 0.0566 ± 0.0053 ms 19.6 ± 38 μs 2.88 ± 5.6
saxpy/static workgroup=(1024,)/Float32/256 11.6 ± 23 μs 11.7 ± 41 μs 0.994 ± 4
saxpy/static workgroup=(1024,)/Float32/262144 0.228 ± 0.027 ms 0.115 ± 0.011 ms 1.97 ± 0.3
saxpy/static workgroup=(1024,)/Float32/32768 0.0744 ± 0.0077 ms 0.0581 ± 0.005 ms 1.28 ± 0.17
saxpy/static workgroup=(1024,)/Float32/4096 0.0522 ± 0.0092 ms 0.0493 ± 0.019 ms 1.06 ± 0.45
saxpy/static workgroup=(1024,)/Float32/512 15.9 ± 43 μs 0.0527 ± 0.04 ms 0.301 ± 0.85
saxpy/static workgroup=(1024,)/Float32/64 11.6 ± 0.22 μs 11.5 ± 0.64 μs 1 ± 0.059
saxpy/static workgroup=(1024,)/Float32/65536 0.0963 ± 0.0064 ms 0.0673 ± 0.0064 ms 1.43 ± 0.17
saxpy/static workgroup=(1024,)/Float64/1024 0.0537 ± 0.039 ms 16.4 ± 39 μs 3.28 ± 8.3
saxpy/static workgroup=(1024,)/Float64/1048576 1.27 ± 0.26 ms 0.678 ± 0.086 ms 1.87 ± 0.45
saxpy/static workgroup=(1024,)/Float64/16384 0.0667 ± 0.0085 ms 0.0585 ± 0.005 ms 1.14 ± 0.17
saxpy/static workgroup=(1024,)/Float64/2048 0.0566 ± 0.0054 ms 0.0556 ± 0.0076 ms 1.02 ± 0.17
saxpy/static workgroup=(1024,)/Float64/256 0.0501 ± 0.044 ms 0.0531 ± 0.038 ms 0.945 ± 1.1
saxpy/static workgroup=(1024,)/Float64/262144 0.301 ± 0.037 ms 0.184 ± 0.021 ms 1.63 ± 0.27
saxpy/static workgroup=(1024,)/Float64/32768 0.0826 ± 0.014 ms 0.0676 ± 0.0074 ms 1.22 ± 0.25
saxpy/static workgroup=(1024,)/Float64/4096 0.0555 ± 0.01 ms 0.0507 ± 0.0089 ms 1.09 ± 0.28
saxpy/static workgroup=(1024,)/Float64/512 0.0515 ± 0.038 ms 0.052 ± 0.04 ms 0.991 ± 1.1
saxpy/static workgroup=(1024,)/Float64/64 11.6 ± 0.23 μs 11.6 ± 44 μs 0.994 ± 3.7
saxpy/static workgroup=(1024,)/Float64/65536 0.116 ± 0.023 ms 0.085 ± 0.0088 ms 1.36 ± 0.3
time_to_load 0.519 ± 0.0042 s 0.509 ± 0.0032 s 1.02 ± 0.01
main d04d294... main / d04d294...
const/@Const/Float32/262144 9 allocs: 0.203 kB 9 allocs: 0.203 kB 1
const/@Const/Float32/65536 9 allocs: 0.203 kB 9 allocs: 0.203 kB 1
const/@Const/Float64/262144 9 allocs: 0.203 kB 9 allocs: 0.203 kB 1
const/@Const/Float64/65536 9 allocs: 0.203 kB 9 allocs: 0.203 kB 1
const/unmarked/Float32/262144 9 allocs: 0.203 kB 9 allocs: 0.203 kB 1
const/unmarked/Float32/65536 9 allocs: 0.203 kB 9 allocs: 0.203 kB 1
const/unmarked/Float64/262144 9 allocs: 0.203 kB 9 allocs: 0.203 kB 1
const/unmarked/Float64/65536 9 allocs: 0.203 kB 9 allocs: 0.203 kB 1
launch/3D static workgroup, dynamic ndrange 9 allocs: 0.219 kB 9 allocs: 0.219 kB 1
launch/3D static workgroup, static ndrange 9 allocs: 0.219 kB 9 allocs: 0.219 kB 1
launch/dynamic workgroup, dynamic ndrange 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
launch/dynamic workgroup, dynamic ndrange, workgroupsize given 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
launch/static workgroup, dynamic ndrange 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
launch/static workgroup, static ndrange 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
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 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float16/1048576 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/default/Float16/16384 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/default/Float16/2048 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float16/256 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/default/Float16/262144 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/default/Float16/32768 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/default/Float16/4096 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float16/512 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float16/64 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/default/Float16/65536 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/default/Float32/1024 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float32/1048576 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/default/Float32/16384 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float32/2048 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float32/256 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/default/Float32/262144 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/default/Float32/32768 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float32/4096 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float32/512 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float32/64 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/default/Float32/65536 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/default/Float64/1024 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float64/1048576 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/default/Float64/16384 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float64/2048 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float64/256 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/default/Float64/262144 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/default/Float64/32768 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/default/Float64/4096 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float64/512 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/default/Float64/64 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/default/Float64/65536 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/static workgroup=(1024,)/Float16/1024 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float16/1048576 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/static workgroup=(1024,)/Float16/16384 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float16/2048 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float16/256 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/static workgroup=(1024,)/Float16/262144 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/static workgroup=(1024,)/Float16/32768 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/static workgroup=(1024,)/Float16/4096 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float16/512 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float16/64 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/static workgroup=(1024,)/Float16/65536 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/static workgroup=(1024,)/Float32/1024 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float32/1048576 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/static workgroup=(1024,)/Float32/16384 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float32/2048 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float32/256 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/static workgroup=(1024,)/Float32/262144 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/static workgroup=(1024,)/Float32/32768 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float32/4096 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float32/512 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float32/64 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/static workgroup=(1024,)/Float32/65536 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/static workgroup=(1024,)/Float64/1024 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float64/1048576 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/static workgroup=(1024,)/Float64/16384 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float64/2048 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float64/256 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/static workgroup=(1024,)/Float64/262144 12 allocs: 0.25 kB 12 allocs: 0.25 kB 1
saxpy/static workgroup=(1024,)/Float64/32768 12 allocs: 0.25 kB 8 allocs: 0.141 kB 1.78
saxpy/static workgroup=(1024,)/Float64/4096 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float64/512 8 allocs: 0.141 kB 8 allocs: 0.141 kB 1
saxpy/static workgroup=(1024,)/Float64/64 5 allocs: 0.0938 kB 5 allocs: 0.0938 kB 1
saxpy/static workgroup=(1024,)/Float64/65536 12 allocs: 0.25 kB 12 allocs: 0.25 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).

@vchuravy

vchuravy commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

This causes problems for kernels with @synchronize.

Each region between @synchronizes is duplicated on its own (if __full_group__ … elseif __active_lane__ … end). A variable defined in one region and used in a later one is then defined on two different paths, and Julia can no longer prove it's defined at the use. It emits UndefVarError checks that LLVM doesn't remove. With the plain mask the condition is the same __active_lane__ in every region, so LLVM folds them away.

Example: a neighbour sum through @localmem (I, l from @index, tile[l] = A[I], @synchronize, then s = 2tile[l] + tile[l-1] + tile[l+1], B[I] = s), workgroup 256, on PoCL:

error blocks in the device IR barriers
main 0 1
this PR 2 (exception reports at the first use of l after the barrier) 1

Results are still correct (n = 4096 and 4099). I haven't measured what the error paths cost on PoCL.

They do cost on GPUs. I checked whether enabling __fullgroup would help on CUDA (Quadro RTX 4000) and AMDGPU (gfx1030), by overriding it from the benchmark script; it isn't enabled there in this PR. For @synchronize kernels it made things worse:

kernel CUDA registers CUDA SASS instructions CUDA time AMD time
neighbour sum 10 → 34 40 → 536 (incl. spills and a gpu_report_exception call) 1.01x 1.14x
block reverse (1024) 12 → 34 40 → 520 1.04x 1.17x
tile transpose (32, 32) 18 → 38–40 1.07x 1.01x

Kernels without @synchronize were within ±3% on both GPUs, so this should stay a PoCL-only opt-in either way.

Emitting the whole body twice behind one uniform branch (if __full_group__ <body, barriers included> else <masked body> end, with @localmem/@uniform hoisted before the branch) avoids the error paths: 0 error blocks on PoCL, and 0.95–1.00x for these kernels on the GPUs. But it puts the barriers inside a branch, which was 3–13x slower on PoCL when I tried barriers in the branch arms earlier.

So for PoCL I'd limit the duplication to kernels without @synchronize (a single region), which covers the stencil cases this is for, and keep the masked form for kernels with barriers. I'll update the PR accordingly unless someone sees a better way to keep the variables defined across regions.

@vchuravy
vchuravy marked this pull request as draft October 7, 2026 08:16
@vchuravy

vchuravy commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

Testing by @maleadt

Most of the ~2x in the issue comes from Zen 3 being slow at masked vector stores. On 9950X (Zen 5, AVX-512) the mask costs only 1.1–1.2x.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CPU backend: the bounds mask makes kernel bodies up to 2.3x slower, even when the workgroups tile the ndrange

1 participant