Salt and Pepper — Running native code within the browser with Google Native Client

When the web started off in 1989 at CERN, it was just a collection of static HTML documents, the first browsers were little more than document viewers, and computing still happend largely on big servers connected to many thin-client workstations. Much has changed over the last 20 years. Computing has moved from a the server-terminal model to the personal computers we know, and as web browsers are rapidly transforming from document viewers into sandboxed Javascript runtime environments, something amazing is happening: computing is moving back to a client-server model, with servers located in the cloud serving web applications running in the browser sandbox.

With browsers transforming into target platforms for general application development, there comes a desire for software running at near-native speed, something which a highly dynamic language like Javascript still has a hard time to deliver (even though Javascript runtimes have improved speed tremendously over the last years). It is not surprising that Google has come up with their own answer to this problem: Google native client (NaCl) allows native C/C++ code to be compiled to run directly in the browser and has been part of Chrome since version 14 (November 2011). In this blog post, I’ll take a closer look at this technology and explore how it works.

How does it work, and what are NaCl and pepper?

Native client (or NaCl) is the name of a sandboxing technology developed by Google. C/C++ programs are compiled to native code which is embedded in a webpage and runs isolated in a browser sandbox. A NaCl module has no direct access to the underlying system. Instead, the browser provides an API which can be used for accessing functions of the host environment like video (including OpenGL ES 2.0), audio and filesystem (similar to HTML5 filesystem) / network access. This API is called Pepper. Pepper is available in both a C and a C++ version and bears many similarities to the HTML5 APIs available to Javascript code — in particular, many of the same sandboxing restrictions apply.

Pepper also implements parts of the POSIX standard, so existing library code can be ported and used with relative ease. In fact, Google maintains the NaCl ports project which provides patches and a build system for using a broad range of existing libraries like Boost or SDL directly with NaCl programs.

A NaCl module can communicate with Javascript code running on the same web page via a message passing API similar to that available for the communication with web worker threads. This allows NaCl modules to seamlessly integrate into web applications, with the NaCl module providing high-speed graphics, audio and processing, while the Javascript part manages a conventional HTML5 UI.

Google claims that NaCl programs run at about 80–90% native speed, allowing for a broad range of applications from high-end games running in the browser to real-time audio and video processing.

Isn’t native code platform dependent?

There are currently two variants of NaCl. The „usual“ version has been around since Chrome 14 and currently supports code running on the x86-32, x86-64 and ARM platforms (independent of the underlying operating system). Since Chrome 31, and Pepper 31, the portable PNaCl toolchain is also supported.

With PNaCl ,programs are not compiled to machine language, but to a subset of LLVM bytecode. This is translated to machine code by the browser at runtime, similar to asm.js enabled Javascript. This way, PNaCl programs can run on any architecture.. Currently, PNaCl still is quite new, and while the ABI has been stabilized, there are still some restrictions which Google promises to remove in the near future. Most notably, C++ exceptions are not yet supported.

How are NaCl programs developed?

Google provides the NaCl/Pepper SDK for developing native client programs. The SDK contains compiler toolchains which can generate NaCl code for x86-32, x86-64 and ARM as well as portable PNaCl code. The SDK can be easily downloaded from the Google developers page.

Although I have yet to try it, NaCl programs can be debugged using the GNU debugger gdb. In order to do that, the feature must be activated in the Chrome flags (chrome://flags). A version of gdb provided with the SDK then connects to the browser and allows debugging the module.

Both the SDK and the browser runtime are open source.

How are NaCl programs deployed?

NaCl programs are embedded into web pages by adding an emded tag to the HTML code

(for a PNaCl program, the mimetype is application/x-pnacl). Note that the embed element actually takes up space on the web page if you assign non-zero dimensions — it provides a canvas on which the module’s output is rendered.

The webbrowser then loads the manifest file from the server (in this case, glow.nmf) which describes the location of the actual executables

The corresponding PNaCl manifest would like

The browser then loads and dispatches the NaCl module, and fires a load event on the embed tag. Messages can be sent to the module using the element’s „postMessage“ method, and incoming messages can be received by listening for the „message“ event.

How does the lifecycle of a NaCl program look like? (WARNING, technical details ahead)

Each NaCl program must implement a Module and an Instance class which derive from corresponding parent classes provided by the API (I’ll only talk about the C++ API here). Once the module is dispatched, a factory function is called which generates a module instance. The Module class itself implements a factory method which is called by the browser to create independent instances of the Instance class. The idea is that a module referenced by several embed tags on the same page is only loaded once, and each embed will just spawn a separate instance of the same module.

The program flow of a NaCl program is event driven. Once the instance has been dispatched, the program sleeps. Input events or incoming messages wake up the program and trigger a callback method on the instance object. All calls to the Pepper API are asynchroneous and return immediately, calling a provided callback once they have finished. This program flow should be very familiar to us web developers: it is exactly how a Javascript program works.

As any scheduled callbacks can run only when the program has yielded control and sleeps, it is vital that all callbacks eventually terminate. If we need long-running functions (like a game main loop), we need to dispatch a separate thread — NaCl supports POSIX threads, something which sets it apart from Emscripten / asm.js.

If we want to use Pepper calls from a separate thread, we need a way for the API to schedule and run the callbacks. For this, Pepper provides queues call message loops. A thread which creates a message loop can call Pepper functions just like the main thread. When the API call finishes, it pushes the callback to the message loop. Once the thread runs the message loop, all queued callbacks are executed. In addition, other threads can post work to the message loop, implementing a powerful tool for inter-thread communication. Synchronous calls to API methods (which block until the call has finished) are also possible.

Of course, with multiple threads come the multithreading pitfalls which wait for the unwary, including races deadlocks. For example, it took me a while to find out that you must not block on the main thread until another thread completes a Pepper call — the API callback will not be scheduled until the main thread is free, resulting in a deadlock. This is different from the Javascript world, where deadlocks are impossible due to the lack of real concurrency.

A code sample: Glow

For digging into NaCl, I have written a small program called Glow. You can find the code on github and a hosted instance here. In order to try it you’ll have to enable native client on web pages in the chrome flags at chrome://flags (NaCl is only enabled in chrome apps by default).

Glow provides you with a canvas to draw on with the mouse. Over time, the lines bleed out and fade away. Sliders allow you to control the parameters of the transformation as well as pencil radius and frame rate.

On the technical side, the program tries to run at the requested framerate, and in each iteration, the intensity of every pixel is calculated as a function of it’s previous intensity and that of its neighbours. The UI controls are provided by Javascript code embedded into the webpage which communicates with the module by exchanging message objects. In order to keep the program simple to build, no external libraries have been used.

Building the program yourself on linux is easy. Download and Install the NaCl SDK, set the NACL_SDL_ROOT environment to the SDK root and call „make“ in order to build the module (you’ll need GNU make installed). In order to run the program, you’ll have to set up a web server to serve the directory containing the module — NaCl programs will not run from file URIs. I tested the build with Pepper versions 29 – 31.

PNaCl is also supported: with Pepper 31 or later, you can build the PNaCl executables via „make pnacl“. The PNaCl version is available as pnacl.html (hosted here). As PNaCl currently doesn’t support exceptions, the code might be less stable than it’s plain NaCl counterpart due to lack of error handling.

I’ve tried to keep the code clean and documented — feel free to look through it, modify it and use it as a starting point for your own NaCl projects.

On my 2.8GHz Core i7, I can get the program to run at about 110 FPS, while my ARM chromebook at 1.7GHz runs at about 40 FPS max (this is NaCl, the PNaCl numbers are slightly lower). It would be very interesting to compare those numbers to a comparable Javascript or asm.js implementation.

Who supports it?

Currently, Chrome is currently the only browser implementing NaCl. The main arguments brought forward against the technology are complexity, cross-platform issues, the closed-source nature of compiled code and comparisons to ActiveX and old-school browser plugins.

However, I think that comparisons to ActiveX or browser plugins are awkward at best. Contrary to both, NaCl modules do not have any dependencies on the host environment and run fully sandboxed and isolated from the host. Also, any platform issues are fully resolved with the introduction of PNaCl. Disassembled compiled code is harder to read than obfuscated Javascript, however, asm.js does the same thing — asm.js code is essentially assembler-like code embedded into Javascript.

The technology is open source, and Google is actively developing, supporting and using it. Given Google’s influence, I think that it is a safe bet that the technology is here to stay, and I think that it is realistic that other browser vendors will eventually start supporting it.

Conclusions

NaCl allows to run compiled C/C++ securely within the browser. The combination of raw speed with the possibility of communicating with Javascript code running on the same page allows for exciting new types of web applications, ranging from high-end games to realtime video and audio processing software. In addition, the Pepper API partly emulates a POSIX environment, so many existing native libraries can be used with NaCl with little or no changes required. Also, with the introduction of PNaCl, the architectural restrictions of native code have been lifted — PNaCl programs are delivered as platform-independent bytecode.

Obviously, NaCl is in direct competition with the combination Emscripten / asm.js which compiles C++ to simplified, statically typed Javascript that can be translated into optimized native code by Javascript engines supporting asm.js. It is hard to tell which technology will prevail on the long run, as both have their pros and cons, but I think that NaCl definitely is worth a closer look. While code generated with Emscripten / asm.js will run in any modern browser, computationally heavy code is likely to suffer a considerable performance hit in any browser but Firefox (which is currently the only browser supporting asm.js).

Native client modules on the other hand will only work in Chrome at this time, but they run at almost native speeds, and the runtime environment supports standard POSIX shared memory multithreading, something which Emscripten cannot do. The toolchain and API are well documented, reasonably easy to use, and if cross-browser compatibility is not an issue, NaCl is a great way to overcome the limits Javascript puts on performance critical, computationally heavy code.

Für neue Blogupdates anmelden:


6 Gedanken zu “Salt and Pepper — Running native code within the browser with Google Native Client

  1. I would think that being able to serve games ina client server model might be attractive to game developers since it should reduce the likelihood of the games being pirated.

    It also gives them access to Mac and Linux users and the possibility of selling subscriptions to games as well.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.