Description
For a while now we've been trying to work towards the ability to enable v8 pointer compression (https://v8.dev/blog/pointer-compression) by default in Node.js (https://github.com/search?q=repo%3Anodejs%2Fnode+%22Pointer+compression%22&type=issues). The key limitation that we have had up to this point is that enabling pointer compression has historically forced a process-wide maximum v8 heap limit of 4 GB, which obviously would be a significant breaking change in the runtime, despite the fact that enabling pointer compression has been benchmarked to provide a 40% performance improvement overall.
The key reason for the 4 GB limitation is that enabling pointer compression introduces a concept of a "pointer cage" within which the compressed pointers are held. This pointer cage is limited in size at compile time (I believe but could be misremembering but it might be tunable at compile time but it still ends up placing a fairly strict limit on heap size that would be a breaking change). But this size limit is inherent in the concept of pointer compression and cannot be avoided in order for pointer compression to even function.
For a while now, Cloudflare Workers (which also uses v8) has been running in a generally unsupported configuration of v8 that enables pointer compression but disables the shared pointer cage. As v8 development has progressed, the need to solve limitations of the shared pointer cage have become critical to prevent v8 from having to continue maintaining this unsupported configuration mode that workers has relied on.
So earlier this year Cloudflare partnered with Igalia to implement a new feature in v8 that will be fully landing soon called an "Isolate Group". Most of the work has already landed in v8.
An Isolate Group represents a whole pointer cage with the 4 GB limit. The key difference is that we can now create multiple Isolate Groups within a single process, essentially allowing us to create multiple groupings of isolates with each group having a maximum of 4 GB but removing the process-wide maximum heap restriction.
Any number of Isolates can be created within an Isolate Group, all of which would sit within the same pointer cage.
So whereas today in Node.js, with pointer compression enabled, the process would look something like...
+----------------------------------------------------------------------------+
| Pointer Cage (4GB) |
| +-----------------+ +-----------------+ +-----------------+ |
| | Isolate | | Isolate | | Isolate | |
| | (main thread) | | (worker 1) | | (worker 2) | |
| +-----------------+ +-----------------+ +-----------------+ |
+----------------------------------------------------------------------------+
With Isolate Groups it could look more along the lines of ...
+------------------------+------------------------+--------------------------+
| Pointer Cage (4GB) | Pointer Cage (4GB) | Pointer Cage (4GB) |
| +-----------------+ | +-----------------+ | +-----------------+ |
| | Isolate | | | Isolate | | | Isolate | |
| | (main thread) | | | (worker 1) | | | (worker 2) | |
| +-----------------+ | +-----------------+ | +-----------------+ |
+------------------------+------------------------+--------------------------+
In other words, rather than creating all isolates (main thread + worker thread isolates) in a single shared process-wide pointer cage, each isolate can be created in a separate pointer cage ("isolate group"), of which we can now have any number, meaning the entire process is no longer limited to just a single 4 GB heap.
Obviously ,this still becomes a breaking change because individual isolates will have the imposed 4 GB limit but with pointer compression this is far less of a limitation than it actually may first appear. The vast majority of Node.js applications that exceed 4 GB heaps without pointer compression will run just fine in a 4 GB heap of compressed pointers.
When Node.js finally updates to a version of v8 that has the Isolate Groups work included, I plan to open a PR that adds an experimental compile-time flag to enable automatic use of Isolate Groups. The code change for this is simple:
With this new compile flag enabled, rather than:
auto isolate = v8::Isolate::New(...);
We would have...
auto group = v8::IsolateGroup::New();
auto isolate = v8::Isolate::New(group, ...);
And compile the Node.js process with pointer compression enabled.
My hope is that we would soon thereafter be able to make this the default build mode for Node.js in a later phase 2.
In that later phase, if no issues are encountered, we would flip the default so that the main release builds would compile with pointer compression and isolate groups enabled, with a compile flag option to disable these.
This ought to result in a significant memory and performance improvement across the board in Node.js and ALSO provides the benefit of allowing every Node.js worker thread to run in its own v8 sandbox (https://v8.dev/blog/sandbox) also boosting overall security.
Cloudflare and Igalia soon plan to publish a blog post covering all of this in far more detail along with some additional work we are collaborating on. There are a number of additional technical details that should be covered in extensive detail in that blog post. That blog post would likely serve as the official public announcement of this work but for now, I wanted to open this issue as a way of giving folks here a heads up and to serve as a tracking issue for the work as it progresses. It was important to us that whatever work was done here did not just benefit Cloudflare but could also be used by any runtime built on v8 including Node.js, Deno, and even browsers.
The actual changes to Node.js are expected to be minimal and the vast majority of applications will see no difference other than improved performance and memory usage. Applications that do require massive heap sizes would likely see an impact so we will need to work out some solutions for those.
I will be sharing more details as things progress so watch this thread. And if you have any questions, ask away and I'll answer whatever I can!
/cc @mcollina @joyeecheung @targos @anonrig @nodejs/v8 @nodejs/workers @nodejs/tsc