Skip to content

Transfer buffer ownership at conversion time instead of a per-launch locked phase - #1050

Merged
luraess merged 2 commits into
JuliaGPU:mainfrom
gbaraldi:launch-ownership
Aug 27, 2026
Merged

Transfer buffer ownership at conversion time instead of a per-launch locked phase#1050
luraess merged 2 commits into
JuliaGPU:mainfrom
gbaraldi:launch-ownership

Conversation

@gbaraldi

Copy link
Copy Markdown
Member

While comparing host-side overheads against CUDA.jl on an MI300A, the kernel-launch path stood out: every launch allocates a Vector{Managed}, rocconvert pushes each argument's buffer into it, and a with_managed phase then does unique + sort(by=objectid) + lock/unlock of every buffer's ReentrantLock around the launch. CUDA.jl performs the equivalent stream-ownership tracking inline in pointer conversion, with no per-launch collection phase.

This PR adopts that design:

  • Runtime.Adaptor carries the launching stream instead of a Managed collector (nothing skips ownership handling, for bare rocconvert used in reflection/argconvert);
  • the ROCArray adapt rule transfers ownership inline via a new take_ownership_fast!: an unlocked fast path for the common case (buffer already owned by this stream and already dirty), falling back to the locked take_ownership! otherwise. The unsynchronized read only races with usage patterns that were already racy under the locked protocol (concurrent use of one array from tasks on different streams);
  • the HIPKernel call operator drops the vector and the with_managed wrapper. with_managed/lock_managed remain for external users.

Measured on MI300A (gfx942, ROCm 7.2.4, Julia 1.12.6), empty kernel / 3-array+4-scalar kernel:

metric before after
no-arg kernel-object launch 4,781 ns / 144 B 4,649 ns / 0 B
launch with 3 arrays + 4 scalars 7,354 ns / 1,232 B 6,596 ns / 416 B
small broadcast c .= a .+ b 15,437 ns / 3,056 B 14,685 ns / 2,320 B

Correctness validated on hardware including the cross-stream case (array owned by the default stream, used from a second stream via stream! — the locked slow path still synchronizes the previous stream).

For context from the same comparison: with this change the Julia-side launch overhead is essentially at parity with CUDA.jl (burst submission 3.7 vs 3.1 µs, and raw hipModuleLaunchKernel costs as much as the full AMDGPU.jl path) — the remaining gaps vs CUDA (launch throughput, 17 µs launch+sync round trip, and 10 µs hipMallocFromPoolAsync+hipFreeAsync pairs vs 1.5 µs on CUDA) are in the HIP runtime, not in this package.

Every kernel launch currently allocates a Vector{Managed}, pushes each
argument's buffer into it during rocconvert, then runs a with_managed
phase: unique + sort by objectid + lock/unlock every buffer's
ReentrantLock around the launch. CUDA.jl performs the equivalent
stream-ownership tracking inline in pointer conversion with no
per-launch collection phase.

Do the same here: Runtime.Adaptor now carries the launching stream, and
the ROCArray adapt rule transfers ownership inline through
take_ownership_fast! — an unlocked fast path for the common case (buffer
already owned by this stream and already dirty), falling back to the
locked take_ownership! otherwise. The unsynchronized read only races
with patterns that were already racy under the locked protocol.

Measured on MI300A (ROCm 7.2.4, Julia 1.12.6): no-argument kernel-object
launches go from 144 to 0 bytes allocated (4.78 -> 4.49 us), launches
with 3 arrays + 4 scalars from 1232 to 896 bytes (7.35 -> 6.81 us).
with_managed/lock_managed stay for any external users.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AMDGPU.jl Benchmarks

Details
Benchmark suite Current: 56a4329 Previous: 16f5974 Ratio
amdgpu/synchronization/context/device 552.5 ns 755 ns 0.73
amdgpu/synchronization/stream/blocking 235 ns 317.5 ns 0.74
amdgpu/synchronization/stream/nonblocking 317.5 ns 432.25 ns 0.73
array/accumulate/Float32/1d 74421 ns 97092.25 ns 0.77
array/accumulate/Float32/dims=1 278646.25 ns 299718.5 ns 0.93
array/accumulate/Float32/dims=1L 79666.25 ns 121446.75 ns 0.66
array/accumulate/Float32/dims=2 70461 ns 130896.75 ns 0.54
array/accumulate/Float32/dims=2L 2757744.75 ns 2806321.5 ns 0.98
array/accumulate/Int64/1d 77241 ns 113217 ns 0.68
array/accumulate/Int64/dims=1 242483.25 ns 269376.25 ns 0.90
array/accumulate/Int64/dims=1L 84176.25 ns 144501.75 ns 0.58
array/accumulate/Int64/dims=2 84701.25 ns 127879.25 ns 0.66
array/accumulate/Int64/dims=2L 2897086.5 ns 2948585.75 ns 0.98
array/broadcast 73251 ns 64054.75 ns 1.14
array/construct 2065 ns 3220 ns 0.64
array/copy 37335.5 ns 39815 ns 0.94
array/copyto!/cpu_to_gpu 111581.5 ns 94539.5 ns 1.18
array/copyto!/gpu_to_cpu 120241.5 ns 95039.5 ns 1.27
array/copyto!/gpu_to_gpu 59270.75 ns 42115 ns 1.41
array/iteration/findall/bool 131351.75 ns 198036.5 ns 0.66
array/iteration/findall/int 148492 ns 207411.5 ns 0.72
array/iteration/findfirst/bool 145439.25 ns 167311.75 ns 0.87
array/iteration/findfirst/int 144769.25 ns 155156.75 ns 0.93
array/iteration/findmin/1d 109584 ns 179444.25 ns 0.61
array/iteration/findmin/2d 96596.25 ns 161089.25 ns 0.60
array/iteration/logical 234700.75 ns 317088.25 ns 0.74
array/iteration/scalar 302924 ns 343653.25 ns 0.88
array/permutedims/2d 71433.5 ns 82694.75 ns 0.86
array/permutedims/3d 71073.5 ns 82232.25 ns 0.86
array/permutedims/4d 73741 ns 84369.75 ns 0.87
array/random/rand/Float32 46008.25 ns 53677.5 ns 0.86
array/random/rand/Int64 54220.75 ns 66274.75 ns 0.82
array/random/rand!/Float32 66036 ns 49389.75 ns 1.34
array/random/rand!/Int64 73293.5 ns 59654.75 ns 1.23
array/random/randn/Float32 78516.25 ns 93112.25 ns 0.84
array/random/randn!/Float32 77758.5 ns 69269.75 ns 1.12
array/reductions/mapreduce/Float32/1d 86956.25 ns 141249.25 ns 0.62
array/reductions/mapreduce/Float32/dims=1 81748.5 ns 105462 ns 0.78
array/reductions/mapreduce/Float32/dims=1L 834063.5 ns 862533.5 ns 0.97
array/reductions/mapreduce/Float32/dims=2 82713.5 ns 108059.5 ns 0.77
array/reductions/mapreduce/Float32/dims=2L 143542 ns 155446.75 ns 0.92
array/reductions/mapreduce/Int64/1d 86626 ns 140932 ns 0.61
array/reductions/mapreduce/Int64/dims=1 81926.25 ns 104764.5 ns 0.78
array/reductions/mapreduce/Int64/dims=1L 830286 ns 860696 ns 0.96
array/reductions/mapreduce/Int64/dims=2 82653.75 ns 107757 ns 0.77
array/reductions/mapreduce/Int64/dims=2L 144024.5 ns 163496.75 ns 0.88
array/reductions/reduce/Float32/1d 86826 ns 145184.25 ns 0.60
array/reductions/reduce/Float32/dims=1 81471 ns 105042 ns 0.78
array/reductions/reduce/Float32/dims=1L 831651 ns 856898.25 ns 0.97
array/reductions/reduce/Float32/dims=2 82553.5 ns 106659.5 ns 0.77
array/reductions/reduce/Float32/dims=2L 143539.25 ns 161129.25 ns 0.89
array/reductions/reduce/Int64/1d 86686 ns 141302 ns 0.61
array/reductions/reduce/Int64/dims=1 81928.5 ns 105144.5 ns 0.78
array/reductions/reduce/Int64/dims=1L 832378.5 ns 851721 ns 0.98
array/reductions/reduce/Int64/dims=2 82113.75 ns 107839.5 ns 0.76
array/reductions/reduce/Int64/dims=2L 144034.5 ns 162989.25 ns 0.88
array/reverse/1d 45635.75 ns 53687.25 ns 0.85
array/reverse/1dL 72140.75 ns 78502.25 ns 0.92
array/reverse/1dL_inplace 60095.75 ns 62952.25 ns 0.95
array/reverse/1d_inplace 54553.25 ns 45130 ns 1.21
array/reverse/2d 49178.25 ns 55499.75 ns 0.89
array/reverse/2dL 81723.75 ns 111822 ns 0.73
array/reverse/2dL_inplace 92346.25 ns 74057 ns 1.25
array/reverse/2d_inplace 44235.5 ns 49312.25 ns 0.90
array/sorting/1d 332882.25 ns 359238.75 ns 0.93
integration/byval/reference 39380 ns 41860 ns 0.94
integration/byval/slices=1 40310 ns 42700 ns 0.94
integration/byval/slices=2 125802 ns 133939 ns 0.94
integration/byval/slices=3 234613 ns 239880 ns 0.98
integration/volumerhs 4999428 ns 4916200 ns 1.02
kernel/indexing 34753 ns 44594.75 ns 0.78
kernel/indexing_checked 49023 ns 47782.25 ns 1.03
kernel/launch 1345 ns 1785 ns 0.75
kernel/rand 97633.75 ns 110472 ns 0.88
latency/import 1702522741 ns 2242598207 ns 0.76
latency/precompile 39534644771 ns 52102215931 ns 0.76
latency/ttfp 2328242218 ns 3070234127 ns 0.76

This comment was automatically generated by workflow using github-action-benchmark.

@luraess luraess left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Thanks, maybe here as well, one could trim inline comments to the minimum before merging.

@luraess
luraess merged commit 24e1345 into JuliaGPU:main Aug 27, 2026
4 checks passed
@luraess

luraess commented Aug 27, 2026

Copy link
Copy Markdown
Member

Thanks!

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.

2 participants