Code in unexecuted if statement causes slowdown in other function

I’m making a Monte Carlo simulation program. The object my simulation is working on is a graph, which I represent by struct

mutable struct IsingGraph
   state::Vector{Int8}
   adj::Vector{Vector{Int32}}
   ...
   eprops::Dict{Edge,Dict{Symbol,Any}}
end

The field state holds a -1 or 1, and the field adj is a vector, which maps every index of the state vector to a few other indices. The dict props is used to just hold weights.

I have a function which gets the energy for an index. I’m trying to include optional weights, and was trying to make a new function to be able to do this. These are the two functions:

function getH(g,idx)
    Estate = 0

    for jdx in g.adj[idx]
        @inbounds Estate += -g.state[idx]*g.state[jdx]
    end

    return Estate
end

function getH(g::IsingGraph, idx::Integer ; weighted::Bool = false)
    @inbounds conns = g.adj[idx]
    E = 0
    @inbounds state = g.state[idx]
    if !weighted
        for conn in conns
            @inbounds E += -state*g.state[conn]
        end
    else
        for conn in conns
            E += -g.eprops[idx=>conn][:weight] * state*g.state[conn]
        end
    end

    return E
end

If I execute either of these with weighted = false for getH, I get about 3.6ns of execution time. However, my simulation is ran by the following function

function updateMonteCarlo!(g::IsingGraph, T)

    beta = T>0 ? 1/T : Inf

    idx = rand(ising_it(g))

    Estate = getH(g,idx) # /getE(g, idx)

    if (Estate >= 0 || rand() < exp(2*beta*Estate))
        @inbounds g.state[idx] *= -1
    end
    
end

Here, depending on if I use getH or getE, the execution time of updateMonteCarlo! will be around 150ns, or 50ns respectively. I found out that if I remove the [:weight] part from the line (causing the line to not make sense, which doesn’t matter for execution since it is not executed), the execution time is about 50-60ns again.

Why is getH so much slower in this case? I’m assuming the compiler is failing to make some optimisation somewhere. Does it have anything to do with the fact that the type of E can potentially change, because the return type of the dict is any?

You are correct on the general problem: type instability in E due to the return type being Any (propagating from the Dict{Symbol,Any}). @code_warntype (and friends) will give a very clear picture of this.

If possible, consider whether the Any could not be some concrete subtype of Number.