 # Type stability: ProfileView and @code_warntype don't agree

I have some code that I feel should run faster (see below). So I ran it with `@profile` and looked at the results with `ProfileView.view()` and as expected a lot of red showed up (i.e. runtime method lookup), the correspinding lines are commented in the code below. But when I run `@code_warntype` on the same function I get no red and it seems, all the code is nice and type stable. As far as I see `ΔE` is type stable and returns exactly what the annotation says, so why does it still get flagged as needing runtime method lookup?
And how do correctly annotate / write the code, such that no runtime method lookup is needed.

``````up = CartesianIndex(0, 1)
right  = CartesianIndex(1, 0)
down = CartesianIndex(0, -1)
left = CartesianIndex(-1, 0)

"Calculate the total energy of 2D Ising model with coupling J and field H"
function energy(lattice, J, H)::Float64
energy = 0.0
for i in eachindex(lattice)
energy -= H*lattice[i]*(1. + J * lattice[i+up] + J * lattice[i+right])
end
return energy
end

"Calculate the total magnetization of the lattice"
magnetization(lattice)::Int = sum(lattice)

"The energy difference when flipping the spin on `site`"
@inline @propagate_inbounds function ΔE(lattice, site, J, H)::Float64
out = (  2H*lattice[site]   # runtime method lookup here
+ 2J*lattice[site]*(lattice[site+up] + lattice[site+down]
+ lattice[site+left] + lattice[site+right]))
out
end

"Make a Markov Chain Monte Carlo Step on the lattice"
function MCMCStep!(lattice,
J,
H)::Tuple{Float64,Int}
site = rand(CartesianIndices(lattice))
@inbounds δE = ΔE(lattice, site, J, H)  # runtime method lookup here
if δE < 0.0
@inbounds δM = -lattice[site]
@inbounds lattice[site] = δM
elseif exp(-δE) > rand()
@inbounds δM = -lattice[site]
@inbounds lattice[site] = δM  # runtime method lookup
else
δE = 0.0::Float64
δM = 0::Int
end
return (δE, 2*δM)   #this line gets flagged as slow
end

function MCMCRun(lattice, steps, J, H) where {N}
E0 = energy(lattice, J, H)
M0 = magnetization(lattice)
E = E0
M = M0
energies = Array{Float64}(undef, steps)
magnetizations = Array{Int64}(undef, steps)
for i in 1:steps
for j in 1:length(lattice)
δE, δM = MCMCStep!(lattice, J, H)  # this is where all the time is spent with a runtime method lookup
E += δE
M += δM
end
energies[i] = E
magnetizations[i] = M
end
E1 = energy(lattice, J, H)
M1 = magnetization(lattice)
return energies, magnetizations
end
``````

You’re using non-constant globals, the first performance gotcha: https://docs.julialang.org/en/latest/manual/performance-tips/#Avoid-global-variables-1

Try

``````const up = CartesianIndex(0, 1)
const right  = CartesianIndex(1, 0)
const down = CartesianIndex(0, -1)
const left = CartesianIndex(-1, 0)
``````
2 Likes

Oops, changed that now. But the main problem, that I spent most of my time in the last line of `MCMCStep!` (so at the return statement) persists…

It’s less obvious now. You’ll have to provide more than just the code, give enough that someone else can run this.

1 Like

Yup, turns out the problem was somewhere else altogether… My had used a Custom Array Type for the `lattice` whose underlying data I didn’t annotate correctly.