News for hackers. Stuff that matters.
Re:Romeo and Julia, Where Romeo Is Basic Statistics by tombert on Friday March 15, 2024 @11:12AM (#39716648)
My dad recently had me help him port some Matlab code to a web service. He eventually ported it over to Octave so I could run it, since I don't have any kind of license for Matlab. I couldn't find any good web server implementations for Octave, so I was looking for free "matlab-like" languages that might, and I found Julia. The code wasn't stats-heavy, but it was heavily number-crunchy, making heavy use of matrices and tensors and stuff that admittedly still kind of goes over my head.

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.

Re:Romeo and Julia, Where Romeo Is Basic Statistics by affinepplan on Friday March 15, 2024 @01:50PM (#39718608)
> and this was without any Julia-specific optimizations other than adding types to the function arguments.

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)

Re:Romeo and Julia, Where Romeo Is Basic Statistics by tombert on Friday March 15, 2024 @02:06PM (#39718799)
In one case adding type information into the function args, for a function with a loop being run like 20 million times, it made a pretty substantial difference; it brought processing down from roughly 2 minutes to roughly 10 seconds.

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.

Re:Romeo and Julia, Where Romeo Is Basic Statistics by samatman on Friday March 15, 2024 @02:54PM (#39719365)
What grants speed to Julia code is type assertions, at least in the case where the assertions lead to type stability. The compiler balances speed (of compilation) and accuracy, when inferring types and deciding what methods to specialize. If and when it can't figure that out, adding a type assert give it more ability to assume the assertion is accurate.

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.

Re:Romeo and Julia, Where Romeo Is Basic Statistics by tombert on Friday March 15, 2024 @03:02PM (#39719455)
That makes sense; basically giving a hint so that the compiler can figure out what kind of special optimizations are possible. I would assume that it can avoid extra reflection calls as well as a result?

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.

Re:Romeo and Julia, Where Romeo Is Basic Statistics by ChrisRackauckas on Thursday March 21, 2024 @04:01AM (#39775857)
> 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.

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.