SkillAgentSearch skills...

Exprtk.js

Node.js bindings for the Mathematical Expression Toolkit

Install / Use

/learn @mmomtchev/Exprtk.js
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ExprTk.js

License: ISC npm version Node.js CI Test published packages codecov

https://exprtk.js.org

Node.js bindings for ExprTk (Github) by @ArashPartow

ExprTk.js supports both synchronous and asynchronous background execution of thunks precompiled from a string including asynchronous and multithreaded versions of TypedArray.prototype.map and TypedArray.prototype.reduce and a synchronous multi-threaded version of TypedArray.prototype.map.

Its main advantage is that it allows deferring of heavy computation for asynchronous execution in a background thread - something that Node.js/V8 does not allow without the very complex mechanisms of worker_threads.

Even in single-threaded synchronous mode ExprTk.js outperforms the native JS TypedArray.prototype.map running in V8 by a significant margin for all types and array sizes and it comes very close to a direct iterative for loop.

It also supports being directly called from native add-ons, including native threads, without any synchronization with V8, allowing JS code to drive multi-threaded mathematical transformations.

It has two main use cases:

  • Performing heavy mathematical calculation in Express-like frameworks which are not allowed to block
  • Speed-up of back-end calculation by parallelizing over multiple cores

Installation

ExprTk.js uses node-pre-gyp and it comes with pre-built binaries for x86-64 for Linux (baseline is Ubuntu 18.04), Windows and OS X.

Since version 2.0.1, the minimum required Node.js version is 10.16.0.

npm install exprtk.js

If your platform is not supported, you can rebuild the binaries:

npm install exprtk.js --build-from-source

If you do not use the integer types, you can obtain a significantly smaller binary by doing:

npm install exprtk.js --build-from-source --disable_int

However this won't have any effect on the startup times, since the addon uses lazy binding and does not load code that is not used.

Rebuilding requires a working C++17 environment. It has been tested with g++, clang and MSVC 2019.

Usage

Different methods of traversal work better for different array sizes, you should probably run/adjust the benchmarks - npm run bench - to see for yourself.

For very small array sizes, the fast setup of eval/evalAsync will be better. For large arrays, the tight internal loop of map()/mapAsync() will be better.

When launching a large number of parallel operations, unless the expression is very complex, the bottleneck will be the cache/memory bandwidth.

The original documentation of ExprTk and the syntax used for the expressions is available here: https://github.com/ArashPartow/exprtk

When launching an asynchronous operation, the scalar arguments will be copied and any TypedArrays will be locked in place and protected from the GC. The whole operation, including traversal and evaluation will happen entirely in a pre-existing background thread picked from a pool and won't solicit the main thread until completion.

Refer to the ExprTk manual for the full expression syntax.

Parallelism

1.0 (obsolete)

An Expression is not reentrant so multiple concurrent evaluations of the same Expression object will wait on one another. This is something that is taken care by the module itself - whether it is called from JS or from C/C++. Multiple evaluations on multiple Expression objects will run in parallel up to the limit set by the Node.js environment variable UV_THREADPOOL_SIZE. Mixing synchronous and asynchronous evaluations is supported, but a synchronous evaluation will block the event loop until all asynchronous evaluations on that same object are finished. If an evaluation of an Expression object has to wait for a previous evaluation of the same object to complete, the two evaluations will use two thread pool slots. This means that starting UV_THREADPOOL_SIZE evaluations on a single object can tie down the whole thread pool until the first one is completed.

2.0 (current)

A single Expression object can contain multiple ExprTk expression instances that are compiled on-demand when needed up to a limit set by the maxParallel instance property. The global number of available threads can be set by using the environment variable EXPRTKJS_THREADS and it is independent of Node.js/libuv's own async work mechanism. It can be read from the maxParallel static class property. The actual peak instances usage of an Expression object can be checked by reading the maxActive instance property.

Simple examples

// internal type will be Float64 (C++ double)
const expr = require("exprtk.js").Float64;

// arithmetic mean
const mean = new expr('(a + b) / 2');

const m = mean.eval({a: 2, b: 4});

Array traversal with map()/mapAsync()

const inputArray = new Float64Array(n);
// clamp to a range
const clamp = new expr('clamp(minv, x, maxv)', ['minv', 'x', 'maxv']);

// these are equivalent
const resultingArray = clamp.map(inputArray, 'x', 5, 10);
const resultingArray = clamp.map(inputArray, 'x', {minv: 5, maxv: 10});

// async
const resultingArray = await clamp.mapAsync(inputArray, 'x', 5, 10);
const resultingArray = await clamp.mapAsync(inputArray, 'x', {minv: 5, maxv: 10});

// OpenMP-style (4 threads map)
const resultingArray = clamp.map(4, inputArray, 'x', 5, 10);
const resultingArray = await clamp.mapAsync(4, inputArray, 'x', {minv: 5, maxv: 10});

Array traversal with reduce()/reduceAsync()

const inputArray = new Float64Array(n);
// sum n-powers
const sumPow = new expr('a + pow(x, n)', ['a', 'x', 'n']);
// these are equivalent
const sumSquares = sumPow.reduce(inputArray, 'x', 'a', 0, 2);
const sumSquares = sumPow.reduce(inputArray, 'x', 'a', 0, {p: 2});

// async
const sumSquares = await sumPow.reduceAsync(inputArray, 'x', 'a', 0, 2);
const sumSquares = await sumPow.reduceAsync(inputArray, 'x', 'a', 0, {p: 2});

Explicit traversal by using an ExprTk for loop

The data type and the array size must be known when compiling (constructing) the expression. ExprTk supports only fixed-size arrays.

const inputArray = new Float64Array(n);
const expr = require("exprtk.js").Float64;

const mean = new expr(
    'var r := 0;' + 
    'for (var i := 0; i < x[]; i += 1)' +
    '{ r += x[i]; };' +
    'r / x[];',
    [], { 'x': 6 });

const r = mean.eval(inputArray);
const r = await mean.evalAsync(inputArray);

Types

ExprTk.js supports the following types:


| JS | C/C++ | | ------- | -------------------------- | | Float64 | double | | Float32 | float | | Uint32 | uint32_t (unsigned long) | | Int32 | int32_t (long) | | Uint16 | uint16_t (unsigned short) | | Int16 | int16_t (short) | | Uint8 | uint8_t (unsigned char) | | Int8 | int8_t (char) |

Strided arrays

Starting from version 2.1, ExprTk.js supports strided N-dimensional arrays. Both the scijs/ndarray and @stdlib/ndarray forms are supported.

An ndarray can be used in place of a normal linear array in cwise/cwiseAsync. If more than one ndarray is passed, all ndarrays must have the same shape. The arrays are always traversed in a positive row-major order, meaning that other orders incur a performance penalty when the array does not fit in the CPU L1 cache.

API

<!-- Generated by documentation.js. Update this documentation by updating the source code. -->

Table of Contents

Expression

Parameters

  • expression string function
  • variables Array<string>? An array containing all the scalar variables' names, will be determined automatically if omitted, however order won't be guaranteed, scalars are passed by value
  • vectors Record<string, number>? An object containing all the vector variables' names and their sizes, vector size must be known at compilation (object construction), vectors are passed by reference and can be modified by the ExprTk expression

Examples

// This determines the internally used type
const expr = require("exprtk.js").Float64;

// arithmetic mean of 2 variables
const mean = new Expression('(a+b)/2', ['a', 'b']);

// naive stddev of an arra
View on GitHub
GitHub Stars15
CategoryDevelopment
Updated4d ago
Forks1

Languages

C++

Security Score

95/100

Audited on Mar 23, 2026

No findings