Shared memory with node.js
This is more like a tutorial on writing a simple node.js add-on to share memory among node.js processes.
One of the limitations of node.js/io.js is that they are single threaded. Only way to use multiple cores in the processor is to run multiple processes . But then you are working on different memory spaces. So it doesn’t help if you want multiple processes working on the same memory block. This is required in memory intensive tasks that cannot be efficiently sharded.
All the source code is available in Github.
Node addon #
node-gyp installed to build the node module.
npm install node-gyp -g
I think the node.js version matters as the addon api has changed. I was working on node 0.12.2, when I tested this out.
binding.gyp is required by node-gyp to build the addon.
shm_addon.cpp. This is a very basic addon that has one export
createSHM, which creates a shared memory block of 800,000 bytes (attaches if exists) with read & write permission to all users.
Shared memory is allocated with
shmget and attached to the address space of the process with
shmid = shmget( key, MEM, IPC_CREAT | 0666 ); data = (char *)shmat( shmid, NULL, 0 );
It keeps a pointer to the memory block and returns it if
createSHM is called twice by the same node.js program .
createSHM returns an
ArrayBuffer, initialized with the pointer to the shared memory.
Local<ArrayBuffer> buffer = ArrayBuffer::New(isolate, (void *)data, MEM);
The node module
shm_addon is built with node-gyp with following commands.
node-gyp configure node-gyp build
The node addon will be created in
Parent and child programs for testing #
This is a simple counting problem to illustrate how shared memory can be used. We will populate the array of 200,000 32-bit integers with the sequence
0,1,2,...998,999,0,1,2,..998,999,0,1,2,.... So there are 200 positions with each integer between 0 and 999. Each of the child programs (workers) will count the number of occurrences of each integer between 0 and 999 by inefficiently traversing the array a 1,000 times.
spawn.coffee is the parent program that starts the child processes.
child.coffee is the child program.
Shared memory is attached by parent program and child program by calling the node addon.
shm = require './build/Release/shm_addon' a = new Int32Array shm.createSHM()
We are calculating the time taken for the child processes to count. Time it takes for processes to get spawn and exit is excluded. Therefore the child processes start counting when they receive something in the standard input. Number of child processes can be set with
process.stdin.on 'data', (msg) -> start()
coffee spawn.coffee will start processes and do the counting and show the time it took to complete.
You can take a look at shared memory allocated by running command
IPC status from <running system> as of Tue Apr 14 13:58:16 IST 2015 T ID KEY MODE OWNER GROUP Shared Memory: m 65536 0x000019a5 --rw-rw-rw- varunajayasiri staff m 65537 0x000019a4 --rw-rw-rw- varunajayasiri staff m 65538 0x000019a2 --rw-rw-rw- varunajayasiri staff
bench.coffee was used to find the time a single process takes to count.
@chethiyaa did some testing on a quad core i7.
|# children||single process (ms)||multi process (ms)|
 Node modules like parallel.js fork new processes when on node and use web workers on browser.
shmget (documentation) allocates shared memory and
shmat (documentation) attaches the shared memory block.
 Since the
ArrayBuffer is constructed with a memory pointer, it will be
external. That is the memory will not be garbage collected and the addon will have to free the memory. Here’s the v8 documentation to ArrayBuffer.
 Shared memory limits are quite small by default. So trying to allocate a lot of shared memory will give errors. This article gives details on changing and viewing these settings.