I know this wasn't the point of your post, but just noting that adding types to function arguments "usually" shouldn't impact performance at all :) when it does, that might mean there is some type-instability being papered over (i.e. there is a better fix lurking around the corner)
I didn't do a control test to figure out exactly why, but I think it was having some issue with a parsed CSV being read as a matrix. I think explicitly putting that requirement into the function signature allowed for a lot of low-level optimizations to be used that it couldn't use before.
I'm not 100% sure though; basically when I was getting some performance bottlenecks I did some preliminary searching and some of the results said "try adding types to your function signatures", and I really couldn't think of a good reason not to add them for most of the variables, and so I did and it worked.
Concrete types in a function signature also work as assertions, so it's one way to force the compiler's hand this way. In many cases it's better to assert the arguments, rather than the parameters, to keep the method itself generic. The main reason to add types to a method is to allow multiple dispatch to pick that method of the function for values of the specified type.
The slow function I added the types to didn't really make any sense to be really generic. For nearly all the arguments (of which there was like 20), there was only one type that made any sense for each. I don't think keeping it more generic would have bought me much, and adding types worked as an assertion that made things much faster.
But yes, once I started adding types to thing, I did start utilizing multiple dispatch, since I've always been a bit fan of Clojure-style multimethods and this got me something more or less comparable to it.
No they do not, this is not how it works. Functions auto-specialize on the types they see and do a multiple dispatch behind the scenes on these specializations. Type parameters in a function signature thus only define what specializations are allowed. See https://book.sciml.ai/notes/02-Optimizing_Serial_Code/ for details. As such, you don't need to add any types to a function signature to get performance.
I've not done a lot with "numeric" programming, but I was able to pick up Julia really quickly, and I was able to do a more-or-less straight port of my dad's code to Julia. What I was extremely impressed with was how much faster it went out of the box; for some of the more crunchy bits, running on the same hardware, the Julia code ran quite literally 150x faster, and this was without any Julia-specific optimizations other than adding types to the function arguments.
Since then I've learned a lot more about Julia, to a point where it has sort of become my go-to language for starting new projects (assuming I don't need to use a specific JVM library or something). It's basically guaranteed to give me fast code out of the box, it has most of the concurrency constructs that I actually care about, it has a better-than-average macro system (at least compared to something like C++), and personally I just think that the language looks pretty once you get into some more of the details. I wish it were a more popular language, because the only thing that has held me back from using it more has been the lack of library support. Most of the libraries are fine, but for example I had a lot of trouble getting the Bcrypt library working, which is something I wouldn't really have a problem with on a JVM language or C++ or something.
Of course, this is (hopefully) a temporary problem, as the language slowly grows in popularity the library support should just get better.
ETA:
I haven't done proper benchmarks on this so this is purely speculation, but that said I think that the biggest boost comes from the fact that Julia properly optimizes loops. My dad is a smart dude, but not being a software engineer in 2024, didn't really know that using loop constructs in most higher-level language is considered a code smell. I don't now about Octave, but I know in Python it's generally considered way better to try and avoid vanilla "loops" and try and convert looping things into some kind of vector operation.
I think the JIT part of Julia is smart enough to optimize loops out of the box, and so my straight port of my dad's code just "magically" got faster, and I was extremely impressed, but I suspect that if my dad had utilized a lot of the vector operations of Octave and/or Matlab, the results wouldn't be nearly as impressive. I suspect Julia would still have performed better (if nothing else because I was able to add type information), but I suspect it wouldn't be the 100+X speed increase that I was getting.