Efficient conditional in-place assignment for arrays?

Is there any way to the get the efficient assignment of f1 with the nicer syntax of f2? Something like a view or conditional SubArray?

julia> function f1!(xs)
        for i in eachindex(xs)
         if xs[i] < 0.5
          xs[i] = 0.0
         end
        end
       end
f1! (generic function with 1 method)

julia> function f2!(xs)
        xs[xs .< 0.5] .= 0.0
       end
f2! (generic function with 1 method)

julia> v = rand(100);

julia> @btime f1!($v);
  121.872 ns (0 allocations: 0 bytes)

julia> @btime f2!($v);
  659.691 ns (3 allocations: 656 bytes)
julia> @btime f1!(v) setup=(v=rand(10^4))
  39.736 μs (0 allocations: 0 bytes)

julia> g!(v) = map!(v,v) do i
           ifelse(i<0.5, 0.0, i)
       end
g! (generic function with 1 method)

julia> @btime g!(v) setup=(v=rand(10^4))
  4.675 μs (0 allocations: 0 bytes)
2 Likes

Try replace!:

1.7.0> @btime g!(v) setup=(v=rand(10^4));
  6.120 μs (0 allocations: 0 bytes)

1.7.0> h!(v) = replace!(x->ifelse(x<0.5, zero(x), x), v)
h! (generic function with 1 method)

1.7.0> @btime h!(v) setup=(v=rand(10^4));
  1.190 μs (0 allocations: 0 bytes)
6 Likes

@DNF, we are used to call f2!() style, MATLAB-style. Would you know if MATLAB also implements your style of solution? Thanks.

Try @inbounds and @simd in your original f1!:

julia> function f1!(xs)
        @inbounds @simd for i in eachindex(xs)
         if xs[i] < 0.5
          xs[i] = 0.0
         end
        end
       end
f1! (generic function with 1 method)

julia> @btime f1!(v) setup=(v=rand(10^4))
  961.094 ns (0 allocations: 0 bytes)

On my computer this is equivalent to h! above of @DNF.

@Marco_Lombardi, @inbounds seems to have 40x speedup impact while @simd only 1%?

You don’t need @simd to get SIMD. The compiler decided to do it on its own, enabled by @inbounds. The @simd macro is redundant here.

2 Likes

There is a replace function in Matlab, but only for strings. I don’t know of any built-in functionality like this.

But now that Matlab code is jit-compiled, one can actually write a loop and test how well it does:

Warning: Matlab code! :skull_and_crossbones:

function v = replacev(v, lim)
    for ii = 1:length(v)
        val = v(ii);
        if val < lim
            v(ii) = 0;
        end
    end
end

Benchmark:

>> v = rand(10000, 1);

>> timeit(@()replacev(v, 0.5))
ans =
   6.9344e-05

70us is roughly half the speed of f1! without @inbounds. Not so bad actually, and almost as fast as the standard Matlab solution

v(v < lim) = 0;

which runs in 60us.

1 Like