Napa.js is a multi-threaded JavaScript runtime built on V8. This deck was presented by the project founder and co-author Daiyi Peng in TypeScript meetup (https://www.meetup.com/San-Francisco-TypeScript-Meetup/events/244605380/) on Dec 13, 2017.
Daiyi PengPrincipal Software Engineer Manager um Microsoft
4. Napa.js
• Napa.js is a multi-threaded JavaScript runtime built on V8, which was
originally designed to develop highly iterative services with non-
compromised performance in Bing. As it evolves, we find it useful to
complement Node.js in CPU-bound tasks, with the capability of
executing JavaScript in multiple V8 isolates and communicating
between them. Napa.js is exposed as a Node.js module, while it can
also be embedded in a host process without Node.js dependency.
• https://github.com/Microsoft/napajs
8. Example 3: Shared memory via `store`
• Max sub matrix with all 1’s
Setup workers to access a shared store named ‘sub-matrix-size-store’
Setup a function defintion
Write to a shared store
across threads
Read from a shared store
11. MS internal project Napa
• Started on July, 2015, motivated in building a platform for AI powered
applications in Bing, with satisfying following requirements:
1. Language-level support to quickly iterate on algorithms
2. Close-to-native performance, scalable with multiple cores
3. Support shared memory across multiple cores
4. Fine-granularity parallelism with minimized communication cost between
threads
12. MS internal project Napa, cont.
• We chose JavaScript for
• Language maturity
• Proven performance
• Productivity and large ecosystem
• Challenges with JavaScript
• there were 2 established ways to support CPU bound tasks in
Node: Computational code in C++ exposed as async JavaScript function,
or Node cluster. The former cannot satisfy requirement of flexibility, and the
latter cannot satisfy requirement of shared memory.
13. Napa.js
• OSS effort branched out in Nov, 2016 to explore how a general-
purpose multi-threading model can bring possibilities to JavaScript
world.
Napa.js OSS work started
Nov, 2016
Napa.js was made public
Aug 8, 2017
Attracted another 6K stars
on Github since Oct
Oct 17, 2017
Media coverage from
Hacker’s news and
InfoWorld
14. Napa.js: Major decisions
• Value proposition
• Complement Node.js instead of competing, by focusing on computation heavy scenarios (See
long-term)
• Distribution
• Distribute as a Node.js module instead of being another Node
• Provide pre-built binaries to minimize installation overhead
• Built from source for embedding scenarios
• Module architecture
• Use the same module architecture with Node.js, so Napa.js can load Node.js modules and
vice versa (limitations see Node.js compatibility)
• Implementation
• Use TypeScript for all JS library files for scaling this project.
• Minimize external library dependency (e.g., uv, etc.) for easy build and easy consumption
(build time: 2 mins)
16. Architecture: Node.JS
A single JS thread Multiple C++ threads
Image credit: https://www.linkedin.com/pulse/nodejs-event-loop-padmanabhan-pillai
17. Napa.JS
Zone 1
(A JS thread pool)
Architecture: Napa.JS
JS worker 1
JS worker 2
…
Zone 2
(A JS thread pool)
JS worker 1
JS worker 2
…
Node.JS
Event loop
(a single JS thread)
A virtual ‘node’
Zone for IO
(only 1 worker)
C++ thread pool
for IO
Call to
zone.execute
• All execution flow are organized on Zones, workers are not exposed from API
• Within a Zone, all workers are symmetric
• Zones are asymmetric among each other (either load different code, or have different policy)
• Node is exposed as a virtual Zone with only 1 worker. (Not for embedding mode yet)
19. Core features
• Parallel execution
• Via zone namespace
• Messaging passing & Shared Memory
• Via transport and store namespace
• Synchronization
• Via sync namespace
20. Zone: namespace `zone`
• Container of workers
• A program can create multiple zones
• All workers within a zone are symmetrical
• Support two operations
• Broadcast – run code on all workers
• Execute – run code on any one worker
• Two type of zones
• Napa zone - zone consists of Napa.js managed JavaScript workers (V8
isolates). Support partial Node.JS APIs.
• Node zone - a 'virtual' zone which exposes Node.js event loop, has access to
full Node.js capabilities.
21. Transport: namespace `transport`
• Purpose
• Efficiently passing values from one worker to another worker
• Dealing with lifecycle if there is shared native resources.
• Now support:
• JavaScript primitive types: number, boolean, array, hash, etc.
• JavaScript built-in types (release soon), Uint32Array, SharedArrayBuffer, etc.
• JavaScript objects that implements Transportable
• Native object wraps that implements Transportable
• Lifecycle (Advanced)
• A object shared across multiple threads will be destroyed after last
dereference (especially useful for native object wraps)
22. Store: namespace `store`
• Purpose
• A key/value store of JavaScript values that can be shared across workers
• Challenges
• Each v8 isolate (thread) maintains their own heap
• If a data type can be passed via `zone.execute`, it should be able to set/get in
store.
• Our solution
• Marshall the transportable type into a JSON string
• Put into C++ heap that can be read from all workers
• Unmarshall the JSON string into per-isolate JavaScript objects
23. Sync: namespace `sync`
• Purpose
• Provide mechanism for synchronization to access exclusive resources among
threads
• Simple API to minimize potential deadlock from user code
• API
• napa.sync.Lock
• lock.guardSync(func, args…)
• Future: RWLock
24. Napa modules
• Napa.js follows Node.js' convention to support modules, which
means:
• Both JavaScript modules and C++ modules are supported.
• Module resolution follows the same algorithm, except instead of searching
file extension .node for addons, Napa.JS searches .napa.
• Supports NPM, with the same way to create and publish modules.
• API of creating C++ modules (addons) are similar. Napa.JS introduced macros
that the same source code can be compiled to produce both Napa.js addon
and Node.js addon.
• See documentation
25. Other features
• Logger
• Via logger API
• Metric
• Via metric namespace
• Memory allocator
• Via memory namespace
26. Limitation
• Within Napa zone, only partial Node API is supported. This can be
compensated by using Node zone.
• This is an area you can contribute!!
• Transport of JS objects
• Function should not access closure
28. Features & Improvements
• Continuous improvement in stability and performance
• More efficient transport layer, supporting SharedArrayBuffer, etc.
• Thorough support for pluggable memory allocator
• Minimize GC impact on execution latency impact
• Node compatibility
• Support more Node API that is used in computation heavy tasks
• Unify native addon file format `.napa` and `.node` into single `.node`
• Tooling
• Debugging: Napa inspector
• napa-gyp: a node-gyp-like package for developing Napa native modules