the various distanceToOut and distanceToIn which takes different types, and often call these function with different types (because geometry can nest within each other), is what we think the problem is at
and seems to be a fundamental limitation of Julia types at this point
Dynamic dispatch is slow in Julia — slower than C++ virtual methods, because C++ method dispatch only depends on a single object type (this) and hence can use vtable lookup.
High-performance code in Julia always relies on devirtualizing critical code. This also means that you can’t easily write performant geometry code in Julia by having an array of geometric objects of different types and relying on dynamic dispatch to execute different methods (determined at runtime) for different objects — you need to implement a different dispatch strategy.
It’s good to have this confirmed, but while the topic was “when # of types increases”, I’m curious, 1) is it for sure slower when there’s actually only one type involved. And 2) when 2+ for one or more extra type, do you think it gets rapidly slower? [I’m not too concerned about that vs C++, since multiple dispatch is more powerful than C++, and I’m not sure C++ has anything to emulate it faster (or slower or at all), though Stroustrup had a, not yet implemented, proposal.]
I’m guessing Julia is optimized for the multiple case, not single, so Julia doesn’t use a vtable even if it could sometimes? So what’s the alternative in the single case? I think basically a big old (C-like) switch (and @goto only as a macro, maybe vtable could be built?).
Except Julia doesn’t have switch (yet) either, but a big if-else should be as fast:
Julia neither has pattern matching built in (like Scala, and recently added to Python and Java), but it’s available with e.g. this package:
Do you know if that is the main package, or at least if it (or any of the many alternatives) are as fast as C’s switch?
I have hoped that I can get away with something like:
function distanceToIn(shape, point, dir)
if shape isa Trap
distanceToIn_trap(shape, point, dir)
elseif shape isa Trd
distanceToIn_trd(shape, point, dir)
elseif shape isa Cone
distanceToIn_cone(shape, point, dir)
elseif shape isa Box
distanceToIn_box(shape, point, dir)
elseif shape isa Tube
distanceToIn_tube(shape, point, dir)
elseif shape isa Volume
distanceToIn_volume(shape, point, dir)
elseif shape isa Polycone
distanceToIn_polycone(shape, point, dir)
elseif shape isa CutTube
distanceToIn_cuttube(shape, point, dir)
elseif shape isa Boolean
distanceToIn_boolean(shape, point, dir)
but it made no difference. The example you linked seems to suggest this has to go inside the loop directly?
by annotating the individual function with types, I’m able to recover the original performance (i.e. much slower than C++) and I feel like essentially I have re-created how Julia does things but manually and with no performance improvement
Have you tried out using GitHub - YingboMa/Unityper.jl ? It has some annoying ergonomics like requiring a default keword argument field for every struct, but it is designed to solve this exact performance problem you’re having.