Hi all, I have a code where I need to perform a element by element multiplication of an Array of Arrays a of size X with an Array b of size X. Note that the inner arrays of a are all the same size. In other words, for every inner array of a, multiply element-wise by scalar b[x]. Using @code_warntype shows me that doing a .* b is type unstable within a larger code.
If I instead perform a two-level loop to perform the multiplication, then I don’t get a type unstable problem anymore. I can understand that maybe the compiler gets confused by the first way of writing, but then I cannot help it either because, as far as I know, you cannot pre-define the size of the inner arrays of an Array of Arrays, right?
A simple example to illustrate:
test1 = Array{Array{Float64,1},1}(undef, 10)
test2 = Array{Float64,1}(undef, 10)
for i = 1:10
test1[i] = rand(100)
test2[i] = rand()
end
function first_test(test1, test2)::Array{Array{Float64,1},1}
return test1 .* test2
end
function second_test(test1, test2)::Array{Array{Float64,1},1}
result = deepcopy(test1) # Have to deepcopy because there is no way to pre-define size of inner arrays
for i = 1:10
for k = 1:100
result[i][k] = test1[i][k] * test2[i]
end
end
return result
end
@code_warntype first_test(test1, test2)
@code_warntype second_test(test1, test2)
Do I have to rely on the two-level loop for these kind of operations? Is there another way to do this?
I’m surprised by the type-instability of the broadcasting operation here, but the performance of broadcasting still seems quite good. FWIW, a list comprehension works in this case as well and is type-stable (although it’s slightly slower):
julia> function first_test(test1, test2)
return test1 .* test2
end
first_test (generic function with 1 method)
julia> function list_comprehension(test1, test2)
[test1[i] * test2[i] for i in eachindex(test1)]
end
list_comprehension (generic function with 1 method)
julia> using BenchmarkTools
julia> @btime first_test($test1, $test2);
580.734 ns (13 allocations: 8.97 KiB)
julia> @btime list_comprehension($test1, $test2);
604.719 ns (13 allocations: 8.97 KiB)
test1 = Array{Array{Float64,1},1}(undef, 10)
test2 = Array{Float64,1}(undef, 10)
for i = 1:10
test1[i] = rand(100)
test2[i] = rand()
end
function first_test(test1, test2)::Array{Array{Float64,1},1}
return test1 .* test2
end
function second_test(test1, test2)::Array{Array{Float64,1},1}
return [test1[i] * test2[i] for i in eachindex(test1)]
end
In my case (Julia 1.3.1 on Windows 8.1), the list comprehension version is slightly faster: