For example:
foo(x::Real) = x ^ 2
bar(x::R) where {R<:Real} = x ^ 2
Both seem to have similar performance, which seems to imply that the compiler is creating specialized code for, e.g., Int
vs Float64
. So is the only difference that in the latter, R
can be used/referenced?
The two are the same:
julia> foo(x::Real) = x ^ 2
foo (generic function with 1 method)
julia> bar(x::R) where {R <: Real} = x ^ 2
bar (generic function with 1 method)
julia> code_lowered
code_lowered (generic function with 2 methods)
julia> code_lowered(foo)
1-element Vector{Core.CodeInfo}:
CodeInfo(
1 ─ %1 = Main.:^
│ %2 = Core.apply_type(Base.Val, 2)
│ %3 = (%2)()
│ %4 = Base.literal_pow(%1, x, %3)
└── return %4
)
julia> code_lowered(bar)
1-element Vector{Core.CodeInfo}:
CodeInfo(
1 ─ %1 = Main.:^
│ %2 = Core.apply_type(Base.Val, 2)
│ %3 = (%2)()
│ %4 = Base.literal_pow(%1, x, %3)
└── return %4
)
The difference in fact is none in the code initially generated, even if you take out the restriction to Real:
julia> baz(x) = x ^ 2
baz (generic function with 1 method)
julia> code_lowered(baz)
1-element Vector{Core.CodeInfo}:
CodeInfo(
1 ─ %1 = Main.:^
│ %2 = Core.apply_type(Base.Val, 2)
│ %3 = (%2)()
│ %4 = Base.literal_pow(%1, x, %3)
└── return %4
)
However, there will be a difference in the just-in-time compilation. If you supply a String argument, only baz("abc")
will work -- the other two will not pass the JIT step because "abc" is not a Real
.
TLDNR: The difference in foo and bar is only in the syntax.