Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 50 additions & 50 deletions benchmarks/results-wasm-core.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,160 +3,160 @@
{
"function": "searchsorted_f64",
"tsb": {
"mean_ms": 0.0008374999999999986,
"mean_ms": 0.0016710259999999978,
"iterations": 1000,
"total_ms": 0.8374999999999986
"total_ms": 1.6710259999999977
},
"tsb_wasm": {
"mean_ms": 0.0021370410000000036,
"mean_ms": 0.028031712999999996,
"iterations": 1000,
"total_ms": 2.1370410000000035
"total_ms": 28.031712999999996
},
"wasm_speedup": 0.39189702022562845
"wasm_speedup": 0.05961198304220645
},
{
"function": "searchsorted_many_f64",
"tsb": {
"mean_ms": 0.0034607080000000037,
"mean_ms": 0.003219333999999989,
"iterations": 1000,
"total_ms": 3.460708000000004
"total_ms": 3.2193339999999893
},
"tsb_wasm": {
"mean_ms": 0.005887209000000006,
"mean_ms": 0.034363152,
"iterations": 1000,
"total_ms": 5.887209000000006
"total_ms": 34.363152
},
"wasm_speedup": 0.5878350845026905
"wasm_speedup": 0.09368564327277047
},
{
"function": "argsort_f64",
"tsb": {
"mean_ms": 0.1133425,
"mean_ms": 0.21699383,
"iterations": 1000,
"total_ms": 113.3425
"total_ms": 216.99383
},
"tsb_wasm": {
"mean_ms": 0.01931533300000001,
"mean_ms": 0.057585872999999996,
"iterations": 1000,
"total_ms": 19.31533300000001
"total_ms": 57.58587299999999
},
"wasm_speedup": 5.8680065210369365
"wasm_speedup": 3.768178178005568
},
{
"function": "searchsorted_str",
"tsb": {
"mean_ms": 0.00027391700000001153,
"mean_ms": 0.0009189930000000004,
"iterations": 1000,
"total_ms": 0.2739170000000115
"total_ms": 0.9189930000000004
},
"tsb_wasm": {
"mean_ms": 0.002622792000000004,
"mean_ms": 0.007566859999999963,
"iterations": 1000,
"total_ms": 2.622792000000004
"total_ms": 7.566859999999963
},
"wasm_speedup": 0.10443717992124847,
"wasm_speedup": 0.12144971626275694,
"notes": "String arrays are copied for each WASM call; raw kernel speedup is partially offset by copy overhead."
},
{
"function": "argsort_str",
"tsb": {
"mean_ms": 0.0004121250000000032,
"mean_ms": 0.002078239999999994,
"iterations": 1000,
"total_ms": 0.4121250000000032
"total_ms": 2.078239999999994
},
"tsb_wasm": {
"mean_ms": 0.0014684579999999982,
"mean_ms": 0.004294708000000014,
"iterations": 1000,
"total_ms": 1.4684579999999983
"total_ms": 4.294708000000014
},
"wasm_speedup": 0.28065154059564773,
"wasm_speedup": 0.4839071713373731,
"notes": "Same array-copy caveat as searchsorted_str."
},
{
"function": "nat_compare",
"tsb": {
"mean_ms": 0.0004783749999999998,
"mean_ms": 0.0010837179999999762,
"iterations": 1000,
"total_ms": 0.4783749999999998
"total_ms": 1.0837179999999762
},
"tsb_wasm": {
"mean_ms": 0.001035334000000006,
"mean_ms": 0.004259321,
"iterations": 1000,
"total_ms": 1.035334000000006
"total_ms": 4.259321
},
"wasm_speedup": 0.4620489619774846
"wasm_speedup": 0.25443445093712735
},
{
"function": "nat_sorted",
"tsb": {
"mean_ms": 0.16902662499999996,
"mean_ms": 0.226383055,
"iterations": 1000,
"total_ms": 169.02662499999997
"total_ms": 226.383055
},
"tsb_wasm": {
"mean_ms": 0.257211584,
"mean_ms": 0.5151697199999998,
"iterations": 1000,
"total_ms": 257.211584
"total_ms": 515.1697199999999
},
"wasm_speedup": 0.6571501266443737
"wasm_speedup": 0.4394339306277552
},
{
"function": "nat_argsort",
"tsb": {
"mean_ms": 0.03192166599999996,
"mean_ms": 0.05495564300000001,
"iterations": 1000,
"total_ms": 31.92166599999996
"total_ms": 54.95564300000001
},
"tsb_wasm": {
"mean_ms": 0.035225500000000014,
"mean_ms": 0.06736009400000012,
"iterations": 1000,
"total_ms": 35.22550000000001
"total_ms": 67.36009400000012
},
"wasm_speedup": 0.9062090247122099
"wasm_speedup": 0.815848668500966
}
],
"coverage": {
"unclassified": 0,
"eligible_missing": 0,
"total_core_entries": 121,
"total_core_entries": 143,
"rust_wasm": 6,
"ts_only_ineligible": 115
"ts_only_ineligible": 137
},
"timestamp": "2026-06-27T02:33:54.386Z",
"timestamp": "2026-07-02T09:46:21.112Z",
"slower_than_typescript": [
{
"function": "searchsorted_f64",
"wasm_speedup": 0.39189702022562845,
"wasm_speedup": 0.05961198304220645,
"explanation": "WASM/JS boundary overhead exceeds kernel speedup at this array size."
},
{
"function": "searchsorted_many_f64",
"wasm_speedup": 0.5878350845026905,
"wasm_speedup": 0.09368564327277047,
"explanation": "WASM/JS boundary overhead exceeds kernel speedup at this array size."
},
{
"function": "searchsorted_str",
"wasm_speedup": 0.10443717992124847,
"wasm_speedup": 0.12144971626275694,
"explanation": "String arrays are copied for each WASM call; raw kernel speedup is partially offset by copy overhead."
},
{
"function": "argsort_str",
"wasm_speedup": 0.28065154059564773,
"wasm_speedup": 0.4839071713373731,
"explanation": "Same array-copy caveat as searchsorted_str."
},
{
"function": "nat_compare",
"wasm_speedup": 0.4620489619774846,
"wasm_speedup": 0.25443445093712735,
"explanation": "WASM/JS boundary overhead exceeds kernel speedup at this array size."
},
{
"function": "nat_sorted",
"wasm_speedup": 0.6571501266443737,
"wasm_speedup": 0.4394339306277552,
"explanation": "WASM/JS boundary overhead exceeds kernel speedup at this array size."
},
{
"function": "nat_argsort",
"wasm_speedup": 0.9062090247122099,
"wasm_speedup": 0.815848668500966,
"explanation": "WASM/JS boundary overhead exceeds kernel speedup at this array size."
}
]
Expand Down
43 changes: 43 additions & 0 deletions benchmarks/wasm-core/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,49 @@ const benchmarks: BenchmarkEntry[] = [];
});
}

// ─── sum_f64 ──────────────────────────────────────────────────────────────────

{
const sumF64Wasm = getWasmFn("sum_f64");
const DATA_F64 = Float64Array.from({ length: 10_000 }, (_, i) => i * 0.5);
const DATA_ARR = Array.from(DATA_F64);
const tsResult = bench(() => DATA_ARR.reduce((acc, v) => acc + v, 0), ITERS);
const wasmResult = bench(() => sumF64Wasm(DATA_F64), ITERS);
benchmarks.push({
function: "sum_f64",
tsb: tsResult,
tsb_wasm: wasmResult,
wasm_speedup: tsResult.mean_ms / wasmResult.mean_ms,
notes: "Scalar sum over 10 000-element f64 array; WASM avoids JS→TS dispatch overhead.",
});
}

// ─── rolling_mean_f64 ────────────────────────────────────────────────────────

{
const rollingMeanWasm = getWasmFn("rolling_mean_f64");
const ROLL_DATA = Float64Array.from({ length: 1_000 }, (_, i) => i * 1.0);
const ROLL_ARR = Array.from(ROLL_DATA);
const window = 10;
const minPeriods = 10;
const tsResult = bench(() => {
return ROLL_ARR.map((_, i) => {
const start = Math.max(0, i + 1 - window);
const slice = ROLL_ARR.slice(start, i + 1).filter((v) => !Number.isNaN(v));
if (slice.length < minPeriods) return null;
return slice.reduce((a, b) => a + b, 0) / slice.length;
});
}, ITERS);
const wasmResult = bench(() => rollingMeanWasm(ROLL_DATA, window, minPeriods), ITERS);
benchmarks.push({
function: "rolling_mean_f64",
tsb: tsResult,
tsb_wasm: wasmResult,
wasm_speedup: tsResult.mean_ms / wasmResult.mean_ms,
notes: "Rolling window mean over 1 000 elements with window=10.",
});
}

// ─── coverage summary ─────────────────────────────────────────────────────────

const coverageManifest = JSON.parse(
Expand Down
115 changes: 115 additions & 0 deletions rust/pkg/tsb_wasm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,66 @@ export function argsort_f64(arr: Float64Array): Uint32Array;
*/
export function argsort_str(arr: string[]): Uint32Array;

/**
* Expanding maximum.
*/
export function expanding_max_f64(data: Float64Array, min_periods: number): Float64Array;

/**
* Expanding mean.
*/
export function expanding_mean_f64(data: Float64Array, min_periods: number): Float64Array;

/**
* Expanding median.
*/
export function expanding_median_f64(data: Float64Array, min_periods: number): Float64Array;

/**
* Expanding minimum.
*/
export function expanding_min_f64(data: Float64Array, min_periods: number): Float64Array;

/**
* Expanding standard deviation (delta degrees-of-freedom `ddof`).
*/
export function expanding_std_f64(data: Float64Array, min_periods: number, ddof: number): Float64Array;

/**
* Expanding sum.
*/
export function expanding_sum_f64(data: Float64Array, min_periods: number): Float64Array;

/**
* Expanding variance (delta degrees-of-freedom `ddof`).
*/
export function expanding_var_f64(data: Float64Array, min_periods: number, ddof: number): Float64Array;

/**
* Maximum of non-NaN values. Returns `NaN` for empty / all-NaN input.
*/
export function max_f64(data: Float64Array): number;

/**
* Arithmetic mean of non-NaN values. Returns `NaN` for empty / all-NaN input,
* matching `Series.mean()`.
*/
export function mean_f64(data: Float64Array): number;

/**
* Median of non-NaN values (middle value of sorted data; average of two
* middle values for even-length arrays). Returns `NaN` for empty / all-NaN
* input, matching `Series.median()`.
*/
export function median_f64(data: Float64Array): number;

/**
* Minimum of non-NaN values. Returns `NaN` for empty / all-NaN input,
* matching `Series.min()` returning `undefined` (coerced to NaN in numeric
* contexts).
*/
export function min_f64(data: Float64Array): number;

/**
* Return the indices that would sort `arr` in natural order.
*/
Expand All @@ -38,6 +98,42 @@ export function nat_compare(a: string, b: string, ignore_case: boolean, reverse:
*/
export function nat_sorted(arr: string[], ignore_case: boolean, reverse: boolean): string[];

/**
* Rolling maximum.
*/
export function rolling_max_f64(data: Float64Array, window: number, min_periods: number): Float64Array;

/**
* Rolling arithmetic mean.
*/
export function rolling_mean_f64(data: Float64Array, window: number, min_periods: number): Float64Array;

/**
* Rolling median.
*/
export function rolling_median_f64(data: Float64Array, window: number, min_periods: number): Float64Array;

/**
* Rolling minimum.
*/
export function rolling_min_f64(data: Float64Array, window: number, min_periods: number): Float64Array;

/**
* Rolling standard deviation (delta degrees-of-freedom `ddof`).
*/
export function rolling_std_f64(data: Float64Array, window: number, min_periods: number, ddof: number): Float64Array;

/**
* Rolling sum. Positions with fewer than `min_periods` non-NaN values → NaN.
*/
export function rolling_sum_f64(data: Float64Array, window: number, min_periods: number): Float64Array;

/**
* Rolling variance (delta degrees-of-freedom `ddof`).
* Positions with fewer than `ddof + 1` valid values → NaN.
*/
export function rolling_var_f64(data: Float64Array, window: number, min_periods: number, ddof: number): Float64Array;

/**
* Binary-search a sorted f64 slice for `value`.
*
Expand Down Expand Up @@ -65,3 +161,22 @@ export function searchsorted_many_str(arr: string[], values: string[], side_righ
* Binary-search a sorted array of strings for `value`.
*/
export function searchsorted_str(arr: string[], value: string, side_right: boolean): number;

/**
* Sample standard deviation with delta degrees-of-freedom `ddof`.
* Returns `NaN` when fewer than `ddof + 1` valid values exist.
*/
export function std_f64(data: Float64Array, ddof: number): number;

/**
* Sum of non-NaN values. Returns `0.0` when there are no valid values,
* matching `Series.sum()` on an all-null / empty series.
*/
export function sum_f64(data: Float64Array): number;

/**
* Sample variance of non-NaN values with delta degrees-of-freedom `ddof`.
* Returns `NaN` when fewer than `ddof + 1` valid values exist, matching
* `Series.var(ddof)`.
*/
export function var_f64(data: Float64Array, ddof: number): number;
Loading
Loading