marnix
marnix

Reputation: 1172

Benchmarking conditional assignments to an Array in Julia

I'm new to Julia and I was testing my understanding by the following benchmarks of three equivalent ways to set all elements of an Array smaller than 0.5 to 0.:

using BenchmarkTools

function test!(A)
    @btime begin # method 1
        mask = $A .< 0.5
        $A[mask] .= 0.
    end
    @btime begin # method 2
        $A[$A .< 0.5] .= 0.
    end
    @btime begin # method 3
        @inbounds begin
            for i in eachindex($A)
                if $A[i] < 0.5
                    $A[i] = 0.
                end
            end
        end
    end
end
n = 1000
test!(rand(n,n))

This outputs

  1.612 ms (13 allocations: 3.94 MiB)
  1.619 ms (13 allocations: 3.94 MiB)
  4.215 ms (0 allocations: 0 bytes)

Based on what I have read about Julia until now I have several questions:

  1. Why is method 3 the slowest? I'd expect it to be the fastest, since it avoids any heap allocations. I tried to look at the LLVM/native code output but that went over my head.
  2. Why are method 1 and 2 equally fast? For me method 1 is the "Python/Numpy method", where allocating an intermediate array for storing the mask is a typical (unwanted) byproduct of vectorized Numpy programming. I had expected Julia to optimize method 2 into method 3 through the . operator.
  3. The expected behavior is to have no allocations and near-C fast performance, preferably without an explicit for-loop. How can I achieve this?

Thank you for your time.

Upvotes: 3

Views: 116

Answers (1)

Oscar Smith
Oscar Smith

Reputation: 6398

Your benchmark is kind of weird. If you instead define 3 functions like so,

function f1(A)
       mask = A .< .5
       A[mask] .= 0
end
function f2(A)
    A[A .< .5] .= 0.
end
function f3(A)
    @inbounds for i in eachindex(A)
        if A[i] < .5
            A[i] = 0.
        end
    end
end

and @btime them, I get that f3 is about 2x faster.

Upvotes: 4

Related Questions