I’m referring to the simplest case where
broadcast are equivalent, that is – given a vector/tuple, I want to map a function over it to obtain another container.
broadcast seems a natural choice here, and
map seems more suited to generic iterables that don’t support indexing.
However, we look at the performance in cases where the element types may not be inferred concretely, in a simple case of computing the
sizes along each dimension of an array, given the
julia> x = (1:3, 1:3); y = [x]; julia> @btime (x -> map(length, x))($y); 3.368 ns (0 allocations: 0 bytes) julia> @btime (x -> length.(x))($y); 3.368 ns (0 allocations: 0 bytes)
So, in type-stable cases, these two are equivalent. However, here’s an instance where types aren’t inferred:
julia> x = (1:3, 1:3); y = Any[x]; julia> @btime (x -> map(length, x))($y); 23.183 ns (1 allocation: 32 bytes) julia> @btime (x -> length.(x))($y); 478.985 ns (3 allocations: 128 bytes)
In such cases,
map may be significantly faster. To me, this seems to suggest that one should choose
map over broadcasting wherever these are equivalent.
For anyone wondering if there are cases where such an operation wouldn’t be concretely inferred: yes, there are.
ApproxFun is littered with such cases, since it deals with both finite and infinite matrices, often in a type-unstable manner.
I’ve submitted one such PR to
FillArrays.jl recently. I wonder if this is an intrinsic limitation because
broadcast is a more complicated operation, in which case developers should be encouraged to prefer
map if that’s an option?