Skip to content

Commit 3c8bf33

Browse files
committed
benchmark for simulating long running queries
1 parent cce7442 commit 3c8bf33

3 files changed

Lines changed: 72 additions & 0 deletions

File tree

tools/benchmark-drivers/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ node index.js --use-local insert small
9898
| `select` | Reading single rows by primary key |
9999
| `select-all` | Reading 100 rows into an array |
100100
| `select-iterate` | Iterating over 100 rows |
101+
| `select-aggregate` | Aggregate functions (COUNT, SUM, AVG, MIN, MAX) with WHERE clause |
101102
| `insert` | Inserting single rows |
102103
| `update` | Updating single rows |
103104
| `transaction` | Inserting 100 rows in a single transaction |
@@ -168,6 +169,27 @@ node:sqlite x 127 ops/sec ±10.75% (event loop: 100%, 7.88ms/op)
168169
169170
For I/O-bound operations, the async driver's overhead becomes negligible compared to disk I/O wait time. The ability to interleave other work becomes an advantage - the event loop can process other tasks while waiting for data.
170171
172+
### Long Query Performance
173+
174+
With such a small amount of data we are currently using, it's not so easy to simulate longer running queries. That's why I tried it here using simple aggregation.
175+
176+
Aggregate functions (COUNT, SUM, AVG, MIN, MAX) with WHERE clauses show even more dramatic async advantages:
177+
178+
```
179+
--- aggregate functions (COUNT, SUM, AVG, MIN, MAX) with WHERE clause ---
180+
better-sqlite3 x 11,246 ops/sec ±0.27% (event loop: 100%, 88.9μs/op)
181+
@homeofthings/sqlite3 x 68,779 ops/sec ±0.60% (event loop: 47%, 6.8μs/op)
182+
node:sqlite x 10,982 ops/sec ±0.40% (event loop: 100%, 91.1μs/op)
183+
```
184+
185+
**Why async wins for aggregation:**
186+
187+
1. **6x higher throughput**: 68,779 vs 11,246 ops/sec
188+
2. **13x less event loop blocking**: 6.8μs/op vs 88.9μs/op
189+
3. **Same pattern as large data**: I/O-bound operations benefit from async
190+
191+
Aggregation queries scan 1000 rows per operation. The async driver's ability to yield during I/O makes it significantly more efficient for these multi-row operations.
192+
171193
## Project Structure
172194
173195
```
@@ -180,6 +202,7 @@ For I/O-bound operations, the async driver's overhead becomes negligible compare
180202
│ ├── insert.js # Insert benchmark
181203
│ ├── select.js # Single row select benchmark
182204
│ ├── select-all.js # Multi-row select benchmark
205+
│ ├── select-aggregate.js # Aggregate functions benchmark
183206
│ ├── select-iterate.js # Iteration benchmark
184207
│ └── transaction.js # Transaction benchmark
185208
└── temp/ # Temporary database files (auto-created)

tools/benchmark-drivers/trials.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ exports.default = [
3030
type: 'update-transaction', table: 'small', columns: ['nul', 'integer', 'real', 'text'],
3131
description: 'updating 100 rows in a single transaction'
3232
},
33+
{
34+
type: 'select-aggregate', table: 'small', columns: ['nul', 'integer', 'real', 'text'],
35+
description: 'aggregate functions (COUNT, SUM, AVG, MIN, MAX) with WHERE clause'
36+
},
3337
];
3438

3539
exports.searchable = [
@@ -82,6 +86,7 @@ exports.searchable = [
8286
{ type: 'update-transaction', table: 'small', columns: ['blob'] },
8387
{ type: 'update-transaction', table: 'large_text', columns: ['text'] },
8488
{ type: 'update-transaction', table: 'large_blob', columns: ['blob'] },
89+
{ type: 'select-aggregate', table: 'small', columns: ['nul', 'integer', 'real', 'text'] },
8590
];
8691

8792
(() => {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
exports.readonly = true; // Aggregate functions (COUNT, SUM, AVG, MIN, MAX) with WHERE clause
3+
4+
exports['better-sqlite3'] = (db, { table, columns }) => {
5+
const stmt = db.prepare(`
6+
SELECT COUNT(*), SUM(integer), AVG(real), MIN(text), MAX(text)
7+
FROM ${table}
8+
WHERE rowid >= ? AND rowid < ?
9+
`);
10+
let start = 0;
11+
return () => {
12+
const result = stmt.get(start, start + 1000);
13+
start = (start + 1) % 9000; // Cycle through 0-8999 to stay within 10000 rows
14+
return result;
15+
};
16+
};
17+
18+
exports['@homeofthings/sqlite3'] = async (db, { table, columns }) => {
19+
const stmt = await db.prepare(`
20+
SELECT COUNT(*), SUM(integer), AVG(real), MIN(text), MAX(text)
21+
FROM ${table}
22+
WHERE rowid >= ? AND rowid < ?
23+
`);
24+
let start = 0;
25+
return async () => {
26+
const result = await stmt.get(start, start + 1000);
27+
start = (start + 1) % 9000; // Cycle through 0-8999 to stay within 10000 rows
28+
return result;
29+
};
30+
};
31+
32+
exports['node:sqlite'] = (db, { table, columns }) => {
33+
const stmt = db.prepare(`
34+
SELECT COUNT(*), SUM(integer), AVG(real), MIN(text), MAX(text)
35+
FROM ${table}
36+
WHERE rowid >= ? AND rowid < ?
37+
`);
38+
let start = 0;
39+
return () => {
40+
const result = stmt.get(start, start + 1000);
41+
start = (start + 1) % 9000; // Cycle through 0-8999 to stay within 10000 rows
42+
return result;
43+
};
44+
};

0 commit comments

Comments
 (0)