inula/packages/inula-reactive/bench/index.js

318 lines
8.0 KiB
JavaScript

/*
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
/**
* Extracted from: https://github.com/Riim/cellx#benchmark
*/
import chalk from 'chalk';
import * as solid from './solid-reactive.js';
import * as reactively from '@reactively/core';
import Table from 'cli-table';
import { reactive, computed, RNode } from '../dist/index.js';
const BATCHED = true;
const RUNS_PER_TIER = 150;
const LAYER_TIERS = [10, 100, 500, 1000, 2000];
const med = array => array.sort((a, b) => (a - b < 0 ? 1 : -1))[Math.floor(array.length / 2)] || 0;
const SOLUTIONS = {
10: [2, 4, -2, -3],
100: [-2, -4, 2, 3],
500: [-2, 1, -4, -4],
1000: [-2, -4, 2, 3],
2000: [-2, 1, -4, -4],
// 2500: [-2, -4, 2, 3],
};
/**
* @param {number} layers
* @param {number[]} answer
*/
const isSolution = (layers, answer) => answer.every((_, i) => SOLUTIONS[layers][i] === _);
async function main() {
const report = {};
report.solid = { fn: runSolid, runs: [] };
report.reactively = { fn: runReactively, runs: [], avg: [] };
report.inluaReactive = { fn: runInulaReactive, runs: [], avg: [] };
// report.inluaDeepReactive = { fn: runInulaDeepReactive, runs: [], avg: [] };
for (const lib of Object.keys(report)) {
const current = report[lib];
for (let i = 0; i < LAYER_TIERS.length; i += 1) {
let layers = LAYER_TIERS[i];
const runs = [];
for (let j = 0; j < RUNS_PER_TIER; j += 1) {
runs.push(await start(current.fn, layers));
}
// Give cellx time to release its global pendingCells array
await new Promise(resolve => setTimeout(resolve, 0));
current.runs[i] = med(runs) * 1000;
}
}
const table = new Table({
head: ['', ...LAYER_TIERS.map(n => chalk.bold(chalk.cyan(n)))],
});
for (let i = 0; i < LAYER_TIERS.length; i += 1) {
let min = Infinity,
max = -1,
fastestLib,
slowestLib;
for (const lib of Object.keys(report)) {
const time = report[lib].runs[i];
if (time < min) {
min = time;
fastestLib = lib;
}
if (time > max) {
max = time;
slowestLib = lib;
}
}
report[fastestLib].runs[i] = chalk.green(report[fastestLib].runs[i].toFixed(2));
report[slowestLib].runs[i] = chalk.red(report[slowestLib].runs[i].toFixed(2));
}
for (const lib of Object.keys(report)) {
table.push([chalk.magenta(lib), ...report[lib].runs.map(n => (typeof n === 'number' ? n.toFixed(2) : n))]);
}
console.log(table.toString());
}
async function start(runner, layers) {
return new Promise(done => {
runner(layers, done);
}).catch(e => {
console.error(e.message);
});
}
function runInulaReactive(layers, done) {
const start = {
a: new RNode(1),
b: new RNode(2),
c: new RNode(3),
d: new RNode(4),
};
let layer = start;
for (let i = layers; i--; ) {
layer = (m => {
return {
a: new RNode(() => m.b.get()),
b: new RNode(() => m.a.get() - m.c.get()),
c: new RNode(() => m.b.get() + m.d.get()),
d: new RNode(() => m.c.get()),
};
})(layer);
}
const startTime = performance.now();
start.a.set(4), start.b.set(3), start.c.set(2), start.d.set(1);
const end = layer;
const solution = [end.a.get(), end.b.get(), end.c.get(), end.d.get()];
const endTime = performance.now() - startTime;
done(isSolution(layers, solution) ? endTime : -1);
}
function runInulaDeepReactive(layers, done) {
const start = {
a: new reactive({ v: 1 }),
b: new reactive({ v: 2 }),
c: new reactive({ v: 3 }),
d: new reactive({ v: 4 }),
};
let layer = start;
for (let i = layers; i--; ) {
layer = (l => {
return {
a: new computed(() => ({
v: l.b.v.get(),
})),
b: new computed(() => ({
v: l.a.v.get() - l.c.v.get(),
})),
c: new computed(() => ({
v: l.b.v.get() + l.d.v.get(),
})),
d: new computed(() => ({
v: l.c.v.get(),
})),
};
})(layer);
}
const startTime = performance.now();
start.a.v.set(4), start.b.v.set(3), start.c.v.set(2), start.d.v.set(1);
const end = layer;
const solution = [end.a.v.get(), end.b.v.get(), end.c.v.get(), end.d.v.get()];
const endTime = performance.now() - startTime;
done(isSolution(layers, solution) ? endTime : -1);
}
function runInulaDeepReactive3Layers(layers, done) {
const start = {
a: new reactive({ v: { v: { v: 1 } } }),
b: new reactive({ v: { v: { v: 2 } } }),
c: new reactive({ v: { v: { v: 3 } } }),
d: new reactive({ v: { v: { v: 4 } } }),
};
let layer = start;
for (let i = layers; i--; ) {
layer = (l => {
return {
a: new computed(() => ({
v: {
v: {
v: l.b.v.v.v.get(),
},
},
})),
b: new computed(() => ({
v: {
v: {
v: l.a.v.v.v.get() - l.c.v.v.v.get(),
},
},
})),
c: new computed(() => ({
v: {
v: {
v: l.b.v.v.v.get() + l.d.v.v.v.get(),
},
},
})),
d: new computed(() => ({
v: {
v: {
v: l.c.v.v.v.get(),
},
},
})),
};
})(layer);
}
const startTime = performance.now();
start.a.v.v.v.set(4), start.b.v.v.v.set(3), start.c.v.v.v.set(2), start.d.v.v.v.set(1);
const end = layer;
const solution = [end.a.v.v.v.get(), end.b.v.v.v.get(), end.c.v.v.v.get(), end.d.v.v.v.get()];
const endTime = performance.now() - startTime;
done(isSolution(layers, solution) ? endTime : -1);
}
/**
* @see {@link https://github.com/solidjs/solid}
*/
function runSolid(layers, done) {
solid.createRoot(async dispose => {
const [a, setA] = solid.createSignal(1),
[b, setB] = solid.createSignal(2),
[c, setC] = solid.createSignal(3),
[d, setD] = solid.createSignal(4);
const start = { a, b, c, d };
let layer = start;
for (let i = layers; i--; ) {
layer = (m => {
const props = {
a: solid.createMemo(() => m.b()),
b: solid.createMemo(() => m.a() - m.c()),
c: solid.createMemo(() => m.b() + m.d()),
d: solid.createMemo(() => m.c()),
};
return props;
})(layer);
}
const startTime = performance.now();
const run = BATCHED ? solid.batch : fn => fn();
run(() => {
setA(4), setB(3), setC(2), setD(1);
});
const end = layer;
const solution = [end.a(), end.b(), end.c(), end.d()];
const endTime = performance.now() - startTime;
dispose();
done(isSolution(layers, solution) ? endTime : -1);
});
}
/**
* @see {@link https://github.com/modderme123/reactively}
*/
function runReactively(layers, done) {
const start = {
a: new reactively.Reactive(1),
b: new reactively.Reactive(2),
c: new reactively.Reactive(3),
d: new reactively.Reactive(4),
};
let layer = start;
for (let i = layers; i--; ) {
layer = (m => {
return {
a: new reactively.Reactive(() => m.b.get()),
b: new reactively.Reactive(() => m.a.get() - m.c.get()),
c: new reactively.Reactive(() => m.b.get() + m.d.get()),
d: new reactively.Reactive(() => m.c.get()),
};
})(layer);
}
const startTime = performance.now();
start.a.set(4), start.b.set(3), start.c.set(2), start.d.set(1);
const end = layer;
const solution = [end.a.get(), end.b.get(), end.c.get(), end.d.get()];
const endTime = performance.now() - startTime;
done(isSolution(layers, solution) ? endTime : -1);
}
main();