How to satisfy the requirement of LoopVectorization argument check?

I want to learn how to use LoopVectorization. It is known that LoopVectorization cannot deal with complex number directly, and it need StructArray, then I write a simple loop for practicing. However there are some errors:

Start
val=StructArray(0.0+0.0*im);
rd=rand(100);
function f(v)
@turbo for n in 1:100
        v+=StructArray(1.0+im*0.1 - rd[n]);
        end
    v
end
@time f(val)
0.029814 seconds (71.45 k allocations: 3.591 MiB, 97.51% compilation time)
Warning: #= In[6]:2 =#:
│ `LoopVectorization.check_args` on your inputs failed; running fallback `@inbounds @fastmath` loop instead.
│ Use `warn_check_args=false`, e.g. `@turbo warn_check_args=false ...`, to disable this warning.
└ @ Main C:\Users\.julia\packages\LoopVectorization\FMfT8\src\condense_loopset.jl:1049
End

The error says that argument check is not pass. To solve this, I make a little modifications on loop:

function f1(val2)
@turbo for n in 1:100
        v=val2;
        val2 =v+StructArray(1.0+im*0.1 - rd[n])
        end
end
@time f1(val)
0.034055 seconds (70.56 k allocations: 3.684 MiB, 99.82% compilation time)

function f2(v)
@turbo for n in 1:1000
        add=StructArray(1.0+im*0.1 - rd[n]);
        v=v+add;
        end
    v
end
@time f2(val)
0.029021 seconds (71.50 k allocations: 3.598 MiB, 97.83% compilation time)
┌ Warning: #= In[7]:2 =#:
│ `LoopVectorization.check_args` on your inputs failed; running fallback `@inbounds @fastmath` loop instead.
│ Use `warn_check_args=false`, e.g. `@turbo warn_check_args=false ...`, to disable this warning.
└ @ Main C:\Users\.julia\packages\LoopVectorization\FMfT8\src\condense_loopset.jl:1049

In f1, LoopVectorization doesn’t throw error, however all three have the same running time. How could I really solve this and how to use LoopVectorization generally?

As far as I understand, StructArrays will convert a vector of complex numbers into two vectors, one for the real part other for the imaginary part. To vectorize, I think you must (unless some macro within LoopVectorization is smart enough to do that automatically), operate on each of these vectors independently. For example:

julia> using LoopVectorization

julia> x = StructArray([rand(ComplexF64) for _ in 1:100]);

julia> @turbo for i in eachindex(x)
           x.re[i] += rand()
           x.im[i] += rand()
       end

(I don’t really understand the use of StructArray if the input is a scalar. I think that only defines the type of element that the arrays will contain, and you are not really doing anything useful there in your example by adding or subtracting values from StructArray(1.0 + 1.0im)).

1 Like

thx. Your suggestion works.
What’s the difference between x.re[i] and real(x)[i]? Both of them can take real part and pick number i element, but only the first works in loop.

real(x) will return an array with the real part of every element. The equivalent of x.re[i] would be real(x[i]).

But x.re[i] = rand() is a setindex! operation, which means that it is assigning to x.re[i] a new value.

This is specific of how StructArrays work. The vector of complex numbers is converted to a structure that contains two vectors, one for the real part and one for the imaginary part of those numbers. x.re is the internal field of that structure that is the array of the real parts, and x.im is the internal field of that structure that contains the vector of the imaginary parts.

x.re and x.im are just to standard vectors or floats, and that is why you can use them without any problem for vectorizing the operations that act on them.

1 Like