Wednesday, May 3, 2017

Energizing Atom with V8's custom start-up snapshot

The team behind Atom, a text editor based on the Electron framework, recently published an article detailing significant improvements to start-up time, owing big parts of the gains to the usage of V8's custom start-up snapshot. Awesome!

Native Electron apps, including Atom, leverage Chromium to display a GUI and Node.js as an execution environment, both of which respectively embed V8 to run JavaScript. This allows Electron apps to take advantage of V8 snapshots to quickly initialize a previously serialized heap for faster startup. Electron developers have even released electron-link, a convenience library for setting up this feature, which Atom heavily relies on for its performance optimizations.

We announced V8's support for custom start-up snapshot more than a year ago. With v8::V8::CreateSnapshotDataBlob, embedders can provide an additional script to customize a start-up snapshot. New contexts created from this snapshot are initialized as if the additional script has already been executed. As the Atom team has shown, using a custom start-up snapshot can significantly boost start-up performance.

To quote the Atom article:

The tricky part of using this technology, however, is that the code is executed in a bare V8 context. In other words, it only allows us to run plain JavaScript code and does not provide access to native modules, Node/Electron APIs or DOM manipulation.

To work around this restriction, electron-link goes great lengths to make sure native functions (backed by C++ functions) are not included in the snapshot, and are instead loaded lazily. V8's serializer simply does not know how to serialize these native functions. Instead, native functions are wrapped into helper functions that load them lazily at runtime.

Since our original post about snapshots, the V8 team has continued developing the snapshot API and added many features and improvements. These features include support for serializing native functions. Provided that the backing C++ functions have been registered with V8, the serializer can now recognize and encode native functions for the deserializer to restore later. We’re happy to announce that the work-around in electron-link is no longer necessary.

One caveat remains: the serializer cannot directly capture state outside of V8, for example changes to the DOM in case of Atom. However, outside state directly attached to JavaScript objects via embedder fields (previously named "internal fields") can now be serialized and deserialized through a new callback API. With some work, this feature allows outside state to be put into the snapshot after all.

Some other highlights include:

FunctionTemplate and ObjectTemplate objects can now be added and extracted from the snapshot, so that they do not have to be set up from scratch.

V8 can now run multiple scripts or apply any other modifications to the context before creating the snapshot, as opposed to only a single source string.

Multiple differently-configured contexts can now be included in the same start-up snapshot blob.

To make these features more accessible, we designed a new, more powerful API with v8::SnapshotCreator. The old API is now merely a wrapper around this underlying API. This is how v8::V8::CreateSnapshotDataBlob is implemented:

The new API is available in V8 version 5.7 and later. We hope that these new features will help embedders make even better use of custom start-up snapshot. If you have any questions, please reach out to our v8-users mailing list.