
Isomorfeus Speednode
A fast runtime for execjs using node js. Works on Linux, BSDs, MacOS and Windows. Inspired by execjs-fastnode.
Community and Support
At the Isomorfeus Framework Project
Installation
In Gemfile:
gem 'isomorfeus-speednode', then bundle install
Configuration
Isomorfeus-speednode provides one node based runtime Speednode which runs scripts in node vms.
The runtime can be chosen by:
ExecJS.runtime = ExecJS::Runtimes::Speednode
If node cant find node modules for the permissive contexts (see below), its possible to set the load path before assigning the runtime:
ENV['NODE_PATH'] = './node_modules'
Contexts
Each ExecJS context runs in a node vm. Speednode offers two kinds of contexts:
- a compatible context, which is compatible with default ExecJS behavior.
- a permissive context, which is more permissive and allows to
requirenode modules.
Compatible
A compatible context can be created with the standard ExecJS.compile or code can be executed within a compatible context by using the standard ExecJS.eval or ExecJS.exec.
Example for a compatible context:
compat_context = ExecJS.compile('Test = "test"')
compat_context.eval('1+1')
Permissive
A permissive context can be created with ExecJS.permissive_compile or code can be executed within a permissive context by using
ExecJS.permissive_eval or ExecJS.permissive_exec.
Example for a permissive context:
perm_context = ExecJS.permissive_compile('Test = "test"')
perm_context.eval('1+1')
Evaluation in a permissive context:
ExecJS.permissive_eval('1+1')
Async function support
Its possible to call async functions synchronously from ruby using Context#await:
context = ExecJS.compile('')
context.eval <<~JAVASCRIPT
async function foo(val) {
return new Promise(function (resolve, reject) { resolve(val); });
}
JAVASCRIPT
context.await("foo('test')") # => 'test'
Attaching ruby methods to Permissive Contexts
Ruby methods can be attached to Permissive Contexts using Context#attach:
context = ExecJS.permissive_compile(SOURCE)
context.attach('foo') { |v| v }
context.await('foo("bar")')
The attached method is reflected in the context by a async javascript function. From within javascript the ruby method is best called using await:
r = await foo('test');
or via context#await as in above example. Attaching and calling ruby methods to/from permissive contexts is not that fast. It is recommended to use it sparingly.
Benchmarks
Highly scientific, maybe.
1000 rounds using node 16.6.2.:
standard ExecJS CoffeeScript eval benchmark:
user system total real
Isomorfeus Speednode Node.js (V8) Windows 0.079000 0.031000 0.110000 ( 0.518821)
Isomorfeus Speednode Node.js (V8) Linux 0.092049 0.039669 0.131718 ( 0.546846)
mini_racer (V8) 0.4.0 Linux only 0.776317 0.062923 0.839240 ( 0.480490)
Node.js (V8) Linux 0.330156 0.354536 52.523972 ( 51.203355)
evel overhead benchmark:
user system total real
Isomorfeus Speednode Node.js (V8) Windows 0.032000 0.031000 0.063000 ( 0.227634)
Isomorfeus Speednode Node.js (V8) Linux 0.085135 0.022002 0.107137 ( 0.153055)
mini_racer (V8) 0.4.0 Linux only 0.083562 0.115612 0.199174 ( 0.128455)
Node.js (V8) Linux 0.273238 0.265101 30.169976 ( 29.636668)
minify discourse benchmark from mini_racer:
minify discourse_app_minified.js:
user system total real
isomorfeus-speednode Windows 0.016000 0.078000 0.094000 ( 11.828518)
isomorfeus-speednode Linux 0.043747 0.000000 15.931284 ( 11.860528)
mini_racer 0.043471 0.000000 15.974214 ( 11.923735)
node 0.043695 0.000000 15.812040 ( 11.781835)
To run benchmarks:
- clone repo
cd rubybundle installbundle exec rake bench
Tests
To run tests:
- clone repo
cd rubybundle installbundle exec rake test