There some things that are a little strange in this MWE.
The vector A simply cannot have -Inf, Inf, or NaN. So you are always just testing how a pass that do not replace anything should go.
The 0 literal is interpreted as Int (which is an alias for either Int32 or Int64 in your system), so there is probably an automatic conversion (maybe optimized away) to the 0.0, otherwise replace! should not work, as it changes the vector in place and you cannot save a Int in a Vector{Float64} without converting.
rand returns a Vector{Float64} such type simply cannot have missing values, another red flag is the number of allocations, there is no reason for replace! to allocate anything, I believe what is happening here is that something inside replace! became type-unstable because of the strange types in the pairs, ideally all pairs should have a Float64 in the right and the left side, unless your vector is a Vector{Float64,Missing}.
I guess, without checking, that the Dict that is being created by replace from the arguments is a Dict{Any,Any} when the missing is present in the example.
Probably you won’t get allocations if using interpolations:
The problem seems to be a type-unstable for-loop inside Base.replace_pairs!. Using recursion instead of a loop solves the problem for this case.
julia> _new(x) = x
_new (generic function with 1 method)
julia> _new(x, p, ps...) = isequal(first(p), x) ? last(p) : _new(x, ps...)
_new (generic function with 2 methods)
julia> function Base.replace_pairs!(res, A, count::Int, old_new::Tuple{Vararg{Pair}})
Base._replace!(res, A, count) do x
_new(x, old_new...)
end
end
julia> @time replace!(A, -Inf => 0, Inf => 0, missing => 0, NaN => 0);
0.074939 seconds (68.23 k allocations: 3.670 MiB, 31.72% compilation time)