How does type annotation affect the performance?

I read that in Julia it’s better to allow more generic type annotation and that type annotation be it done or not does not have an impact on the performance. I think I might have misunderstood what I read. So I did a microbenchmark to see what is going on. I defined four functions in which I annotated the types of the inputs in three and did not annotate the type for the fourth function all doing the same thing. Consider the code below:

using BenchmarkTools

function test_number(x::Vector{Number})
    return sum(x)

function test_real(x::Vector{Real})
        return sum(x)
function test_float(x::Vector{Float64})
    return sum(x)

function test_notype(x)
    return sum(x)

x_number = ones(Number,100)
x_real = ones(Real,100)
x_float = ones(Float64, 100);

@btime test_number(x_number); takes 1.590 μs
@btime test_real(x_real); takes 1.589 μs
@btime test_float(x_float); takes 22.573 ns
@btime test_notype(x_number); takes 1.566 μs
@btime test_notype(x_real); takes 1.548 μs
@btime test_notype(x_float); takes 45.605 ns

  1. Why does test_float function is fastest among these if type annotation does not impact the performance?
    I had the impression that test_notype should have been as fast as test_float since I thought Julia itself “specialize” this function for float input, however, while test_notype is faster than test_number and test_real, it is not more performant than test_float.
  2. Why isn’t test_notype as fast as test_float for float input?
  3. When should I type annotate the function arguments for performance?
  1. The difference isn’t in the functions but in the data. If you do
function test_number(x::Vector{<:Number})
        return sum(x)

and call it with x_float you will get the same performance as test_float.

  1. this is benchmarking error. When benchmarking functions that take less than ~100ns you should interploate the arguments to @btime. (i.e. @btime test_notype($x_float)). Otherwise you will also be measuring the time to figure out the type of the global variable x_float.
  2. never.

When I call test_number on x_float that is test_number(x_float); I get a method error:
MethodError: no method matching test_number(::Vector{Float64})

I think the issue is Vector{Number} is not the same as Vector{Float64}, that is while Float64 is a number and so a sub-type of Number, this is not true for Vector{Number} and Vector{Float64}.

Note the difference between Vector{<:Number} and Vector{Number}. Types in julia are invariant. Vector{Float64}<:Vector{<:Number}

1 Like

As complement to what has been said here, attention that type annotation is particularly important for performances for struct fields.


See also the section on argument-type declarations in the manual.