This is a comparison of different methods of calling Rust code from Node with benchmarks. You should have [email protected] and [email protected]+ installed and node-gyp configured.
There are a few different ways to call Rust from Node. All of them are based on FFI (Foreign Function Interface).
cargo new embed cd embed edit src/lib.rs #[no_mangle]pubexternfnfibonacci(n:i32) -> i32{returnmatch n {0 => 0,1 => 1, n => fibonacci(n - 1) + fibonacci(n - 2)}}edit Cargo.toml Add to bottom:
[lib] name = "embed"crate-type = ["dylib"]Then you can build the dynamic library:
cargo build --release ls target/release You can now find a libembed.dylib file on macOS (different for other architectures).
You can call any dynamic library with node-ffi module using only JavaScript.
varffi=require('ffi');varlib=ffi.Library('rust/target/release/libembed',{fibonacci: ['int',['int']]});lib.fibonacci(10)// 89NOTE: path could be different
This is the most complicated way because you have to write a bit of C++. You can read more about native modules here.
mkdir cpp-ffi cd cpp-ffi edit addon.cc #include<node.h>usingnamespacev8;// here we define the fibonacci function from external library (rust dynamic library in our case)externint32_tfibonacci(int32_t input); voidMethod(const FunctionCallbackInfo<Value>& args){Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); int value = args[0]->NumberValue(); args.GetReturnValue().Set(Number::New(isolate, fibonacci(value)))} voidinit(Handle<Object> exports){NODE_SET_METHOD(exports, "fibonacci", Method)} NODE_MODULE(addon, init)edit binding.gyp {"targets": [{"target_name": "addon", "sources": ["addon.cc" ], "libraries": [ "../../embed/target/release/libembed.dylib" ] }] } NOTE: path could be different
edit index.js module.exports=require('./build/Release/addon');Then you can build the module:
node-gyp configure build git clone [email protected]:wtfil/rust-in-node.git cd rust-in-node npm install npm run build node benchmark (i5-4258U, EI Capitan 10.11.5)
vanilla.fibonacci(10) x 1,619,893 ops/sec ±2.52% (88 runs sampled) nativeRustFFI.fibonacci(10) x 221,780 ops/sec ±7.69% (78 runs sampled) nativeCpp.fibonacci(10) x 3,261,136 ops/sec ±4.91% (81 runs sampled) nativeCppFFI.fibonacci(10) x 3,936,481 ops/sec ±5.73% (84 runs sampled) nativeRustNeon.fibonacci(10) x 2,337,627 ops/sec ±4.08% (80 runs sampled) As you can see the direct ffi call is too slow to have deal with it, but ffi + C++ wrapper is as fast as a native C++ module, so Rust is a good candidate for native modules for Node.js.
(i5-4200U, Win 10)
vanilla.fibonacci(10) x 1,377,844 ops/sec ±0.16% (97 runs sampled) nativeRustFFI.fibonacci(10) x 339,064 ops/sec ±0.28% (102 runs sampled) nativeCpp.fibonacci(10) x 2,698,431 ops/sec ±0.22% (100 runs sampled) nativeCppFFI.fibonacci(10) x 4,479,237 ops/sec ±0.32% (98 runs sampled) (same i5-4200U, Ubuntu 15)
vanilla.fibonacci(10) x 2,108,353 ops/sec ±0.49% (98 runs sampled) nativeRustFFI.fibonacci(10) x 334,195 ops/sec ±0.42% (93 runs sampled) nativeCpp.fibonacci(10) x 4,646,598 ops/sec ±0.40% (100 runs sampled) nativeCppFFI.fibonacci(10) x 5,235,762 ops/sec ±0.70% (100 runs sampled) (Intel® Xeon(R) CPU E5630 @ 2.53GHz × 8 ubuntu 16.04)
vanilla.fibonacci(10) x 1,481,437 ops/sec ±0.93% (100 runs sampled) nativeRustFFI.fibonacci(10) x 170,863 ops/sec ±0.46% (96 runs sampled) nativeCpp.fibonacci(10) x 2,500,092 ops/sec ±0.22% (103 runs sampled) nativeCppFFI.fibonacci(10) x 2,451,105 ops/sec ±0.93% (96 runs sampled) nativeRustNeon.fibonacci(10) x 2,405,373 ops/sec ±0.17% (97 runs sampled) For some reason on Windows performance of a Rust lib connected to a C++Node.js extension via the C ABI is drastically faster. It is quite possibly that VC++ 2015 compiler is suboptimal. Rust + C++ is also faster on Ubuntu.
Building on Windows might be a challenging task, because node-gyp makes everyone unhappy on Windows.
If everything is configured properly npm run build should just work.
However, it is likely to be broken. In that case try these steps:
- First ensure that you followed all Windows installation instruction from README on https://github.com/nodejs/node-gyp
- Ensure that you using the same target for both
RustandC++.Rustshould be compiled with MSVC target and target platform should be the same (ie i686/win32) - Newer versions of
Cargoproduce.dll.libfiles and older versions produce simply.lib. After building rust code please ensure that win embed lib name insrc\native-cpp-ffi\binding.gypmatches file names inrust\target\release. - If you are building only
Rustandnative-cpp-ffithen you need to copy all libs compiled by Cargo into the directory of the Node addon. In this particular case: copyrust/target/release/embed*tosrc/native-cpp-ffi/build/Release
Test Rust module with multi-threading. It could produce even better results. Feel free to add any other tests :)