The performance difference between expfunc1! and expfunc2! is arises due to the vectorization of random number generation, which currently only occurs in calls to rand!. This can be demonstrated by calling @code_typed on each of the functions. Or, to remove the extraneous details introduced by exp, see below.
julia> function randeach!(x)
@inbounds for i β eachindex(x)
x[i] = rand()
end
x
end;
Then @code_typed randeach!(v)
julia> @code_typed randeach!(v)
CodeInfo(
1 ββ %1 = Base.arraysize(x, 1)::Int64
β %2 = Base.slt_int(%1, 0)::Bool
β %3 = Core.ifelse::typeof(Core.ifelse)
β %4 = (%3)(%2, 0, %1)::Int64
β %5 = Base.slt_int(%4, 1)::Bool
ββββ goto #3 if not %5
2 ββ goto #4
3 ββ goto #4
4 ββ %9 = Ο (#2 => true, #3 => false)::Bool
β %10 = Ο (#3 => 1)::Int64
β %11 = Ο (#3 => 1)::Int64
β %12 = Base.not_int(%9)::Bool
ββββ goto #10 if not %12
5 ββ %14 = Ο (#4 => %10, #9 => %66)::Int64
β %15 = Ο (#4 => %11, #9 => %67)::Int64
β %16 = $(Expr(:foreigncall, :(:jl_get_current_task), Ref{Task}, svec(), 0, :(:ccall)))::Task
β %17 = Base.getfield(%16, :rngState0)::UInt64
β %18 = Base.getfield(%16, :rngState1)::UInt64
β %19 = Base.getfield(%16, :rngState2)::UInt64
β %20 = Base.getfield(%16, :rngState3)::UInt64
β %21 = Base.add_int(%17, %20)::UInt64
β %22 = Base.shl_int(%21, 0x0000000000000017)::UInt64
β %23 = Base.lshr_int(%21, 0xffffffffffffffe9)::UInt64
β %24 = Core.ifelse::typeof(Core.ifelse)
β %25 = (%24)(true, %22, %23)::UInt64
β %26 = Base.lshr_int(%21, 0x0000000000000029)::UInt64
β %27 = Base.shl_int(%21, 0xffffffffffffffd7)::UInt64
β %28 = Core.ifelse::typeof(Core.ifelse)
β %29 = (%28)(true, %26, %27)::UInt64
β %30 = Base.or_int(%25, %29)::UInt64
β %31 = Base.add_int(%30, %17)::UInt64
β %32 = Base.shl_int(%18, 0x0000000000000011)::UInt64
β %33 = Base.lshr_int(%18, 0xffffffffffffffef)::UInt64
β %34 = Core.ifelse::typeof(Core.ifelse)
β %35 = (%34)(true, %32, %33)::UInt64
β %36 = Base.xor_int(%19, %17)::UInt64
β %37 = Base.xor_int(%20, %18)::UInt64
β %38 = Base.xor_int(%18, %36)::UInt64
β %39 = Base.xor_int(%17, %37)::UInt64
β %40 = Base.xor_int(%36, %35)::UInt64
β %41 = Base.shl_int(%37, 0x000000000000002d)::UInt64
β %42 = Base.lshr_int(%37, 0xffffffffffffffd3)::UInt64
β %43 = Core.ifelse::typeof(Core.ifelse)
β %44 = (%43)(true, %41, %42)::UInt64
β %45 = Base.lshr_int(%37, 0x0000000000000013)::UInt64
β %46 = Base.shl_int(%37, 0xffffffffffffffed)::UInt64
β %47 = Core.ifelse::typeof(Core.ifelse)
β %48 = (%47)(true, %45, %46)::UInt64
β %49 = Base.or_int(%44, %48)::UInt64
β Base.setfield!(%16, :rngState0, %39)::UInt64
β Base.setfield!(%16, :rngState1, %38)::UInt64
β Base.setfield!(%16, :rngState2, %40)::UInt64
β Base.setfield!(%16, :rngState3, %49)::UInt64
β %54 = Base.lshr_int(%31, 0x000000000000000b)::UInt64
β %55 = Base.shl_int(%31, 0xfffffffffffffff5)::UInt64
β %56 = Core.ifelse::typeof(Core.ifelse)
β %57 = (%56)(true, %54, %55)::UInt64
β %58 = Base.uitofp(Float64, %57)::Float64
β %59 = Base.mul_float(%58, 1.1102230246251565e-16)::Float64
β Base.arrayset(false, x, %59, %14)::Vector{Float64}
β %61 = (%15 === %4)::Bool
ββββ goto #7 if not %61
6 ββ goto #8
7 ββ %64 = Base.add_int(%15, 1)::Int64
ββββ goto #8
8 ββ %66 = Ο (#7 => %64)::Int64
β %67 = Ο (#7 => %64)::Int64
β %68 = Ο (#6 => true, #7 => false)::Bool
β %69 = Base.not_int(%68)::Bool
ββββ goto #10 if not %69
9 ββ goto #5
10 β return x
) => Vector{Float64}
And @code_typed rand!(v)
julia> @code_typed rand!(v)
CodeInfo(
1 β %1 = $(Expr(:gc_preserve_begin, Core.Argument(2)))
β %2 = $(Expr(:foreigncall, :(:jl_array_ptr), Ptr{Float64}, svec(Any), 0, :(:ccall), Core.Argument(2)))::Ptr{Float64}
β %3 = Base.bitcast(Ptr{UInt8}, %2)::Ptr{UInt8}
β %4 = Base.arraylen(A)::Int64
β %5 = Base.mul_int(%4, 8)::Int64
β %6 = Random.XoshiroSimd.Float64::Type{Float64}
β %7 = Random.XoshiroSimd._bits2float::typeof(Random.XoshiroSimd._bits2float)
β %8 = Base.sle_int(64, %5)::Bool
βββ goto #3 if not %8
2 β %10 = invoke Random.XoshiroSimd.xoshiro_bulk_simd($(QuoteNode(TaskLocalRNG()))::TaskLocalRNG, %3::Ptr{UInt8}, %5::Int64, %6::Type{Float64}, $(QuoteNode(Val{8}()))::Val{8}, %7::typeof(Random.XoshiroSimd._bits2float))::Int64
β %11 = Base.sub_int(%5, %10)::Int64
β %12 = Core.bitcast(Core.UInt, %3)::UInt64
β %13 = Base.bitcast(UInt64, %10)::UInt64
β %14 = Base.add_ptr(%12, %13)::UInt64
βββ %15 = Core.bitcast(Ptr{UInt8}, %14)::Ptr{UInt8}
3 β %16 = Ο (#2 => %15, #1 => %3)::Ptr{UInt8}
β %17 = Ο (#2 => %11, #1 => %5)::Int64
β %18 = (%17 === 0)::Bool
β %19 = Base.not_int(%18)::Bool
βββ goto #5 if not %19
4 β invoke Random.XoshiroSimd.xoshiro_bulk_nosimd($(QuoteNode(TaskLocalRNG()))::TaskLocalRNG, %16::Ptr{UInt8}, %17::Int64, %6::Type{Float64}, %7::typeof(Random.XoshiroSimd._bits2float))::Any
5 β goto #6
6 β $(Expr(:gc_preserve_end, :(%1)))
βββ goto #7
7 β goto #8
8 β goto #9
9 β return A
) => Vector{Float64}