SkillAgentSearch skills...

Fastcall

fastcall - Fast, dyncall based foreign function interface library for Node.js

Install / Use

/learn @cmake-js/Fastcall
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Build Status

TOC

<!-- TOC --> <!-- /TOC -->

About

fastcall is a foreign function interface library which aim is to provide an easy to use, 100% JavaScript based method for developers to use native shared libraries in Node.js, without needing to touch anything in C++ and without sacrificing too much performance. It's designed with performance and simplicity in mind, an it has comparable function call overhead with hand made C++ native modules. See the benchmarks.

Why?

There is a a popular dynamic binding library for Node.js: node-ffi. Then why we need another one could ask? For performance! There is a good 20x-40x function call performance overhead when using node-ffi compared to hand made C++ native module, which is unacceptable in most cases (see the benchmarks).

Features

  • uses CMake.js as of its build system (has no Python 2 dependency)
  • based on TooTallNate's excellent ref module and its counterparts (ref-struct, ref-array and ref-union), it doesn't try to reinvent the wheel
  • has an almost 100% node-ffi compatible interface, could work as a drop-in replacement of node-ffi
  • RAII: supports deterministic scopes and automatic, GC based cleanup
  • supports thread synchronization of asynchronous functions

Requirements

  • CMake
  • A proper C/C++ compiler toolchain of the given platform

Install

npm install --save fastcall

Clone and Build

git clone --recursive https://github.com/cmake-js/fastcall.git
cd fastcall
npm install

For subsequent builds, type:

cmake-js compile

But don't forget to install CMake.js as a global module first:

npm install -g cmake-js

Benchmarks

To run benchmarks, type:

node benchmarks

Results:

Results

There are 3 tests with synchronous and asynchronous versions:

  • addNumbers: simple addition, intended to test function call performance
  • concat: string concatenation, intended to test function call performance when JavaScript string to C string marshaling required, which is costly.
  • callback: simple addition with callbacks, intended to test how fast native code can call JavaScript side functions

All tests implemented in:

  • native: C++ code. Assuming function call's cost is almost zero and there is no need for marshaling, its numbers shows the run time of test function bodies
  • native module: Nan based native module in C++. This is the reference, there is no way to be anything to be faster than this. fastcall's aim is to get its performance as close as possible to this, so the overhead ratio got counted according to its numbers
  • node-ffi: binding with node-ffi in pure JavaScript
  • fastcall: binding with fastcall in pure JavaScript

Yeah, there is a room for improvement at fastcall side. Improving marshaling and callback performance is the main target of the future development.

Documentation and Tutorials

fastcall.Library

Central part of fastcall is the Library class. With that you can load shared libraries to Node.js' address spaces, and could access its functions and declared types.

class Library {
	constructor(libPath, options);

	isSymbolExists(name);

	release();

	declare(); declareSync(); declareAsync();

	function(); syncFunction(); asyncFunction();

	struct();

	union();

	array();

	callback();

	get functions();

	get structs();

	get unions();

	get arrays();

	get callbacks();

	get interface();
}

Constructor:

  • libPath: path of the shared library to load. There is no magical platform dependent extension guess system, you should provide the correct library paths on each supported platforms (os module would help). For example, for OpenCL, you gotta pass OpenCL.dll on Windows, and libOpenCL.so on Linux, and so on.
  • options: optional object with optional properties of:
    • defaultCallMode: either the default Library.callMode.sync, which means synchronous functions will get created, or Library.callMode.async which means asynchronous functions will get created by default
    • syncMode: either the default Library.syncMode.lock, which means asynchronous function invocations will get synchronized with a library global mutex, or Library.syncMode.queue which means asynchronous function invocations will get synchronized with a library global call queue (more on that later)

Methods:

  • isSymbolExists: returns true if the specified symbol exists in the library
  • release: release loaded shared library's resources (please note that Library is not a Disposable because you'll need to call this method in very rare situations)
  • declare: parses and process a declaration string. Its functions are declared with the default call mode
  • declareSync: parses and process a declaration string. Its functions are declared as synchronous
  • declareAsync: parses and process a declaration string. Its functions are declared as asynchronous
  • function: declares a function with the default call mode
  • syncFunction: declares a synchronous function
  • asyncFunction: declares an asynchronous function
  • struct: declares a structure
  • union: declares an union
  • array: declares an array
  • callback: declares a callback

Properties:

  • functions: declared functions (metadata)
  • structs: declared structures (metadata)
  • unions: declared unions (metadata)
  • arrays: declared arrays (metadata)
  • callbacks: declared callbacks (metadata)

ref

Let's take a look at ref before going into the details (credits for TooTallNate). ref is a native type system with pointers and other types those are required to address C based interfaces of native shared libraries. It also has a native interface compatible types for structs, unions and arrays.

In fastcall there are a bundled versions of ref, ref-array, ref-struct and ref-union. Those are 100% compatible with the originals, they are there because I didn't wanted to have a CMake.js based module to depend on anything node-gyp based stuff. Bundled versions are built with CMake.js. The only exception is ref-array, fastcall's version contains some interface breaking changes for the sake of a way much better performance.

const fastcall = require('fastcall');

// 100% ref@latest built with CMake.js:
const ref = fastcall.ref;

// 100% ref-struct@latest
const StructType = fastcall.StructType;

// 100% ref-union@latest
const UnionType = fastcall.UnionType;

// modified ref-struct@latest
const ArrayType = fastcall.ArrayType;

ref-array changes:

See the original FAQ there: https://github.com/TooTallNate/ref-array

There are two huge performance bottleneck exists in this module. The first is the price of array indexer syntax:

const IntArray = new ArrayType('int');
const arr = new IntArray(5);
arr[1] = 1;
arr[0] = arr[1] + 1;

Those [0] and [1] are implemented by defining Object properties named "0" and "1" respectively. For supporting a length of 100, there will be 100 properties created on the fly. On the other hand those indexing numbers gets converted to string on each access.

Because of that, fastcalls version uses get and set method for indexing:

const IntArray = new ArrayType('int');
const arr = new IntArray(5);
arr.set(1, 1);
arr.set(0, arr.get(1) + 1);

Not that nice, but way much faster than the original.

The other is the continous reinterpretation of a dynamically sized array.

const IntArray = new ArrayType
View on GitHub
GitHub Stars205
CategoryDevelopment
Updated5mo ago
Forks13

Languages

JavaScript

Security Score

82/100

Audited on Oct 21, 2025

No findings