Look at these functions:
function fA1(s::AbstractString, n)
T = getfield(Base, Symbol(s))
x = one(T)
for i in 1:n
x = x+x
end
return x
end
function fA2(s::AbstractString, n)
T = getfield(Base, Symbol(s))
x = one(Float64)
for i in 1:n
x = x+x
end
return x
end
function fB1(s::AbstractString, n)
T = getfield(Base, Symbol(s))
x = one(T)
[x = x+x for i in 1:n]
return x
end
function fB2(s::AbstractString, n)
T = getfield(Base, Symbol(s))
x = one(Float64)
[x = x+x for i in 1:n]
return x
end
fA1
is type-unstable and slow, fA2
is type-stable and fast.
When I however move the for
loop as a list-comprehension, both fB1
and fB2
are type-unstable and slow, while the numerical result remains (obviously) the same.
Why is that ?
The reason is explained here in the Julia manual.
It is important to know that comprehension creates a a new local scope as is explained here in the Julia manual.
In this case, if you stick to updating a variable from outer scope inside a comprehension (which in general is not recommended as it is typically confusing for people that read such code), the best you can do as far as I know (maybe someone can come up with a better solution but I think this is unlikely given the current state of the Julia compiler) is to use type annotation:
This will not avoid boxing, but should make the return type inferrable and the performance should significantly improve.
In the future is is very likely that the Julia compiler will be smart enough to handle such code without requiring type annotation.
Here is a performance comparison: