Repeat floating-point results on the same GPU and driver, not across vendors or drivers. Linux and Windows require Vulkan 1.3.
dotnet new console dotnet add package RecursiveAI.OneGpu.Backends dotnet add package RecursiveAI.OneGpu.Kernels
Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.
using RecursiveAI.OneGpu; using RecursiveAI.OneGpu.Kernels; using var device = GpuBackends.CreateDevice( new GpuDeviceOptions { Adapter = "discrete" }); using var left = device.CreateBuffer<float>(new float[] { 1, 2, 3, 4 }); using var right = device.CreateBuffer<float>(new float[] { 5, 6, 7, 8 }); using var output = device.CreateBuffer<float>(4); using var gemm = new Gemm(device, GemmElementType.F32, GpuKernelMode.Deterministic); var first = new float[4]; var second = new float[4]; foreach (var result in new[] { first, second }) { var encoder = device.BeginCommands(); gemm.Encode(encoder, new GemmProblem(2, 2, 2), left, right, output); device.Submit(encoder); await output.ReadAsync(result); } Console.WriteLine(string.Join(", ", first)); Console.WriteLine($"Identical bits: {first.Select(BitConverter.SingleToInt32Bits) .SequenceEqual(second.Select(BitConverter.SingleToInt32Bits))}");
The input buffers left and right hold row-major 2 × 2 matrices. The example project has ImplicitUsings enabled, which supplies System.Linq for Select and SequenceEqual. Gemm means general matrix multiplication. The second readback completes before the buffers and kernel are disposed. This output was recorded on an NVIDIA GeForce RTX 4070 with Vulkan.
A wrong shape, stride, size or offset throws GpuKernelShapeException; an unsupported capability throws GpuUnsupportedException naming what is missing.
Unzip the example, then dotnet run.
Prints the matrix product and compares the bits from two submissions:
19, 22, 43, 50 Identical bits: True
Variations
Select a GPU by name
Replace the device creation in the program with this. The name must match an adapter on your machine; this version produced the same output on the RTX 4070:
using var device = GpuBackends.CreateDevice( new GpuDeviceOptions { Adapter = "NVIDIA GeForce RTX 4070" });
Related recipes
The short answer
Pass GpuKernelMode.Deterministic when constructing a supported OneGpu kernel family. In OneGpu 1.0.0, that mode applies to GEMM, reductions, softmax and attention. Keep the input data, GPU and driver the same when you need bit-repeatable results. The release’s determinism contract explicitly excludes bit identity across vendors or drivers.
The example checks the stored float bits, not just rounded text. Two results can print the same decimal text while differing in their binary representation. BitConverter.SingleToInt32Bits exposes each float’s representation; comparing those integers checks what the example claims.
What does non-deterministic mean?
For this task, it means repeated computations with the same input can produce different output bits. That is a different question from whether both outputs are acceptably close to a reference result. A tolerance test can accept both while a bitwise comparison rejects them.
OneGpu’s default mode is Fast. The release documentation says a fast kernel may sum in any order and use fused multiply-add. Its deterministic mode fixes the reduction and accumulation order, avoids float atomics, and avoids choices that depend on occupancy or subgroup scheduling. It changes the execution rules of the supported kernels; it does not change float32 into exact real-number arithmetic.
Choose deterministic mode when a saved reference output or a repeatability check requires matching bits on the same environment. Use a numerical tolerance when the requirement is closeness to a mathematical reference rather than repeatability of one implementation. Keep those two checks separate in test output so a mismatch tells you which requirement failed.
How do I get deterministic GPU compute results in C#?
Create the deterministic kernel before encoding the work, as the snippet does. Reuse that kernel for both submissions, keep the operands unchanged, and wait for each readback before comparing the arrays. Setting a random seed would not configure this kernel’s accumulation order; the mode is an explicit constructor argument.
The sample’s matrices are deliberately small enough to inspect: the first output is 1 × 5 + 2 × 7, which is 19. The remaining cells are 22, 43 and 50. These inputs show the complete upload, submission and readback path without any external files.
For a larger workload, retain the bitwise check but use the application’s actual matrices. Record the package version, GPU and driver alongside reference outputs. After a driver change, establish the reference again rather than interpreting the old bit pattern as a portable specification. Other OneGpu kernel families give the same bits in both modes according to the release documentation; deterministic mode is not a separate cross-device numerical format.