In the REPL I was experimenting with a function that returns Vector{Any}, and I wanted to see how I could make Julia “automatically” change the type to a fixed type without having to specify it myself. I noticed that even though the end result is the same, the performance can vary widely depending on how I do it.
I found a simple example that shows an extreme difference: (I’ll suppress the outputs of the vectors because they’re long)
julia> s = Any[i for i in 1:10000]
# 3 different ways to do it:
julia> [i for i in s] == map(identity, s) == identity.(s)
true
#Now to time them:
julia> using BenchmarkTools
julia> @btime [i for i in s]
5.217 μs (5 allocations: 78.22 KiB)
10000-element Vector{Int64}:
julia> @btime map(identity, s)
5.493 μs (6 allocations: 78.23 KiB)
10000-element Vector{Int64}:
julia> @btime identity.(s)
107.125 μs (9502 allocations: 226.83 KiB)
10000-element Vector{Int64}:
Woah. The listcomp and the map are similar, but the broadcast is 19.5 times slower than the map.
Why is the broadcast so much slower here? (If it helps, I’m using Julia 1.8.5.)
Ah, so it seems to be fixed in 1.9 then - that’s good news! The differing number of allocations between the 3 methods is interesting though, wonder what causes that. And also it seems that listcomps still reign supreme for now.
Interesting, thanks - learning about some new functions here (hadn’t heard of isconcretetype before). I tried this out, and it works, but unfortunately it’s slower than the broadcast: