Introduction
I am working on a library and need to increase the performance of native calls. I found this option to be very interesting and works great when applied correctly. The Critical Linker option is a hint you can add to a fast native method, and it allows for some optimizations to happen. This can make the code go faster. That isn't the only good thing though. Down calls marked as critical also optionally allow you to pass heap memory segments where normally only off-heap memory segments are allowed. This means less copying to a native memory segment, another time saver! So let's take a closer look at this option.
What the Java doc says
You can find the complete Java doc here The Java doc tells us the following things about the methods we could mark as critical:
Warnings:
- They should be fast, like calling an empty function
- They should not make call backs into Java
- When used on non-critical functions it can have adverse effects like loss of performance or JVM crashes.
- Only when functions need short-lived access to Java heap memory
Upside
- Optimizations for fast down calls.
- Optional access to heap memory.
What the Java doc tells us is that we need to be careful when marking a method as critical. When you look at the signature of the Option you can see that access to heap memory is optional.
The Options signature:
|
|
Using Critical
Using the option is pretty straightforward as you can see in the following example. All you have to do is add the
following line Linker.Option.critical(true)
when creating an downcallHandle
.
|
|
Adding the options allows for optimizations, and enables the down call to pass heap memory. Also invoking the method is still the same as before as you can see in the next example.
If you don't want to allow access to heap memory all you need to do is pass false
to the option.
|
|
Passing Heap memory
When marking a downcall as critical you get the option to allow it to access heap memory. This can help you improve
performance because you don't have to copy data to a memory segment. Instead, a temporary native address is created for that
heap memory. The native address is valid for the duration of the call. The easiest way of creating a memory segment of heap memory is to
use MemorySegment.ofArray()
.
In the following example ofArray
is used to pass a null-terminated string directly to the native open
method.
|
|
Using the heap memory directly saves us from having to do a copy to a native segment first. This is very powerful for methods that can't afford the time to copy data.