Suggestions on code-style?

Hello,

Below are two functions that do the same thing and I was wondering what is the more appropriate julian way to write and allocate output within these functions. In the second function, the only difference is that I am using knowledge of the output dimensions of intermediaries (A_tmp, Z_tmp, x and y) to pre-initialize the arrays of the appropriate sizes whereas I am not doing that in the first.

function f(App::Array{T}, Offer::Array{T}, D::Array{T}, pie::Array{S},
           N::T, J::T, K::T, L::T) where {T} where{S}

  A_tmp = permutedims(App) .* permutedims(D)
  Z_tmp = permutedims(Offer) .* permutedims(D)
  x = permutedims(pie.*Offer) .* permutedims(D)
  y = permutedims((1 .- pie).*(1 .- Offer)) .* permutedims(D)
  res = @view (prod((A_tmp .* (x .+ y)) .+ (1 .- A_tmp) .* (1 .- Z_tmp), dims = 1))[1, :, :, :]
  return res
end

function f_new(App::Array{T}, Offer::Array{T}, D::Array{T}, pie::Array{S},
               N::T, J::T, K::T, L::T) where {T} where{S}

  A_tmp = Array{T}(undef, J, L)
  Z_tmp = Array{T}(undef, J, L)
  x = Array{S}(undef, J, L)
  y = Array{S}(undef, J, L)
  res = Array{S}(undef, L, 1)

  A_tmp .= permutedims(App) .* permutedims(D) # J x L
  Z_tmp .= permutedims(Offer) .* permutedims(D) # J x L
  x .= permutedims(pie.*Offer) .* permutedims(D) # J x L
  y .= permutedims((1 .- pie).*(1 .- Offer)) .* permutedims(D) # J x L
  res .= @view (prod((A_tmp .* (x .+ y)) .+ (1 .- A_tmp) .* (1 .- Z_tmp), dims = 1))[1, :, :, :] # L x 1 (product is taken across j = 1, ..., J)
  return res
end


The first is better.

The only reason to preallocate is if you are doing things e.g in a loop.
Where you can thus avoid multple allocations.

Preallocating and only using once, just makes the code harder to read,
and maybe marginally slower.

2 Likes

Thanks for the reply, although ultimately f_new will be nested in a for loop (which I’ll parallelize over). So, there will be multiple calls to f_new, once for each index in the outer for loop.