Typeof on Any vs Typeof on Union?

Hi everybody,

Is there a general explanation of why typeof comparisons on Any is 20 times slower than on Union in the following example ?
Especially, the huge memory allocation in one case but not in the other ?

Here is the output of the test:
First run ------------------------------

  • Typeof on union:
    1.301423 seconds
    Total=7898919599
  • Typeof on any:
    21.113947 seconds (657.92 M allocations: 16.339 GiB, 7.15% gc time, 0.05% compilation time)
    Total=7898919599

Second run ------------------------------

  • Typeof on union:
    1.055775 seconds (2 allocations: 32 bytes)
    Total=7898919599
  • Typeof on any:
    21.103012 seconds (657.92 M allocations: 16.339 GiB, 7.05% gc time)
    Total=7898919599

Here is the code:

struct Person
    name::String
    age::UInt8
end

struct Employee
    person::Person
    salary::Real
end

function sum_age(elements, repeat)
    total = 0
    for _ in 1:repeat, element in elements
        if typeof(element) === Person
            total += element.age
        elseif typeof(element) === Employee
            total += element.person.age
        end
    end
    return total
end

function test(size, repeat)
    person = Person("John", 41)
    employee = Employee(Person("Jane", 31), 1001.0)
    elements_any = Vector{Any}()
    elements_union = Vector{Union{Person,Employee}}()
    for _ in 1:size
        element = rand([person, employee])
        push!(elements_any, element)
        push!(elements_union, element)
    end

    println("First run ------------------------------")
    println("- Typeof on union:")
    @time total = sum_age(elements_union, repeat)
    println("  Total=", total)

    println("- Typeof on any:")
    @time total = sum_age(elements_any, repeat)
    println("  Total=", total)

    println()
    println("Second run ------------------------------")
    println("- Typeof on union:")
    @time total = sum_age(elements_union, repeat)
    println("  Total=", total)

    println("- Typeof on any:")
    @time total = sum_age(elements_any, repeat)
    println("  Total=", total)
end

test(123457, 1777)

FWIW, this may not answer your question exactly, but this pattern doesn’t have optimization support, as the expected way to write this is element isa Person

5 Likes

Useful advice @jameson
I will keep in mind to always prefer isa to typeof and === (am I right ?).
I checked that using isa in my example actually removes the issue and is faster.
Thanks

3 Likes

that is correct.

2 Likes