diff --git a/README.md b/README.md index 7614ce8..39c4266 100644 --- a/README.md +++ b/README.md @@ -10,27 +10,27 @@ JsMap: ┌─────────┬─────────────────┬────────────────────┬───────────────────┐ │ (index) │ totalLookupTime │ totalInsertionTime │ totalDeletionTime │ ├─────────┼─────────────────┼────────────────────┼───────────────────┤ -│ 0 │ '1112.18 ms' │ '1258.46 ms' │ '1272.44 ms' │ -│ 1 │ '1103.26 ms' │ '1199.46 ms' │ '1281.72 ms' │ -│ average │ '1107.72 ms' │ '1228.96 ms' │ '1277.08 ms' │ +│ 0 │ '134.71 ms' │ '362.9 ms' │ '212.05 ms' │ +│ 1 │ '140.82 ms' │ '149.01 ms' │ '217.53 ms' │ +│ average │ '137.77 ms' │ '255.95 ms' │ '214.79 ms' │ └─────────┴─────────────────┴────────────────────┴───────────────────┘ Kivi: ┌─────────┬─────────────────┬────────────────────┬───────────────────┐ │ (index) │ totalLookupTime │ totalInsertionTime │ totalDeletionTime │ ├─────────┼─────────────────┼────────────────────┼───────────────────┤ -│ 0 │ '1350.07 ms' │ '559.04 ms' │ '1355.66 ms' │ -│ 1 │ '1349.39 ms' │ '557.93 ms' │ '1346.2 ms' │ -│ average │ '1349.73 ms' │ '558.49 ms' │ '1350.93 ms' │ +│ 0 │ '916.68 ms' │ '225.9 ms' │ '976.8 ms' │ +│ 1 │ '907.01 ms' │ '217.65 ms' │ '1017.35 ms' │ +│ average │ '911.84 ms' │ '221.78 ms' │ '997.08 ms' │ └─────────┴─────────────────┴────────────────────┴───────────────────┘ This table shows how much JsMap is faster than Kivi: ┌───────────┬─────────┐ │ (index) │ Values │ ├───────────┼─────────┤ -│ lookup │ '1.22x' │ -│ insertion │ '0.45x' │ -│ deletion │ '1.06x' │ +│ lookup │ '6.62x' │ +│ insertion │ '0.85x' │ +│ deletion │ '4.64x' │ └───────────┴─────────┘ ``` diff --git a/bench/bench-with-builtin.js b/bench/bench-with-builtin.js index 1d68872..f4a057d 100644 --- a/bench/bench-with-builtin.js +++ b/bench/bench-with-builtin.js @@ -1,62 +1,36 @@ -import fs from "node:fs"; -import path from "node:path"; -import json from "big-json"; -import { fileURLToPath } from "node:url"; import { Kivi } from "../src/drivers/js/index.js"; -import { isBun } from "../src/drivers/js/runtime.js"; import { generateFakeData } from "./faker/generate.js"; const repeatBenchmark = 2; -const fakeDataJsonFile = "faker/data/data.json"; -const dataJsonPath = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - fakeDataJsonFile -); +const data = generateFakeData(); const assert = (name, left, right) => { - if (left !== right) { + if (!left.equals(right)) { throw new Error( - `Assertion '${name}' failed! Left was '${left}' and right was '${right}'.` + `Assertion '${name}' failed! Left was '${left.toString()}' and right was '${right.toString()}'.` ); } }; -const resolveOnEmit = (event) => { - return new Promise(function (resolve, reject) { - try { - event.on("data", (data) => resolve(data)); - } catch (e) { - reject(e); - } - }); -}; const roundToTwoDecimal = (num) => +(Math.round(num + "e+2") + "e-2"); -const benchmarkDeletion = (data, o) => { +const benchmarkDeletion = (data, o, keyidx) => { const startingTime = performance.now(); for (const item of data) { - assert( - `${o.name} deletion`, - o.del(Buffer.from(item.key, "utf8")).toString(), - Buffer.from(item.value, "utf8").toString() - ); + assert(`${o.name} deletion`, o.del(item[keyidx]), item.value); } return performance.now() - startingTime; }; -const benchmarkLookup = (data, o) => { +const benchmarkLookup = (data, o, keyidx) => { const startingTime = performance.now(); for (const item of data) { - assert( - `${o.name} lookup`, - o.get(Buffer.from(item.key, "utf8")).toString(), - Buffer.from(item.value, "utf8").toString() - ); + assert(`${o.name} deletion`, o.get(item[keyidx]), item.value); } return performance.now() - startingTime; }; -const benchmarkInsertion = (data, o) => { +const benchmarkInsertion = (data, o, keyidx) => { const startingTime = performance.now(); for (const item of data) { - o.set(Buffer.from(item.key, "utf8"), Buffer.from(item.value, "utf8")); + o.set(item[keyidx], item.value); } return performance.now() - startingTime; }; @@ -120,23 +94,6 @@ const logRatio = () => { }); }; -let data; -if (!fs.existsSync(dataJsonPath)) { - await generateFakeData(); -} -console.log("Loading the data. Please be patient..."); -if (!isBun()) { - const readStream = fs.createReadStream(dataJsonPath); - const parseStream = json.createParseStream(); - readStream.pipe(parseStream); - - data = await resolveOnEmit(parseStream); -} else { - const file = Bun.file(dataJsonPath); - data = await file.json(); -} -let dataKeys = data.map((el) => el.key); - const builtinMapBenchmark = () => { const durationArr = []; let average = { @@ -149,23 +106,23 @@ const builtinMapBenchmark = () => { name: "JsMap", map: new Map(), get: function (k) { - return this.map.get(k.toString()); + return this.map.get(k); }, set: function (k, v) { - return this.map.set(k.toString(), v); + return this.map.set(k, v); }, del: function (k) { - const v = this.map.get(k.toString()); - this.map.delete(k.toString()); + const v = this.map.get(k); + this.map.delete(k); return v; }, destroy: function () { return this.map.clear(); }, }; - const insertionDuration = benchmarkInsertion(data, o); - const lookupDuration = benchmarkLookup(data, o); - const deletionDuration = benchmarkDeletion(data, o); + const insertionDuration = benchmarkInsertion(data, o, "key"); + const lookupDuration = benchmarkLookup(data, o, "key"); + const deletionDuration = benchmarkDeletion(data, o, "key"); o.destroy(); durationArr.push({ iteration: i, @@ -213,9 +170,9 @@ const kiviBenchmark = () => { return this.map.destroy(); }, }; - const insertionDuration = benchmarkInsertion(data, o); - const lookupDuration = benchmarkLookup(data, o); - const deletionDuration = benchmarkDeletion(data, o); + const insertionDuration = benchmarkInsertion(data, o, "kyb"); + const lookupDuration = benchmarkLookup(data, o, "kyb"); + const deletionDuration = benchmarkDeletion(data, o, "kyb"); o.destroy(); durationArr.push({ iteration: i, diff --git a/bench/faker/generate.js b/bench/faker/generate.js index 6098fb4..83cd8f0 100644 --- a/bench/faker/generate.js +++ b/bench/faker/generate.js @@ -1,42 +1,58 @@ -import fs from "node:fs"; -import path from "path"; -import json from "big-json"; -import { fileURLToPath } from "url"; -import { faker } from "@faker-js/faker"; - const count = 1_000_000; -export const generateFakeData = () => - new Promise((resolve) => { - console.log("Generating fake data for the benchmark..."); - - const arr = []; - for (let i = 0; i < count; i++) { - arr.push({ - key: faker.string.uuid() + "_" + faker.person.fullName(), - value: JSON.stringify({ - bio: faker.person.bio(), - gender: faker.person.gender(), - jobArea: faker.person.jobArea(), - jobTitle: faker.person.jobTitle(), - jobType: faker.person.jobType(), - name: faker.person.fullName(), - }), - }); - if (i % (count / 10) == 0 && i != 0) { - console.log((i / count) * 100 + "%"); - } +const genRandomNum = (min, max) => { + return Math.random() * (max - min) + min; +}; +const genRandomStr = function (length) { + let result = ""; + const characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const charactersLength = characters.length; + let counter = 0; + while (counter < length) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + counter += 1; + } + return result; +}; +function genUUID() { + // Public Domain/MIT + var d = new Date().getTime(); //Timestamp + var d2 = + (typeof performance !== "undefined" && + performance.now && + performance.now() * 1000) || + 0; //Time in microseconds since page-load or 0 if unsupported + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { + var r = Math.random() * 16; //random number between 0 and 16 + if (d > 0) { + //Use timestamp until depleted + r = (d + r) % 16 | 0; + d = Math.floor(d / 16); + } else { + //Use microseconds since page-load if supported + r = (d2 + r) % 16 | 0; + d2 = Math.floor(d2 / 16); } + return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); + }); +} + +export const generateFakeData = () => { + console.log("Generating fake data for the benchmark..."); - const dataJsonPath = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - "data/data.json" - ); - console.log("Writing the data. Please be patient."); - const writeStream = fs.createWriteStream(dataJsonPath); - const stringifyStream = json.createStringifyStream({ - body: arr, + const data = []; + for (let i = 0; i <= count; i++) { + const key = genRandomStr(genRandomNum(2, 150)) + genUUID(); + data.push({ + key, + kyb: Buffer.from(key, "utf8"), + value: Buffer.from(genRandomStr(genRandomNum(2, 500)), "utf8"), }); - const pipe = stringifyStream.pipe(writeStream); - pipe.on("finish", () => resolve()); - }); + if (i % (count / 10) == 0 && i != 0) { + console.log((i / count) * 100 + "%"); + } + } + + return data; +}; diff --git a/bench/package.json b/bench/package.json index 0b212d3..692bfbe 100644 --- a/bench/package.json +++ b/bench/package.json @@ -11,8 +11,5 @@ }, "author": "", "license": "ISC", - "dependencies": { - "big-json": "^3.2.0", - "@faker-js/faker": "^8.0.2" - } + "dependencies": {} } diff --git a/build.zig b/build.zig index 65c8a52..6633cda 100644 --- a/build.zig +++ b/build.zig @@ -134,24 +134,6 @@ inline fn run_npm_command( } } -inline fn run_npm_install( - b: *std.Build, - comptime dir: anytype, - dependency_step: ?*std.Build.Step, - runner_step: ?*std.Build.Step, -) std.Build.Step { - const syscommand = b.addSystemCommand(&[2][]const u8{ "pnpm", "install" }); - syscommand.cwd = get_lazypath(dir); - if (dependency_step) |step| { - syscommand.step.dependOn(step); - } - if (runner_step != null) { - runner_step.?.dependOn(&syscommand.step); - } - - return syscommand.step; -} - pub fn build(b: *std.Build) !void { optimize = b.standardOptimizeOption(.{}); resolved_target = b.standardTargetOptions(.{}); @@ -250,10 +232,6 @@ pub fn build(b: *std.Build) !void { test_step, ); - // Installs benchmark dependencies - const bench_dep_install_step = b.step("bench-install", "Installs benchmark dependencies"); - _ = run_npm_install(b, "bench", null, bench_dep_install_step); - // Benchmarks Kivi const bench_step = b.step("bench", "Benchmarks kivi"); _ = run_npm_command( @@ -264,7 +242,7 @@ pub fn build(b: *std.Build) !void { // "deno-bench", // "bun-bench", }, - .{ bench_dep_install_step, drivers_build_step }, + .{drivers_build_step}, bench_step, ); }