VARUNA JAYASIRI

@vpj

A few node.js/Javascript performance tips

March 19, 2016

Here's a list of small node.js related performance tips. I will keep updating this with new stuff we come across.

vm.runInContext vs vm.runInThisContext

runInContext and runInNewContext are much slower than runInThisContext.

Here's a small script that compares the performance with a simple for loop.

vm = require 'vm'
LOG = (require '../lib/log.js/log').log

N = 10000000
COUNT_SCRIPT = "for(var i = 0; i < #{N}; ++i) { count++; }"

T = (new Date).getTime()
count = 0
for i in [0...N]
 count++
LOG 'info', 'normal_time', "#{(new Date).getTime() - T}ms", count: count

global.count = 0
script = new vm.Script COUNT_SCRIPT
T = (new Date).getTime()
try
 script.runInThisContext timeout: 100
catch e
 LOG 'error', 'vm_error', e.message
LOG 'info', 'run_in_this', "#{(new Date).getTime() - T}ms", count: global.count

sandbox = count: 0
context = vm.createContext sandbox
script = new vm.Script COUNT_SCRIPT
T = (new Date).getTime()
try
 script.runInContext context, timeout: 1000
catch e
 LOG 'error', 'vm_error', e.message
LOG 'info', 'run_in_context', "#{(new Date).getTime() - T}ms", sandbox

This is the output

img/node-performance/vm.png

Heap Limit

The heap size limit of node.js applications on 64-bit system is about 1.7gb. Fortunately, you can increase this by passing max_old_space_size parameter to node.

node --max_old_space_size=4096 [JS_FILE]

However, typed arrays are not bound by this limit. That is, you can allocate a few gigabytes of memory to typed arrays without specifying the above parameter.

Parent of sliced string

Substrings of large strings keep a reference to the parent string. This eats up memory if you want to discard the parent string.

A quick hack of splitting and joining the substring solves this.

String join

String concatenation s += part eats up a lot of memory, that doesn't get garbage collected. It's ok for a few concatenations. Anything more than 10 should use Array.join.

The reason why the normal_time is higher than run_in_this is probably because coffeescript for loop has two counters.

runInContext gets terminated within the timeout (1s) before it completes the loop.

The value is specified in megabytes.

Here's a list of small node.js related performance tips. I will keep updating this with new stuff we come across. ##``vm.runInContext`` vs ``vm.runInThisContext`` >>> <<https://nodejs.org/api/vm.html(Documentation)>> ``runInContext`` and ``runInNewContext`` are much slower than ``runInThisContext``. Here's a small script that compares the performance with a simple for loop. ```coffee vm = require 'vm' LOG = (require '../lib/log.js/log').log N = 10000000 COUNT_SCRIPT = "for(var i = 0; i < #{N}; ++i) { count++; }" T = (new Date).getTime() count = 0 for i in [0...N] count++ LOG 'info', 'normal_time', "#{(new Date).getTime() - T}ms", count: count global.count = 0 script = new vm.Script COUNT_SCRIPT T = (new Date).getTime() try script.runInThisContext timeout: 100 catch e LOG 'error', 'vm_error', e.message LOG 'info', 'run_in_this', "#{(new Date).getTime() - T}ms", count: global.count sandbox = count: 0 context = vm.createContext sandbox script = new vm.Script COUNT_SCRIPT T = (new Date).getTime() try script.runInContext context, timeout: 1000 catch e LOG 'error', 'vm_error', e.message LOG 'info', 'run_in_context', "#{(new Date).getTime() - T}ms", sandbox This is the output !img/node-performance/vm.png >>> The reason why the **normal_time** is higher than **run_in_this** is probably because coffeescript for loop has two counters. ``runInContext`` gets terminated within the timeout (1s) before it completes the loop. ##Heap Limit The heap size limit of node.js applications on 64-bit system is about 1.7gb. Fortunately, you can increase this by passing ``max_old_space_size`` parameter to node. ```bash node --max_old_space_size=4096 [JS_FILE] >>> The value is specified in megabytes. However, <<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays(typed arrays)>> are not bound by this limit. That is, you can allocate a few gigabytes of memory to typed arrays without specifying the above parameter. ##Parent of sliced string >>> <<http://vpj.github.io/string_slice.html(Wrote a separate post on this)>> Substrings of large strings keep a reference to the parent string. This eats up memory if you want to discard the parent string. A quick hack of splitting and joining the substring solves this. ##String join String concatenation ``s += part`` eats up a lot of memory, that doesn't get garbage collected. It's ok for a few concatenations. Anything more than 10 should use ``Array.join``.