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}