Node.js Binding — Running the Benchmarks¶
The tulip_rs_node vs technicalindicators / indicatorts benchmark results are part of the
Standard Performance page — select the Node Binding tab there to see the full comparison table.
This page covers how to install the bench package and run it locally.
Setup¶
Prerequisites¶
| Requirement | Notes |
|---|---|
| Node.js 18+ | LTS recommended |
| tulip-rs-node built | Run npm run build in tulip_rs_node/ first |
| Docker + Docker Compose v2 | For the benchmark databases |
1. Build tulip-rs-node¶
The bench package references tulip-rs-node as a local file dependency.
Build it first if you have not already:
2. Start the databases¶
The Node benchmarks use the same PostgreSQL instance as the Rust and Python benchmarks.
The Node benchmark views (node_performance_comparison, node_avg_options_comparison)
are applied automatically via 05_node_benchmark_views.sql on first boot.
cd tulip_rs/tulip_test/docker
docker compose up -d
# First start takes ~30 s while the SQL seed data loads.
# The pg_data volume keeps data across restarts.
| Database | Purpose |
|---|---|
stocks |
Read-only OHLCV market data (4 symbols, ~6,705 bars each) |
indicator_benchmark |
Benchmark run metadata and result storage |
3. Install the bench package¶
4. Configure the environment¶
Create or edit tulip_rs_node/bench/.env:
# Connection to the stocks database (read-only market data)
DATABASE_URL=postgres://tulip:tulip@localhost:5432/stocks
# Connection to the benchmark results database
BENCHMARK_DATABASE_URL=postgres://tulip:tulip@localhost:5432/indicator_benchmark
# Set to 1 to write timing rows into indicator_benchmark after each run.
BENCHMARK_LOG_TO_DB=0
Tip
dotenv walks upward from the working directory to find .env, so running
npm run bench from tulip_rs_node/bench/ picks it up automatically.
No shell exports needed.
Running¶
cd tulip_rs_node/bench
# All 81 indicators — stdout only
npm run bench
# All 81 indicators — write results to benchmark DB
npm run bench:db
# Specific indicators by name — stdout only
node src/run_all.js sma
node src/run_all.js ema rsi macd adx
# Specific indicators — write to DB
BENCHMARK_LOG_TO_DB=1 node src/run_all.js ema rsi macd
Example output¶
================================================================
tulip-rs Node.js Benchmark Suite
================================================================
[1/3] Loading stock data ...
loaded 6,705 bars BHP/ASX
loaded 6,705 bars CBA/ASX
loaded 6,705 bars AAPL/NYSE
loaded 6,705 bars MSFT/NYSE
[2/3] DB logging disabled (BENCHMARK_LOG_TO_DB=0) — stdout only
[3/3] Running benchmarks ...
----------------------------------------------------------------
EMA
----------------------------------------------------------------
tulip_rs_node BHP_ASX [14] 12,942 ns ± 312
technicalindicators BHP_ASX [14] 144,942 ns ± 4,210 × 11.2 slower
indicatorts BHP_ASX [14] 19,981 ns ± 589 × 1.5 slower
tulip_rs_node BHP_ASX [20] 12,983 ns ± 287
...
================================================================
Finished 81 indicator(s) in 284.7s
================================================================
Methodology¶
| Item | Detail |
|---|---|
| Reference libraries | technicalindicators (anandanand84) and indicatorts (Onur Cinar) — pure JS/TS |
| Input data | Real OHLCV market data — 6,705 bars per asset |
| Stocks | BHP/ASX, CBA/ASX, AAPL/NYSE, MSFT/NYSE |
| Option sets | 4 per indicator (same sets as the Rust benchmarks); results averaged across all 4 |
| Timing harness | performance.now() — 10 back-to-back calls per sample, 30 independent samples |
| Inputs | Float64Array passed to tulip_rs_node; reference libraries receive number[] (their native format) |
| Indicator count | 81 indicators total; reference library coverage is a subset |
nAPI boundary cost
Each call from JavaScript into the native tulip_rs_node binding carries a fixed overhead
for argument marshalling and Float64Array ownership handoff. This cost is roughly
4–25 µs per call depending on the number of input and output series, and is independent
of indicator complexity.
The Rust native times shown in the Standard Performance Node Binding tab reflect the underlying computation before this overhead — the gap between "Rust native" and "tulip_rs_node" columns is the pure nAPI boundary cost.
Reference library coverage
Not every indicator has a technicalindicators or indicatorts equivalent.
Where neither library implements an indicator, only the tulip_rs_node time is recorded.
The results table on the Standard Performance page shows only the 41 indicators where
at least one reference library ran.