Skip to content

Commit a7bf4f3

Browse files
JohnCobblerclaude
andcommitted
fix: use a private task-local RNG for kernel launch seeds
Kernel launches were drawing seeds via `Random.rand(UInt32)`, which consumed from Julia's task-global default RNG. This silently perturbed the host random-number stream whenever user code interleaved `@cuda` calls with `rand()` calls, making reproducibility impossible. Fix: introduce `launch_rng()`, which lazily creates a `Random.Xoshiro` in `task_local_storage()` (key `:CUDACore_launch_rng`) seeded from `Random.RandomDevice()`. The private RNG is completely independent of the default RNG and is task-local, so concurrent tasks each get their own seeded RNG. `make_seed(::HostKernel)` now draws from this private RNG instead. Device-side seeding (DeviceKernel / random.jl) is unchanged. Fixes #2417 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 81d7397 commit a7bf4f3

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

CUDACore/src/compiler/execution.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,18 @@ end
473473
# cache of kernel instances
474474
const _kernel_instances = Dict{Any, Any}()
475475

476-
make_seed(::HostKernel) = Random.rand(UInt32)
476+
# Return a task-local Xoshiro RNG that is seeded from RandomDevice on first use
477+
# per task. Using a private RNG ensures that launching a kernel does NOT
478+
# perturb the user-visible default RNG stream. (JuliaGPU/CUDA.jl#2417)
479+
const _LAUNCH_RNG_KEY = :CUDACore_launch_rng
480+
481+
function launch_rng()
482+
get!(() -> Random.Xoshiro(rand(Random.RandomDevice(), UInt64)),
483+
task_local_storage(),
484+
_LAUNCH_RNG_KEY)::Random.Xoshiro
485+
end
486+
487+
make_seed(::HostKernel) = rand(launch_rng(), UInt32)
477488

478489

479490
## device-side kernels

test/core/execution.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,3 +1222,32 @@ end
12221222
end
12231223

12241224
############################################################################################
1225+
1226+
# JuliaGPU/CUDA.jl#2417: kernel launches must not perturb the host default RNG stream
1227+
@testset "launch seed does not perturb host RNG" begin
1228+
1229+
dummy_kernel() = return
1230+
1231+
# Capture two host-rand values with a kernel launch interleaved; the sequence
1232+
# must be identical to the sequence produced without any launch.
1233+
Random.seed!(0xdeadbeef)
1234+
a_before = rand(UInt64)
1235+
@cuda threads=1 dummy_kernel() # must not consume from default RNG
1236+
a_after = rand(UInt64)
1237+
1238+
Random.seed!(0xdeadbeef)
1239+
b_before = rand(UInt64)
1240+
b_after = rand(UInt64)
1241+
1242+
@test a_before == b_before
1243+
@test a_after == b_after
1244+
1245+
# Two consecutive launches must still receive distinct seeds.
1246+
k = @cuda launch=false dummy_kernel()
1247+
seed1 = CUDACore.make_seed(k)
1248+
seed2 = CUDACore.make_seed(k)
1249+
@test seed1 != seed2
1250+
1251+
end
1252+
1253+
############################################################################################

0 commit comments

Comments
 (0)