chore: tidy up benchmark code (#17490)

* add create_test utility

* add missing import

* DRY

* DRY out

* mol (this affects benchmark times, because create_test uses 1e3 iterations whereas the old code used 1e4)

* move utils.js

* WIP

* DRY

* reduce indirection

* more

* tidy up

* tidy up

* colocate

* DRY out
pull/17497/head
Rich Harris 8 months ago committed by GitHub
parent 16e9c70d1e
commit 2eb54f57ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,12 +1,5 @@
import { kairo_avoidable_owned, kairo_avoidable_unowned } from './kairo/kairo_avoidable.js'; import fs from 'node:fs';
import { kairo_broad_owned, kairo_broad_unowned } from './kairo/kairo_broad.js'; import path from 'node:path';
import { kairo_deep_owned, kairo_deep_unowned } from './kairo/kairo_deep.js';
import { kairo_diamond_owned, kairo_diamond_unowned } from './kairo/kairo_diamond.js';
import { kairo_mux_unowned, kairo_mux_owned } from './kairo/kairo_mux.js';
import { kairo_repeated_unowned, kairo_repeated_owned } from './kairo/kairo_repeated.js';
import { kairo_triangle_owned, kairo_triangle_unowned } from './kairo/kairo_triangle.js';
import { kairo_unstable_owned, kairo_unstable_unowned } from './kairo/kairo_unstable.js';
import { mol_bench_owned, mol_bench_unowned } from './mol_bench.js';
import { import {
sbench_create_0to1, sbench_create_0to1,
sbench_create_1000to1, sbench_create_1000to1,
@ -19,10 +12,14 @@ import {
sbench_create_4to1, sbench_create_4to1,
sbench_create_signals sbench_create_signals
} from './sbench.js'; } from './sbench.js';
import { fileURLToPath } from 'node:url';
import { create_test } from './util.js';
// This benchmark has been adapted from the js-reactivity-benchmark (https://github.com/milomg/js-reactivity-benchmark) // This benchmark has been adapted from the js-reactivity-benchmark (https://github.com/milomg/js-reactivity-benchmark)
// Not all tests are the same, and many parts have been tweaked to capture different data. // Not all tests are the same, and many parts have been tweaked to capture different data.
const dirname = path.dirname(fileURLToPath(import.meta.url));
export const reactivity_benchmarks = [ export const reactivity_benchmarks = [
sbench_create_signals, sbench_create_signals,
sbench_create_0to1, sbench_create_0to1,
@ -33,23 +30,16 @@ export const reactivity_benchmarks = [
sbench_create_1to2, sbench_create_1to2,
sbench_create_1to4, sbench_create_1to4,
sbench_create_1to8, sbench_create_1to8,
sbench_create_1to1000, sbench_create_1to1000
kairo_avoidable_owned,
kairo_avoidable_unowned,
kairo_broad_owned,
kairo_broad_unowned,
kairo_deep_owned,
kairo_deep_unowned,
kairo_diamond_owned,
kairo_diamond_unowned,
kairo_triangle_owned,
kairo_triangle_unowned,
kairo_mux_owned,
kairo_mux_unowned,
kairo_repeated_owned,
kairo_repeated_unowned,
kairo_unstable_owned,
kairo_unstable_unowned,
mol_bench_owned,
mol_bench_unowned
]; ];
for (const file of fs.readdirSync(`${dirname}/tests`)) {
if (!file.includes('.bench.')) continue;
const name = file.replace('.bench.js', '');
const module = await import(`${dirname}/tests/${file}`);
const { owned, unowned } = create_test(name, module.default);
reactivity_benchmarks.push(owned, unowned);
}

@ -1,91 +0,0 @@
import { assert, fastest_test } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
import { busy } from './util.js';
function setup() {
let head = $.state(0);
let computed1 = $.derived(() => $.get(head));
let computed2 = $.derived(() => ($.get(computed1), 0));
let computed3 = $.derived(() => (busy(), $.get(computed2) + 1)); // heavy computation
let computed4 = $.derived(() => $.get(computed3) + 2);
let computed5 = $.derived(() => $.get(computed4) + 3);
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(computed5);
busy(); // heavy side effect
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
assert($.get(computed5) === 6);
for (let i = 0; i < 1000; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(computed5) === 6);
}
}
};
}
export async function kairo_avoidable_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
destroy();
return {
benchmark: 'kairo_avoidable_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_avoidable_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_avoidable_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -1,97 +0,0 @@
import { assert, fastest_test } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
function setup() {
let head = $.state(0);
let last = head;
let counter = 0;
const destroy = $.effect_root(() => {
for (let i = 0; i < 50; i++) {
let current = $.derived(() => {
return $.get(head) + i;
});
let current2 = $.derived(() => {
return $.get(current) + 1;
});
$.render_effect(() => {
$.get(current2);
counter++;
});
last = current2;
}
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
counter = 0;
for (let i = 0; i < 50; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(last) === i + 50);
}
assert(counter === 50 * 50);
}
};
}
export async function kairo_broad_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
destroy();
return {
benchmark: 'kairo_broad_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_broad_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_broad_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -1,97 +0,0 @@
import { assert, fastest_test } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
let len = 50;
const iter = 50;
function setup() {
let head = $.state(0);
let current = head;
for (let i = 0; i < len; i++) {
let c = current;
current = $.derived(() => {
return $.get(c) + 1;
});
}
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(current);
counter++;
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
counter = 0;
for (let i = 0; i < iter; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(current) === len + i);
}
assert(counter === iter);
}
};
}
export async function kairo_deep_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
destroy();
return {
benchmark: 'kairo_deep_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_deep_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_deep_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -1,101 +0,0 @@
import { assert, fastest_test } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
let width = 5;
function setup() {
let head = $.state(0);
let current = [];
for (let i = 0; i < width; i++) {
current.push(
$.derived(() => {
return $.get(head) + 1;
})
);
}
let sum = $.derived(() => {
return current.map((x) => $.get(x)).reduce((a, b) => a + b, 0);
});
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(sum);
counter++;
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
assert($.get(sum) === 2 * width);
counter = 0;
for (let i = 0; i < 500; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(sum) === (i + 1) * width);
}
assert(counter === 500);
}
};
}
export async function kairo_diamond_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
destroy();
return {
benchmark: 'kairo_diamond_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_diamond_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_diamond_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -1,94 +0,0 @@
import { assert, fastest_test } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
function setup() {
let heads = new Array(100).fill(null).map((_) => $.state(0));
const mux = $.derived(() => {
return Object.fromEntries(heads.map((h) => $.get(h)).entries());
});
const splited = heads
.map((_, index) => $.derived(() => $.get(mux)[index]))
.map((x) => $.derived(() => $.get(x) + 1));
const destroy = $.effect_root(() => {
splited.forEach((x) => {
$.render_effect(() => {
$.get(x);
});
});
});
return {
destroy,
run() {
for (let i = 0; i < 10; i++) {
$.flush(() => {
$.set(heads[i], i);
});
assert($.get(splited[i]) === i + 1);
}
for (let i = 0; i < 10; i++) {
$.flush(() => {
$.set(heads[i], i * 2);
});
assert($.get(splited[i]) === i * 2 + 1);
}
}
};
}
export async function kairo_mux_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
destroy();
return {
benchmark: 'kairo_mux_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_mux_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_mux_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -1,98 +0,0 @@
import { assert, fastest_test } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
let size = 30;
function setup() {
let head = $.state(0);
let current = $.derived(() => {
let result = 0;
for (let i = 0; i < size; i++) {
result += $.get(head);
}
return result;
});
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(current);
counter++;
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
assert($.get(current) === size);
counter = 0;
for (let i = 0; i < 100; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(current) === i * size);
}
assert(counter === 100);
}
};
}
export async function kairo_repeated_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
destroy();
return {
benchmark: 'kairo_repeated_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_repeated_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_repeated_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -1,111 +0,0 @@
import { assert, fastest_test } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
let width = 10;
function count(number) {
return new Array(number)
.fill(0)
.map((_, i) => i + 1)
.reduce((x, y) => x + y, 0);
}
function setup() {
let head = $.state(0);
let current = head;
let list = [];
for (let i = 0; i < width; i++) {
let c = current;
list.push(current);
current = $.derived(() => {
return $.get(c) + 1;
});
}
let sum = $.derived(() => {
return list.map((x) => $.get(x)).reduce((a, b) => a + b, 0);
});
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(sum);
counter++;
});
});
return {
destroy,
run() {
const constant = count(width);
$.flush(() => {
$.set(head, 1);
});
assert($.get(sum) === constant);
counter = 0;
for (let i = 0; i < 100; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(sum) === constant - width + i * width);
}
assert(counter === 100);
}
};
}
export async function kairo_triangle_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
destroy();
return {
benchmark: 'kairo_triangle_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_triangle_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_triangle_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -1,97 +0,0 @@
import { assert, fastest_test } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
function setup() {
let head = $.state(0);
const double = $.derived(() => $.get(head) * 2);
const inverse = $.derived(() => -$.get(head));
let current = $.derived(() => {
let result = 0;
for (let i = 0; i < 20; i++) {
result += $.get(head) % 2 ? $.get(double) : $.get(inverse);
}
return result;
});
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(current);
counter++;
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
assert($.get(current) === 40);
counter = 0;
for (let i = 0; i < 100; i++) {
$.flush(() => {
$.set(head, i);
});
}
assert(counter === 100);
}
};
}
export async function kairo_unstable_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
destroy();
return {
benchmark: 'kairo_unstable_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function kairo_unstable_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run();
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run();
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'kairo_unstable_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -1,6 +0,0 @@
export function busy() {
let a = 0;
for (let i = 0; i < 1_00; i++) {
a++;
}
}

@ -1,3 +1,4 @@
/** @import { Source } from '../../../packages/svelte/src/internal/client/types.js' */
import { fastest_test } from '../../utils.js'; import { fastest_test } from '../../utils.js';
import * as $ from '../../../packages/svelte/src/internal/client/index.js'; import * as $ from '../../../packages/svelte/src/internal/client/index.js';
@ -7,360 +8,180 @@ const COUNT = 1e5;
* @param {number} n * @param {number} n
* @param {any[]} sources * @param {any[]} sources
*/ */
function create_data_signals(n, sources) { function create_sources(n, sources) {
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
sources[i] = $.state(i); sources[i] = $.state(i);
} }
return sources; return sources;
} }
/** /**
* @param {number} i * @param {Source<number>} source
*/ */
function create_computation_0(i) { function create_derived(source) {
$.derived(() => i); $.derived(() => $.get(source));
} }
/** /**
* @param {any} s1 *
*/ * @param {string} label
function create_computation_1(s1) { * @param {(n: number, sources: Array<Source<number>>)} fn
$.derived(() => $.get(s1)); * @param {number} count
} * @param {number} num_sources
/**
* @param {any} s1
* @param {any} s2
*/ */
function create_computation_2(s1, s2) { function create_sbench_test(label, count, num_sources, fn) {
$.derived(() => $.get(s1) + $.get(s2)); return async () => {
} // Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
function create_computation_1000(ss, offset) { fn(count, create_sources(num_sources, []));
$.derived(() => {
let sum = 0;
for (let i = 0; i < 1000; i++) {
sum += $.get(ss[offset + i]);
} }
return sum;
});
}
/** const { timing } = await fastest_test(10, () => {
* @param {number} n const destroy = $.effect_root(() => {
*/ for (let i = 0; i < 10; i++) {
function create_computations_0to1(n) { fn(count, create_sources(num_sources, []));
for (let i = 0; i < n; i++) { }
create_computation_0(i); });
} destroy();
} });
/**
* @param {number} n
* @param {any[]} sources
*/
function create_computations_1to1(n, sources) {
for (let i = 0; i < n; i++) {
const source = sources[i];
create_computation_1(source);
}
}
/** return {
* @param {number} n benchmark: label,
* @param {any[]} sources time: timing.time.toFixed(2),
*/ gc_time: timing.gc_time.toFixed(2)
function create_computations_2to1(n, sources) { };
for (let i = 0; i < n; i++) { };
create_computation_2(sources[i * 2], sources[i * 2 + 1]);
}
} }
function create_computation_4(s1, s2, s3, s4) { export const sbench_create_signals = create_sbench_test(
$.derived(() => $.get(s1) + $.get(s2) + $.get(s3) + $.get(s4)); 'sbench_create_signals',
} COUNT,
COUNT,
create_sources
);
function create_computations_1000to1(n, sources) { export const sbench_create_0to1 = create_sbench_test('sbench_create_0to1', COUNT, 0, (n) => {
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
create_computation_1000(sources, i * 1000); $.derived(() => i);
}
}
function create_computations_1to2(n, sources) {
for (let i = 0; i < n / 2; i++) {
const source = sources[i];
create_computation_1(source);
create_computation_1(source);
}
}
function create_computations_1to4(n, sources) {
for (let i = 0; i < n / 4; i++) {
const source = sources[i];
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
}
}
function create_computations_1to8(n, sources) {
for (let i = 0; i < n / 8; i++) {
const source = sources[i];
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
create_computation_1(source);
} }
} });
function create_computations_1to1000(n, sources) { export const sbench_create_1to1 = create_sbench_test(
for (let i = 0; i < n / 1000; i++) { 'sbench_create_1to1',
const source = sources[i]; COUNT,
for (let j = 0; j < 1000; j++) { COUNT,
create_computation_1(source); (n, sources) => {
for (let i = 0; i < n; i++) {
create_derived(sources[i]);
} }
} }
} );
function create_computations_4to1(n, sources) {
for (let i = 0; i < n; i++) {
create_computation_4(
sources[i * 4],
sources[i * 4 + 1],
sources[i * 4 + 2],
sources[i * 4 + 3]
);
}
}
/**
* @param {any} fn
* @param {number} count
* @param {number} scount
*/
function bench(fn, count, scount) {
let sources = create_data_signals(scount, []);
fn(count, sources);
}
export async function sbench_create_signals() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_data_signals, COUNT, COUNT);
}
const { timing } = await fastest_test(10, () => { export const sbench_create_2to1 = create_sbench_test(
for (let i = 0; i < 100; i++) { 'sbench_create_2to1',
bench(create_data_signals, COUNT, COUNT); COUNT / 2,
COUNT,
(n, sources) => {
for (let i = 0; i < n; i++) {
$.derived(() => $.get(sources[i * 2]) + $.get(sources[i * 2 + 1]));
} }
});
return {
benchmark: 'sbench_create_signals',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_0to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_0to1, COUNT, 0);
} }
);
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => { export const sbench_create_4to1 = create_sbench_test(
for (let i = 0; i < 10; i++) { 'sbench_create_4to1',
bench(create_computations_0to1, COUNT, 0); COUNT / 4,
} COUNT,
}); (n, sources) => {
destroy(); for (let i = 0; i < n; i++) {
}); $.derived(
() =>
return { $.get(sources[i * 4]) +
benchmark: 'sbench_create_0to1', $.get(sources[i * 4 + 1]) +
time: timing.time.toFixed(2), $.get(sources[i * 4 + 2]) +
gc_time: timing.gc_time.toFixed(2) $.get(sources[i * 4 + 3])
}; );
} }
export async function sbench_create_1to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to1, COUNT, COUNT);
}
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => {
for (let i = 0; i < 10; i++) {
bench(create_computations_1to1, COUNT, COUNT);
}
});
destroy();
});
return {
benchmark: 'sbench_create_1to1',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_2to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_2to1, COUNT / 2, COUNT);
}
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => {
for (let i = 0; i < 10; i++) {
bench(create_computations_2to1, COUNT / 2, COUNT);
}
});
destroy();
});
return {
benchmark: 'sbench_create_2to1',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_4to1() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_4to1, COUNT / 4, COUNT);
} }
);
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => { export const sbench_create_1000to1 = create_sbench_test(
for (let i = 0; i < 10; i++) { 'sbench_create_1000to1',
bench(create_computations_4to1, COUNT / 4, COUNT); COUNT / 1000,
} COUNT,
}); (n, sources) => {
destroy(); for (let i = 0; i < n; i++) {
}); const offset = i * 1000;
return { $.derived(() => {
benchmark: 'sbench_create_4to1', let sum = 0;
time: timing.time.toFixed(2), for (let i = 0; i < 1000; i++) {
gc_time: timing.gc_time.toFixed(2) sum += $.get(sources[offset + i]);
}; }
} return sum;
});
export async function sbench_create_1000to1() { }
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1000to1, COUNT / 1000, COUNT);
} }
);
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => { export const sbench_create_1to2 = create_sbench_test(
for (let i = 0; i < 10; i++) { 'sbench_create_1to2',
bench(create_computations_1000to1, COUNT / 1000, COUNT); COUNT,
} COUNT / 2,
}); (n, sources) => {
destroy(); for (let i = 0; i < n / 2; i++) {
}); const source = sources[i];
create_derived(source);
return { create_derived(source);
benchmark: 'sbench_create_1000to1', }
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_1to2() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to2, COUNT, COUNT / 2);
} }
);
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => { export const sbench_create_1to4 = create_sbench_test(
for (let i = 0; i < 10; i++) { 'sbench_create_1to4',
bench(create_computations_1to2, COUNT, COUNT / 2); COUNT,
} COUNT / 4,
}); (n, sources) => {
destroy(); for (let i = 0; i < n / 4; i++) {
}); const source = sources[i];
create_derived(source);
return { create_derived(source);
benchmark: 'sbench_create_1to2', create_derived(source);
time: timing.time.toFixed(2), create_derived(source);
gc_time: timing.gc_time.toFixed(2) }
};
}
export async function sbench_create_1to4() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to4, COUNT, COUNT / 4);
} }
);
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => { export const sbench_create_1to8 = create_sbench_test(
for (let i = 0; i < 10; i++) { 'sbench_create_1to8',
bench(create_computations_1to4, COUNT, COUNT / 4); COUNT,
} COUNT / 8,
}); (n, sources) => {
destroy(); for (let i = 0; i < n / 8; i++) {
}); const source = sources[i];
create_derived(source);
return { create_derived(source);
benchmark: 'sbench_create_1to4', create_derived(source);
time: timing.time.toFixed(2), create_derived(source);
gc_time: timing.gc_time.toFixed(2) create_derived(source);
}; create_derived(source);
} create_derived(source);
create_derived(source);
export async function sbench_create_1to8() { }
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to8, COUNT, COUNT / 8);
} }
);
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => { export const sbench_create_1to1000 = create_sbench_test(
for (let i = 0; i < 10; i++) { 'sbench_create_1to1000',
bench(create_computations_1to8, COUNT, COUNT / 8); COUNT,
COUNT / 1000,
(n, sources) => {
for (let i = 0; i < n / 1000; i++) {
const source = sources[i];
for (let j = 0; j < 1000; j++) {
create_derived(source);
} }
}); }
destroy();
});
return {
benchmark: 'sbench_create_1to8',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function sbench_create_1to1000() {
// Do 3 loops to warm up JIT
for (let i = 0; i < 3; i++) {
bench(create_computations_1to1000, COUNT, COUNT / 1000);
} }
);
const { timing } = await fastest_test(10, () => {
const destroy = $.effect_root(() => {
for (let i = 0; i < 10; i++) {
bench(create_computations_1to1000, COUNT, COUNT / 1000);
}
});
destroy();
});
return {
benchmark: 'sbench_create_1to1000',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -0,0 +1,35 @@
import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
import { busy } from '../util.js';
export default () => {
let head = $.state(0);
let computed1 = $.derived(() => $.get(head));
let computed2 = $.derived(() => ($.get(computed1), 0));
let computed3 = $.derived(() => (busy(), $.get(computed2) + 1)); // heavy computation
let computed4 = $.derived(() => $.get(computed3) + 2);
let computed5 = $.derived(() => $.get(computed4) + 3);
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(computed5);
busy(); // heavy side effect
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
assert($.get(computed5) === 6);
for (let i = 0; i < 1000; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(computed5) === 6);
}
}
};
};

@ -0,0 +1,41 @@
import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
export default () => {
let head = $.state(0);
let last = head;
let counter = 0;
const destroy = $.effect_root(() => {
for (let i = 0; i < 50; i++) {
let current = $.derived(() => {
return $.get(head) + i;
});
let current2 = $.derived(() => {
return $.get(current) + 1;
});
$.render_effect(() => {
$.get(current2);
counter++;
});
last = current2;
}
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
counter = 0;
for (let i = 0; i < 50; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(last) === i + 50);
}
assert(counter === 50 * 50);
}
};
};

@ -0,0 +1,41 @@
import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
let len = 50;
const iter = 50;
export default () => {
let head = $.state(0);
let current = head;
for (let i = 0; i < len; i++) {
let c = current;
current = $.derived(() => {
return $.get(c) + 1;
});
}
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(current);
counter++;
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
counter = 0;
for (let i = 0; i < iter; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(current) === len + i);
}
assert(counter === iter);
}
};
};

@ -0,0 +1,45 @@
import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
let width = 5;
export default () => {
let head = $.state(0);
let current = [];
for (let i = 0; i < width; i++) {
current.push(
$.derived(() => {
return $.get(head) + 1;
})
);
}
let sum = $.derived(() => {
return current.map((x) => $.get(x)).reduce((a, b) => a + b, 0);
});
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(sum);
counter++;
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
assert($.get(sum) === 2 * width);
counter = 0;
for (let i = 0; i < 500; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(sum) === (i + 1) * width);
}
assert(counter === 500);
}
};
};

@ -0,0 +1,38 @@
import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
export default () => {
let heads = new Array(100).fill(null).map((_) => $.state(0));
const mux = $.derived(() => {
return Object.fromEntries(heads.map((h) => $.get(h)).entries());
});
const splited = heads
.map((_, index) => $.derived(() => $.get(mux)[index]))
.map((x) => $.derived(() => $.get(x) + 1));
const destroy = $.effect_root(() => {
splited.forEach((x) => {
$.render_effect(() => {
$.get(x);
});
});
});
return {
destroy,
run() {
for (let i = 0; i < 10; i++) {
$.flush(() => {
$.set(heads[i], i);
});
assert($.get(splited[i]) === i + 1);
}
for (let i = 0; i < 10; i++) {
$.flush(() => {
$.set(heads[i], i * 2);
});
assert($.get(splited[i]) === i * 2 + 1);
}
}
};
};

@ -0,0 +1,42 @@
import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
let size = 30;
export default () => {
let head = $.state(0);
let current = $.derived(() => {
let result = 0;
for (let i = 0; i < size; i++) {
result += $.get(head);
}
return result;
});
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(current);
counter++;
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
assert($.get(current) === size);
counter = 0;
for (let i = 0; i < 100; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(current) === i * size);
}
assert(counter === 100);
}
};
};

@ -0,0 +1,55 @@
import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
let width = 10;
function count(number) {
return new Array(number)
.fill(0)
.map((_, i) => i + 1)
.reduce((x, y) => x + y, 0);
}
export default () => {
let head = $.state(0);
let current = head;
let list = [];
for (let i = 0; i < width; i++) {
let c = current;
list.push(current);
current = $.derived(() => {
return $.get(c) + 1;
});
}
let sum = $.derived(() => {
return list.map((x) => $.get(x)).reduce((a, b) => a + b, 0);
});
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(sum);
counter++;
});
});
return {
destroy,
run() {
const constant = count(width);
$.flush(() => {
$.set(head, 1);
});
assert($.get(sum) === constant);
counter = 0;
for (let i = 0; i < 100; i++) {
$.flush(() => {
$.set(head, i);
});
assert($.get(sum) === constant - width + i * width);
}
assert(counter === 100);
}
};
};

@ -0,0 +1,41 @@
import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client';
export default () => {
let head = $.state(0);
const double = $.derived(() => $.get(head) * 2);
const inverse = $.derived(() => -$.get(head));
let current = $.derived(() => {
let result = 0;
for (let i = 0; i < 20; i++) {
result += $.get(head) % 2 ? $.get(double) : $.get(inverse);
}
return result;
});
let counter = 0;
const destroy = $.effect_root(() => {
$.render_effect(() => {
$.get(current);
counter++;
});
});
return {
destroy,
run() {
$.flush(() => {
$.set(head, 1);
});
assert($.get(current) === 40);
counter = 0;
for (let i = 0; i < 100; i++) {
$.flush(() => {
$.set(head, i);
});
}
assert(counter === 100);
}
};
};

@ -1,4 +1,4 @@
import { assert, fastest_test } from '../../utils.js'; import { assert } from '../../../utils.js';
import * as $ from 'svelte/internal/client'; import * as $ from 'svelte/internal/client';
/** /**
@ -18,7 +18,7 @@ function hard(n) {
const numbers = Array.from({ length: 5 }, (_, i) => i); const numbers = Array.from({ length: 5 }, (_, i) => i);
function setup() { export default () => {
let res = []; let res = [];
const A = $.state(0); const A = $.state(0);
const B = $.state(0); const B = $.state(0);
@ -62,60 +62,4 @@ function setup() {
assert(res[0] === 3198 && res[1] === 1601 && res[2] === 3195 && res[3] === 1598); assert(res[0] === 3198 && res[1] === 1601 && res[2] === 3195 && res[3] === 1598);
} }
}; };
} };
export async function mol_bench_owned() {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run(0);
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1e4; i++) {
run(i);
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: 'mol_bench_owned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
export async function mol_bench_unowned() {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run(0);
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1e4; i++) {
run(i);
}
});
destroy();
return {
benchmark: 'mol_bench_unowned',
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}

@ -0,0 +1,73 @@
import * as $ from 'svelte/internal/client';
import { fastest_test } from '../../utils.js';
export function busy() {
let a = 0;
for (let i = 0; i < 1_00; i++) {
a++;
}
}
/**
*
* @param {string} label
* @param {() => { run: (i?: number) => void, destroy: () => void }} setup
*/
export function create_test(label, setup) {
return {
unowned: async () => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run(0);
destroy();
}
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run(i);
}
});
destroy();
return {
benchmark: `${label}_unowned`,
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
},
owned: async () => {
let run, destroy;
const destroy_owned = $.effect_root(() => {
// Do 10 loops to warm up JIT
for (let i = 0; i < 10; i++) {
const { run, destroy } = setup();
run(0);
destroy();
}
({ run, destroy } = setup());
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 1000; i++) {
run(i);
}
});
// @ts-ignore
destroy();
destroy_owned();
return {
benchmark: `${label}_owned`,
time: timing.time.toFixed(2),
gc_time: timing.gc_time.toFixed(2)
};
}
};
}
Loading…
Cancel
Save