Nice post. Maybe it’s worth also adding advantages of threads over processes for performance. Imagine genomic app loading data and spamming fork() and than passing data between processes which is inter process communication less efficient than between threads. I think when people use fork() they don’t want to deal with race conditions and it’s easier for them, but threads so much lighter and just better for performance.
There is some nuance here. Threaded Julia currently can have some problems with GC time, which can lead to multiprocess being easier to get full speed with (for now).
Thanks, this is an interesting read!
Could someone clarify this part which I’m not sure I understand correctly:
A practical advice is to look through your code and see when you conceptually use a type whose structure is not perfectly captured by any existing types in your code. For example, if you see
Vector{Tuple{String, UInt8, DNA}}
, in your code, then it probably means you need to refactor the tuple out to a new struct, which you then can optimise.
From a code design perspective this is clear but I wonder what exactly would be the performance gain in defining a new type for the tuple. From what I understand, small tuples like this are often indistinguishable from defined types and often get compiled to the same machine code.
Or is it simply that you can define and dispatch on more efficient methods for this datatype?
That’s exactly right. For the compiler, structs and tuples are the same, and produce the same machine code. But my experience so far has been that for the programmer (i.e. me), creating a new type enables me to be more clear about what data I need to encode, how it can be encoded efficiently, and what kinds of operations I do on this thing.
Could you please explain the concept of type stability for me as a newby?
It just means that for a given set of input types to a function, the return type is the same no matter what concrete values the inputs have. And an extended version is that also all intermediate values / results of function calls inside the function have the same types for a given set of input types. As the compiler only compiles knowing input types, not values (except for constant folding) you get inefficient machine code if values of unknown types at compile time have to be handled.
The simplest way to violate type stability is to do something like
if condition
object_of_type_A
else
object_of_type_B
end
Fyi, this blog post on type stability is a very good read.
That is helpful. Thank you.