Memory safety always has run-time costs, no matter what language we're in. 1 In Vale, the primary run-time cost is in generation checks, when the code will check that a reference still points to a valid object.
There are a lot of ways to avoid generation checks:
Between those first three, generation checks are likely a negligible source of overhead, especially since they rarely cause CPU cache misses, and their branching is perfectly predicted.
But if you need that extra bit of performance, keep reading!
Warning! There are risks to using any of the below features. If you access an object that's already been destroyed, you'll likely corrupt memory.
Corrupting memory doesn't just cause bugs, it also means your program becomes nondeterministic and cannot use deterministic replayability.
Also note that these features are completely ignored in any dependencies by default, for security reasons. You'll need to explicitly whitelist every single module that the compiler should allow unsafe operations and FFI for, using the --unsafe_whitelist flag like --unsafe_whitelist that_module
The check override operator !! can be used to skip any run-time overhead.
Any function that uses !! must be annotated with the unsafe keyword.
We'll be able to turn off all generation checks for a certain module with the --unsafe_force flag, like --unsafe_force my_module.
This will be useful to measure the overall memory-safety overhead for your program.
The unsafe block will establish a new region in which we can do unsafe operations. Specifically, it will use the normal heap (malloc and free), and it will have a normal stack like C does.
The unsafe block uses the region borrow checker to keep these separate from the normal surrounding region.
In this example, safeShip is in the safe region, and notSafeShip is in the unsafe region.
func main() {
safeShip = Spaceship(1337);
unsafe block {
notSafeShip = Spaceship(1448);
...
}
}
Similar to Fearless FFI, whenever the unsafe region has a reference to a safe region's object, it will be scrambled. When we try to use it again, it will be unscrambled and checked. This way, bugs inside the unsafe region don't corrupt memory in the safe region.
This will turn off all generation checks across the program. Use with care!
With garbage collection and reference counting the cost is obvious, and surprisingly, even borrow checking indirectly causes run-time overhead: it centralizes objects such that we need to perform bounds checks and hashing more often than other paradigms.
See Zero-Cost Memory Safety with Vale Regions for more on this.
See Hybrid-Generational Memory for more on this.