Cool Julia 1.11 features
Cameron
March 25, 2024
There's some cool Julia features in the NEWS files I thought I'd highlight. Very brief stuff but good to note.
The type for arrays
You can read more about the design of this feature here. It's a really good design doc and I'd recommend taking a look. I've summarized it here, but please check it out for a much more thoughtful treatment. Thanks to Oscar Smith for the work.
Array types are powerful + general, but have a few shortcomings:
They use a lot of C, which means the Julia/LLVM compiler can't work magic
There's lots of overhead (also due to C calls)
is slow
No element-wise atomic operations
The type is out, which is a low-level type that is intended to address some of this stuff. Some performance improvements:
Array appends () are about 2.2x faster
Empty array gen is 3x faster ()
80% faster for empty implementers
The system image is a little larger.
for carrying locks with resources
A common pattern when working with multithreaded code is to use a lock to protect a value from multiple threads accessing it at the same time. For example, you might do something like
The above code is safe because the function ensures that only one thread can access the array at a time -- no data races.
is a super convenient feature where you can attach a lock to a resource, so you don't have to worry about managing the lock yourself. Here's an example:
Notice that now we're just using on the raw resource and the lock is managed for us. This is a nice feature because it makes the code cleaner and easier to read.
The keyword
The keyword is applied to symbols that are considered part of the public API of a module, but are not exported when you call . This is part of the wider discussion in the Julia community that exporting everything is not always the best idea -- there's been a lot of clutter and such with people being trigger-happy about s. Thanks to Lilith Hafner for the work.
As an example, you might have a module like
when you , you get and directly accessible:
Now, you'll be able to use public functions like and without having to them. This is a nice feature because it allows you to keep your module clean and not export everything.
Now you'd have the following behavior:
I'm curious to see how the community will use this stuff. I'm not sure it's immediately obvious to me how I'll use it, but it seems like standard engineering practice.
The thread scheduler
You can now use a greedy thread scheduler, which greedily works on iterator elements as they are produced. Greedy threads simply take the next available task in an iterator without regard to how hard the task is, how many threads there are, etc. If you have a lot of tasks that are all about the same difficulty, greedy scheduling can be a good choice.
Julia has the other scheduling options and , which are more sophisticated and can be more efficient in some cases. will partition the iterator into chunks and assign each chunk to a thread, while will dynamically allocate small chunks to threads. is the default scheduler, but I suspect will be useful in some repeated, small multithreading tasks.
The PR is here. Thanks to Valentin Bogad/Sukera.
Discussion in the ATmosphere