Understanding @report_code output

Hi there,

This is my first time using JET.jl to fix the type-instability issues of my code. I am aware that maybe asking without a reproducible example might be a stretch but I will try anyways.

My function takes as an argument an SVector{9, Float64} labeled as uJ_grid then in the loop seen in the screenshot below it gets added a number and therefore the new biased_uJ is also an SVector{9, Float64}. This static array gets broadcasted by one of the functions returned by:

initial_bank_cdf::Vector{Function} = [x -> 1.0 - hermite5_cdf(params[a], initial_bank_ÎŒ, initial_bank_σ)(-overall_scale * (x + scaled_Îș)) for a in param_indices.bank_params]

This screenshot summarizes the above:

And I get the following possible error regarding type instability:

Any help to understand the message and how to fix it would be greatly appreciated. Thanks in advance!

Not sure if it’s the entire reason because this isn’t a MWE, but indexing a Vector{Function} and involving the inferred-Function element in a call requires a runtime dispatch because Function is abstract. If you don’t need to add or change elements to initial_band_cdf, you might improve the situation if you remove the ::Vector{Function} annotation and let the array literal try to make a concrete element type. That said, full inferrability would also depend on the captured variables not being reassigned.

Don’t post screenshots of code, paste text between 2 lines of triple-backticks (```), it’s more readable. If you think it’s too long, then put it in a Hide Details block (click the settings gear icon in the comment’s interface to see the option).

1 Like

Thanks a lot for your answer. It worked! Given my low experience with Julia and computing in general let me ask two quick follow up questions to try and understand fully what you mean:

  1. Without the annotation, the compiler can try and infer the type of the return based on the array literal but the annotation prevents it? My current understanding is that the annotation invokes a type check at run time giving an error if the type ends up not being Function but why is this making a difference with respect to letting the compiler go beyond this?

  2. “That said, full inferrability would also depend on the captured variables not being reassigned” I did not understand this, could you please add a bit of verbosity?

Thanks a lot in advance, and sorry for the questions. Just trying to improve my Julia understanding.

I should first mention that the typical way to annotate an element type is in array literal syntax like initial_bank_cdf = Function[...]. That would be directly involved in the instantiation. What initial_bank_cdf::Vector{Function} does is force all assignments to that variable to do the conversion and assert the conversion worked; if the instance was already the type it would skip the instantiation. In your case, you made a vector with a more specific type but then instantiated a Vector{Function} copy, which is probably wasteful.

Given no annotation, the element type is figured out in an inner function call, and it could be handled at compile-time given the right circumstances. Given an annotation or elements of all the same type, it’s handled by dispatch, which could also be handled at compile-time. An annotation is lowered to providing the type as an argument to a function call; the compiler can’t optimize away from your choice. That has its uses, now you can setindex! or push! with arbitrary Function subtypes, with the obvious cost of performance due to runtime dispatch.

Performance of captured variable - The Julia Language
While there are possible improvements to the situation, don’t expect it to go as far as some commenters might claim. There are fundamental limitations to call-wise type inference, capturing variables that are reassignable everywhere, and methods sharing instances.

1 Like