Skip to content

Commit abc0b42

Browse files
committed
feat: overwrite, benchmark, setMany, index
1 parent ee75221 commit abc0b42

File tree

14 files changed

+394
-482
lines changed

14 files changed

+394
-482
lines changed

benchmark/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
data
1+
*.sqlite*

benchmark/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# BrineDB Benchmark
2+
3+
To run the benchmark, you need to follow these steps:
4+
5+
1. Run ``docker compose up -d``
6+
2. Run ``cd .. && yarn release-native && cd benchmark``
7+
3. Run ``yarn bench``
8+
9+
The benchmark will run and output the results to the console.
10+
11+
## Benchmark Results
12+
13+
Valid as of 2024-04-07
14+
15+
```bash
16+
😃 Results for: SQLite (Memory)
17+
┌─────────┬───────────┬──────────┬───────────────────┬──────────┬─────────┬───────────────────┐
18+
│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ Average Time (ms) │
19+
├─────────┼───────────┼──────────┼───────────────────┼──────────┼─────────┼───────────────────┤
20+
│ 0 │ 'get''19,244' │ 51962.33089113228 │ '±1.74%' │ 19245 │ '0.052'
21+
│ 1 │ 'set''18,509' │ 54027.41947847479 │ '±2.45%' │ 18523 │ '0.054'
22+
│ 2 │ 'count''4,056' │ 246511.4823761241 │ '±0.25%' │ 4057 │ '0.247'
23+
│ 3 │ 'setMany''1,186' │ 842960.5206401688 │ '±4.24%' │ 1187 │ '0.843'
24+
└─────────┴───────────┴──────────┴───────────────────┴──────────┴─────────┴───────────────────┘
25+
26+
😃 Results for: SQLite (File)
27+
┌─────────┬───────────┬──────────┬────────────────────┬──────────┬─────────┬───────────────────┐
28+
│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ Average Time (ms) │
29+
├─────────┼───────────┼──────────┼────────────────────┼──────────┼─────────┼───────────────────┤
30+
│ 0 │ 'get''17,053' │ 58637.99859266467 │ '±1.57%' │ 17054 │ '0.059'
31+
│ 1 │ 'set''98' │ 10146073.959595695 │ '±6.38%' │ 99 │ '10.146'
32+
│ 2 │ 'count''189' │ 5266332.842932184 │ '±1.59%' │ 191 │ '5.266'
33+
│ 3 │ 'setMany''1,475' │ 677721.3696680956 │ '±4.24%' │ 1477 │ '0.678'
34+
└─────────┴───────────┴──────────┴────────────────────┴──────────┴─────────┴───────────────────┘
35+
36+
😃 Results for: PostgreSQL
37+
┌─────────┬───────────┬─────────┬────────────────────┬──────────┬─────────┬───────────────────┐
38+
│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ Average Time (ms) │
39+
├─────────┼───────────┼─────────┼────────────────────┼──────────┼─────────┼───────────────────┤
40+
│ 0 │ 'get''4,884' │ 204745.00000005757 │ '±1.48%' │ 4885 │ '0.205'
41+
│ 1 │ 'set''1,730' │ 577933.152512942 │ '±1.78%' │ 1731 │ '0.578'
42+
│ 2 │ 'count''12' │ 80366305.00000095 │ '±1.26%' │ 13 │ '80.366'
43+
│ 3 │ 'setMany''445' │ 2243906.7219727584 │ '±2.40%' │ 446 │ '2.244'
44+
└─────────┴───────────┴─────────┴────────────────────┴──────────┴─────────┴───────────────────┘
45+
46+
😃 Results for: MySQL
47+
┌─────────┬───────────┬─────────┬────────────────────┬───────────┬─────────┬───────────────────┐
48+
│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ Average Time (ms) │
49+
├─────────┼───────────┼─────────┼────────────────────┼───────────┼─────────┼───────────────────┤
50+
│ 0 │ 'get''3,631' │ 275340.9485132162 │ '±1.28%' │ 3632 │ '0.275'
51+
│ 1 │ 'set''399' │ 2502239.482499499 │ '±12.75%' │ 400 │ '2.502'
52+
│ 2 │ 'count''11' │ 84151469.41666414 │ '±19.07%' │ 12 │ '84.151'
53+
│ 3 │ 'setMany''747' │ 1337851.9331546512 │ '±2.58%' │ 748 │ '1.338'
54+
└─────────┴───────────┴─────────┴────────────────────┴───────────┴─────────┴───────────────────┘
55+
56+
😃 Results for: MariaDB
57+
┌─────────┬───────────┬─────────┬────────────────────┬──────────┬─────────┬───────────────────┐
58+
│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ Average Time (ms) │
59+
├─────────┼───────────┼─────────┼────────────────────┼──────────┼─────────┼───────────────────┤
60+
│ 0 │ 'get''3,572' │ 279935.25776648754 │ '±2.08%' │ 3573 │ '0.280'
61+
│ 1 │ 'set''1,215' │ 822408.2055921347 │ '±6.10%' │ 1216 │ '0.822'
62+
│ 2 │ 'count''1' │ 977628002.3000028 │ '±2.41%' │ 10 │ '977.628'
63+
│ 3 │ 'setMany''630' │ 1584830.0000005558 │ '±2.99%' │ 631 │ '1.585'
64+
└─────────┴───────────┴─────────┴────────────────────┴──────────┴─────────┴───────────────────┘
65+
```

benchmark/docker-compose.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
services:
2+
mysql:
3+
image: mysql
4+
environment:
5+
MYSQL_ROOT_PASSWORD: root
6+
MYSQL_DATABASE: brinedb
7+
ports:
8+
- "3306:3306"
9+
10+
postgres:
11+
image: postgres
12+
environment:
13+
POSTGRES_USER: root
14+
POSTGRES_PASSWORD: root
15+
POSTGRES_DB: brinedb
16+
ports:
17+
- "5432:5432"
18+
19+
mariadb:
20+
image: mariadb
21+
environment:
22+
MYSQL_ROOT_PASSWORD: root
23+
MYSQL_DATABASE: brinedb
24+
ports:
25+
- "3307:3306"

benchmark/package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
"name": "brine-benchmark",
33
"scripts": {
44
"build": "tsup",
5-
"start": "rm data/*; node dist/index.js"
5+
"start": "node dist/index.js",
6+
"bench": "yarn build && yarn start"
67
},
78
"dependencies": {
89
"@favware/colorette-spinner": "^1.0.1",
9-
"@joshdb/core": "npm:^1.2.7",
10-
"@joshdb/sqlite": "npm:^1.1.9",
11-
"better-sqlite3": "latest"
10+
"tinybench": "^2.6.0"
1211
},
1312
"devDependencies": {
1413
"tsup": "^8.0.2"

benchmark/src/index.ts

Lines changed: 102 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,150 +1,132 @@
1-
import { Brine } from "../../dist";
2-
import Josh from "@joshdb/core";
3-
// @ts-expect-error 7016
4-
import JoshSqlite from "@joshdb/sqlite";
1+
import { Brine } from "../../";
52
import { Spinner } from "@favware/colorette-spinner";
63
import { BrineDatabases } from "../../src";
4+
import { Bench } from "tinybench";
75

8-
const runs = 100;
9-
const databaseSize = 5;
6+
const randomData = (length: number) => {
7+
const chars =
8+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
9+
let result = "";
1010

11-
console.log(
12-
`\nBrine Benchmark commencing, runs set at ${runs} and ${databaseSize} database size.\n`,
13-
);
14-
15-
interface kv {
16-
set: (key: string, value: string) => Promise<void>;
17-
get: (key: string) => Promise<void>;
18-
clear: () => Promise<void>;
19-
}
20-
21-
const test = async (store: kv) => {
22-
const start = performance.now();
11+
for (let i = 0; i < length; i++) {
12+
result += chars.charAt(Math.floor(Math.random() * chars.length));
13+
}
2314

24-
const randomKey = Math.random().toString(36).substring(7);
25-
const randomValue = Math.random().toString(36).substring(7);
15+
return result;
16+
};
2617

27-
await store.set(randomKey, randomValue);
28-
console.log(await store.get(randomKey));
18+
const benchme = async (name: string, db: Brine) => {
19+
const spinner = new Spinner("Initializing database");
20+
const bench = new Bench({ time: 1000, warmupTime: 500 });
2921

30-
const end = performance.now();
22+
spinner.start();
3123

32-
return end - start;
33-
};
24+
await db.init();
3425

35-
const spinner = new Spinner("Benchmark");
36-
const benchmark = async (name: string, runner: kv) => {
37-
spinner.start({
38-
text: `Benchmarking (${name}): 0/${runs}`,
26+
spinner.update({
27+
text: "Setting up database",
3928
});
4029

41-
let total = 0;
42-
let lastUpdate = Date.now();
30+
await db.clear();
4331

44-
await runner.clear();
32+
const setInitialManyData: [string, string][] = [];
33+
34+
for (let i = 0; i < 1_000_000; i++) {
35+
setInitialManyData.push([`key-${i}`, randomData(100)]);
4536

46-
for (let i = 0; i < runs; i++) {
47-
total += await test(runner);
4837
spinner.update({
49-
text: `Benchmarking (${name}): ${i + 1}/${runs}`,
38+
text: `Setting up database (${i + 1}/1000000) (${(
39+
(i / 1_000_000) *
40+
100
41+
).toFixed(2)}%)`,
5042
});
51-
52-
if (lastUpdate + 50 < Date.now()) {
53-
lastUpdate = Date.now();
54-
spinner.spin();
55-
}
5643
}
5744

58-
return {
59-
[name]: {
60-
"Average (ms)": (total / runs).toFixed(3),
61-
"Operations (op/s)": Math.round(runs / (total / 1000)).toLocaleString(),
62-
"Total (s)": (total / 1000).toFixed(2),
63-
},
64-
};
65-
};
66-
67-
(async () => {
68-
const josh = new Josh({
69-
name: "josh",
70-
provider: JoshSqlite,
45+
spinner.update({
46+
text: "Setting up database",
7147
});
7248

73-
const joshResults = await benchmark("Josh (SQLite)", {
74-
set: async (key, value) => {
75-
await josh.set(key, value);
76-
},
77-
get: async (key) => {
78-
await josh.get(key);
79-
},
80-
clear: async () => {
81-
await josh.delete(josh.all);
82-
},
83-
});
49+
await db.setMany(setInitialManyData);
8450

85-
const brine_postgres = new Brine<string>(
86-
BrineDatabases.postgres.build({
87-
host: "localhost",
88-
port: 5432,
89-
user: "root",
90-
password: "root",
91-
database: "brine",
92-
}),
51+
const setManyData: [string, string][] = Array.from(
52+
{ length: 100 },
53+
(_, i) => [`key-many-${i}`, randomData(100)],
9354
);
9455

95-
await brine_postgres.init();
96-
97-
const brineResults = await benchmark("Brine (Postgres)", {
98-
set: async (key, value) => {
99-
await brine_postgres.set(key, value);
100-
},
101-
get: async (key) => {
102-
await brine_postgres.get(key);
103-
},
104-
clear: async () => {
105-
await brine_postgres.clear();
106-
},
56+
bench
57+
.add("get", async () => {
58+
await db.get(`key-${Math.floor(Math.random() * 1000)}`);
59+
})
60+
.add("set", async () => {
61+
await db.set(`key-${Math.floor(Math.random() * 1000)}`, randomData(100));
62+
})
63+
.add("count", async () => {
64+
await db.count();
65+
})
66+
.add("setMany", async () => {
67+
await db.setMany(setManyData);
68+
});
69+
70+
spinner.update({
71+
text: "Running warmup",
10772
});
10873

109-
const brine_sqlite = new Brine<string>(
110-
BrineDatabases.sqlite.file("./data/brine.sqlite"),
111-
);
74+
await bench.warmup();
11275

113-
await brine_sqlite.init();
114-
115-
const brineResultsSqlite = await benchmark("Brine (SQLite)", {
116-
set: async (key, value) => {
117-
await brine_sqlite.set(key, value);
118-
},
119-
get: async (key) => {
120-
await brine_sqlite.get(key);
121-
},
122-
clear: async () => {
123-
await brine_sqlite.clear();
124-
},
76+
spinner.update({
77+
text: "Running benchmarks",
12578
});
12679

127-
const brine_memory = new Brine<string>(BrineDatabases.sqlite.memory);
80+
await bench.run();
81+
await db.close();
12882

129-
await brine_memory.init();
83+
spinner.stop();
13084

131-
const brineResultsMemory = await benchmark("Brine (Memory)", {
132-
set: async (key, value) => {
133-
await brine_memory.set(key, value);
134-
},
135-
get: async (key) => {
136-
await brine_memory.get(key);
137-
},
138-
clear: async () => {
139-
await brine_memory.clear();
140-
},
141-
});
85+
// clear line
86+
process.stdout.moveCursor(0, -1);
87+
process.stdout.clearLine(1);
14288

143-
spinner.success({ text: "Benchmarking complete!" });
144-
console.table({
145-
...joshResults,
146-
...brineResults,
147-
...brineResultsSqlite,
148-
...brineResultsMemory,
149-
});
150-
})();
89+
const table = bench.table();
90+
const finalTable: Record<string, string | number>[] = [];
91+
92+
console.log(`😃 Results for: ${name}\n`); // Add Average Time (ms) column based on "Average Time (ns)" column
93+
94+
for (const row of table) {
95+
if (!row) continue;
96+
if (typeof row["Average Time (ns)"] !== "number") continue;
97+
98+
finalTable.push({
99+
...row,
100+
"Average Time (ms)": (row["Average Time (ns)"] / 1000000).toFixed(3),
101+
});
102+
}
103+
104+
console.table(finalTable);
105+
};
106+
107+
(async () => {
108+
const login = {
109+
user: "root",
110+
password: "root",
111+
database: "brinedb",
112+
};
113+
114+
const sqlite_memory = new Brine(BrineDatabases.sqlite.memory);
115+
const sqlite_file = new Brine(BrineDatabases.sqlite.file("benchmark.sqlite"));
116+
const postgres = new Brine(BrineDatabases.postgres.build(login));
117+
const mysql = new Brine(BrineDatabases.mysql.build(login));
118+
const mariadb = new Brine(
119+
BrineDatabases.mysql.build({
120+
...login,
121+
port: 3307,
122+
}),
123+
);
124+
125+
await benchme("SQLite (Memory)", sqlite_memory);
126+
await benchme("SQLite (File)", sqlite_file);
127+
await benchme("PostgreSQL", postgres);
128+
await benchme("MySQL", mysql);
129+
await benchme("MariaDB", mariadb);
130+
131+
console.log("✅ All benchmarks complete");
132+
})().catch(console.error);

0 commit comments

Comments
 (0)