My mental model of how Julia handles dynamic dispatch is by compiling code that looks like:
for ele in vector_of_abstract_type
do_something(ele)
end
to something that looks like this
for ele in vector_of_abstract_type
type_of_ele = ele.type # inspect some type information stored with the object
if type_of_ele in do_something_method_table
do_something_method_specialization = do_something_method_table[type_of_ele] # lookup method specialization
do_something_method_specialization(ele) # jump to already compiled code machine code
else
do_something_function_specialization = determine_method(do_something, type_of_ele) # determine which method of the do_something_function to use
do_something_method_specialization = compile(do_something_function_specialization, type_of_ele) # compile a specialization of the method based on type_of_ele
do_something_method_table[type_of_ele] = do_something_method_specialization # store a reference to the final compiled machine code
do_something_method_specialization(ele) # jump to the machine code
end
end
Basically the cost of dynamic dispatch according to this is the same as a single hash table lookup if the specialization has already been compiled. Else it requires determining the method to use and then compiling that method for our specific type.
I differentiate method speicalizations (raw machine code) from function specializations (which method of a function is called).
1.) Is this what actually happens? Does the hash table map the types of the inputs to the method (function specialization) or the final compiled machine code location (method speicalization)? In my understanding above it was the latter.
2.) If do_something is roughly 10x the cost of a dictionary lookup then can we ignore the cost of dynamic dispatch (given that the type instability doesn’t propagate to other parts of the code)? Is dictionary lookup a good baseline cost comparison?
3.) If we know that the return type of do_something is the same, no matter what the type of ele is, can we assume that the compiler also knows? Can we use a return value in this case in a type stable way? Or should we put a type assertion? If we put a type assertion will the compiler be able to continue in a type stable way with that return value?
4.) If do_something adds a new method to do_something then will the hash table be emptied out? Thus causing the else branch to execute every time leading to very slow code?
5.) Kind of unrelated, but does the compiler compile as deep as it can until it hits a value with a type it cannot determine at compile time, insert a callback to itself there, unwind and execute? Or does it just compile the local function and at any function call point insert a callback to itself, followed by an overwriting of that callback once it compiles the callee?
6.) Does Julia store some type identifying information with every piece of data?