KernelAbstractions.jl icon indicating copy to clipboard operation
KernelAbstractions.jl copied to clipboard

`@Const` can lead to wrong results on CPU in Julia 1.11/1.12

Open maximilian-gelbrecht opened this issue 1 month ago • 10 comments

MWE below @vchuravy

Works in 1.10, but not in 1.11/1.12

"""
Minimal working example to demonstrate the difference between:
1. Using `array[I] +=` in a loop (BROKEN)
2. Using a local accumulator then `array[I] =` (WORKS)

This appears to be a bug in KernelAbstractions.jl or me misunderstanding how it handles
compound assignment operators on array elements within loops.
"""

using KernelAbstractions
using Test

# Version 1: Using += directly on array element (BROKEN)
@kernel function accumulate_broken!(output, input, @Const(n))
    I = @index(Global, Cartesian)
    i = I[1]
    j = I[2]
    
    # This should accumulate input[i, k] for k in j:n
    # But it produces WRONG results!
    for k in j:n
        output[I] += input[i, k]
    end
end

# Version 2: Using local accumulator (WORKS)
@kernel function accumulate_fixed!(output, input, @Const(n))
    I = @index(Global, Cartesian)
    i = I[1]
    j = I[2]
    
    # Use local accumulator
    sum_val = zero(eltype(output))
    for k in j:n
        sum_val += input[i, k]
    end
    output[I] = sum_val
end

# Test function
function test_kernel_bug()
    println("="^70)
    println("Minimal Kernel Bug Demonstration")
    println("="^70)
    
    # Setup test data
    n = 8
    m = 5
    
    input = Float32[i + k for i in 1:m, k in 1:n]
    output_broken = zeros(Float32, m, n)
    output_fixed = zeros(Float32, m, n)
    output_cpu = zeros(Float32, m, n)
    
    println("\nInput matrix ($(m)×$(n)):")
    display(input)
    println("\n")
    
    # CPU reference implementation
    println("Computing CPU reference...")
    for i in 1:m
        for j in 1:n
            for k in j:n
                output_cpu[i, j] += input[i, k]
            end
        end
    end
    
    # Version 1: Broken (using +=)
    println("Running BROKEN kernel (using output[I] += ...)...")
    backend = CPU()
    kernel_broken! = accumulate_broken!(backend)
    kernel_broken!(output_broken, input, n, ndrange=size(output_broken))
    KernelAbstractions.synchronize(backend)
    
    # Version 2: Fixed (using local accumulator)
    println("Running FIXED kernel (using local accumulator)...")
    kernel_fixed! = accumulate_fixed!(backend)
    kernel_fixed!(output_fixed, input, n, ndrange=size(output_fixed))
    KernelAbstractions.synchronize(backend)
    
    # Compare results
    println("\n" * "="^70)
    println("RESULTS")
    println("="^70)
    
    println("\nCPU Reference:")
    display(output_cpu)
    println("\n")
    
    println("\nBROKEN Kernel (output[I] +=):")
    display(output_broken)
    println("\n")
    
    println("\nFIXED Kernel (local accumulator):")
    display(output_fixed)
    println("\n")
    
    # Check if broken version is actually broken
    is_broken_wrong = !(output_broken ≈ output_cpu)
    is_fixed_correct = output_fixed ≈ output_cpu
    
    return (is_broken_wrong, is_fixed_correct)
end

# Run the test
if abspath(PROGRAM_FILE) == @__FILE__
    test_kernel_bug()
end

maximilian-gelbrecht avatar Nov 03 '25 16:11 maximilian-gelbrecht

@maximilian-gelbrecht can you share the operating system (if Linux, also the distribution) and CPU where you can reproduce this issue?

giordano avatar Nov 03 '25 17:11 giordano

MacOS 14.6.1 with M3

maximilian-gelbrecht avatar Nov 03 '25 17:11 maximilian-gelbrecht

@simone-silvestri is that ☝🏼 the reason why Oceananigans doesn't run on 1.11 or did you fix this problem already?

milankl avatar Nov 03 '25 18:11 milankl

Yes, it seems to be the same issue because the offending Ocenanigans example works with #653, but I could reproduce the error in Oceananigans only on Linux and only on some machines, never on macOS, that's why I was curious about where @maximilian-gelbrecht observed the issue.

giordano avatar Nov 03 '25 18:11 giordano

Yeah, this is the PR in question: https://github.com/CliMA/Oceananigans.jl/pull/4836. Now we have other problems 😅, but they popped up just toda,y so we are hopeful that this week we can get oceananigans on > 1.11

simone-silvestri avatar Nov 03 '25 19:11 simone-silvestri

MacOS always seemed to work for us

simone-silvestri avatar Nov 03 '25 19:11 simone-silvestri

Yeah, I didn't have problems with Oceananigans on M1 and M4, I tested on macOS 12.6 and 15.5

giordano avatar Nov 03 '25 19:11 giordano

I tagged a KernelAbstraction release, but importantly you can't use @Const. If you annotate a function with @Const it is still possible to hit this bug

vchuravy avatar Nov 03 '25 19:11 vchuravy

Great that the fix was so quick! @vchuravy Shall I close this issue or do you want to keep it open for related issues?

maximilian-gelbrecht avatar Nov 04 '25 07:11 maximilian-gelbrecht

It's sadly still broken if the user uses @Const, so let's leave it open.

vchuravy avatar Nov 19 '25 02:11 vchuravy