added benchmarks

This commit is contained in:
Joshua Wise
2016-09-11 14:18:22 -04:00
parent fdca4b818d
commit 8d07a4424b
23 changed files with 384 additions and 138 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ db.on('open', function () {
## Why should I use this instead of [node-sqlite3](https://github.com/mapbox/node-sqlite3)?
- `node-sqlite3` uses asynchronous APIs for tasks that don't touch the hard disk. That's not only bad besign, but it wastes tons of resources.
- `node-sqlite3` forces you to manage the memory of SQLite3 statements yourself. `better-sqlite3` does it the JavaScript way, allowing the garbage collector to worry about memory management.
- `node-sqlite3` exposes low-level (C language) memory management functions. `better-sqlite3` does it the JavaScript way, allowing the garbage collector to worry about memory management.
- This module secretly splits your database connectino into two parts; a read-only connection, and a writable connection, which gives you completely atomic transactions and protects you from reading uncommitted data.
# API
+2
View File
@@ -1,4 +1,6 @@
---------- Version 1 ----------
add filename to database object
write tests and benchmark
compare tests and benchmark to node-sqlite3
+10 -16
View File
@@ -3,31 +3,25 @@ var path = require('path');
var ours = require('../.');
var theirs = require('sqlite3');
module.exports = function (sql, filename, ourTest, theirTest) {
module.exports = function (sql, name, callback) {
var opened = 0;
var created = 0;
ours = new ours(path.join('temp', path.basename(filename) + '.1.db')).on('open', open);
theirs = new theirs.Database(path.join('temp', path.basename(filename) + '.2.db'), open);
var ourDb = new ours(path.join('temp', name + '.ours.db')).on('open', open);
var theirDb = new theirs.Database(path.join('temp', name + '.theirs.db'), open);
function open() {
if (++opened === 2) {
ours.statement(sql).run(ready);
theirs.run(sql, ready);
ourDb.statement(sql).run(ready);
theirDb.run(sql, ready);
}
}
function ready(err) {
if (err) {
console.error(err);
process.exit(1);
}
if (++created === 2) {
var done = function () {
process.exit();
};
var callback = function () {
global.gc();
theirTest(theirs, done);
};
setTimeout(function () {
global.gc();
ourTest(ours, callback);
}, 100);
callback(ourDb, theirDb);
}
}
};
+19
View File
@@ -0,0 +1,19 @@
'use strict';
module.exports = function (db, count, SQL, values, callback) {
var newStatement = db.prepare ? function () {return db.prepare(SQL);}
: function () {return db.statement(SQL);};
var ranCount = 0;
for (var i=0; i<count; ++i) {
newStatement().run(values, ran);
}
function ran(err) {
if (err) {
console.log('Error: Failed to fill table.');
}
if (++ranCount === count) {
callback();
}
}
};
+19
View File
@@ -0,0 +1,19 @@
'use strict';
var path = require('path');
var ours = require('../.');
var theirs = require('sqlite3');
module.exports = function (name, callback) {
var opened = 0;
ours = new ours(path.join('temp', name + '.ours.db')).on('open', open);
theirs = new theirs.Database(path.join('temp', name + '.theirs.db'), open);
function open() {
++opened === 2 && pragma();
}
function pragma() {
ours.pragma('cache_size = -16000;');
theirs.run('PRAGMA cache_size = -16000;', function () {
callback(ours, theirs);
});
}
};
+34 -13
View File
@@ -3,18 +3,44 @@ var path = require('path');
var fs = require('fs-extra');
var clc = require('cli-color');
var spawn = require('child_process').spawn;
process.chdir(path.dirname(__dirname));
fs.removeSync(path.join(__dirname, '..', 'temp'));
fs.ensureDirSync(path.join(__dirname, '..', 'temp'));
fs.removeSync('temp/');
fs.ensureDirSync('temp/');
var trials = fs.readdirSync(path.join(__dirname, 'trials'));
var trials = fs.readdirSync(path.join(__dirname, 'trials')).filter(function (name) {return name[0] !== '.';});
if (!trials.length) {
console.log(clc.yellow('No benchmarks exist!'));
process.exit();
}
console.log(clc.magenta('--- Benchmarks ---'));
;(function next() {
console.log('Generating tables...');
var createdCount = 0;
function created() {
if (++createdCount === 2) {
console.log(clc.magenta('--- Benchmarks ---'));
next();
}
}
require('./create-table')('CREATE TABLE entries (aaaa TEXT, bbbb INTEGER, cccc FLOAT, dddd TEXT, eeee INTEGER, ffff BLOB)', 'select-db', function (ourDb, theirDb) {
var values = [
'John Smith',
524,
0.324176234,
'New York City',
20,
Buffer.alloc(1024 * 1024).fill(0xdd)
];
var filledCount = 0;
function filled() {++filledCount === 2 && created();}
require('./fill-table')(ourDb, 1000, 'INSERT INTO entries VALUES (?, ?, ?, ?, ?, ?)', values, filled);
require('./fill-table')(theirDb, 1000, 'INSERT INTO entries VALUES (?, ?, ?, ?, ?, ?)', values, filled);
});
require('./create-table')('CREATE TABLE entries (name TEXT, number INTEGER, data BLOB)', 'insert-db', created);
function next() {
if (!trials.length) {
fs.removeSync(path.join(__dirname, '..', 'temp'));
console.log(clc.green('All benchmarks complete!'));
@@ -24,11 +50,6 @@ console.log(clc.magenta('--- Benchmarks ---'));
var trialName = trials.shift();
console.log(clc.cyan(trialName));
var child = spawn('node', ['--expose-gc', path.join(__dirname, 'trials', trialName)], {
cwd: path.dirname(__dirname),
stdio: 'inherit'
});
child.on('exit', function () {
setTimeout(next, 100);
});
}());
var child = spawn('node', ['--expose-gc', path.join(__dirname, 'trials', trialName)], {stdio: 'inherit'});
child.on('exit', function () {setTimeout(next, 100);});
}
+54
View File
@@ -0,0 +1,54 @@
'use strict';
module.exports = function (ourDb, theirDb, count, params) {
function callback0() {
global.gc();
ourTest(ourDb, count, params, callback1);
}
function callback1() {
global.gc();
theirTest(theirDb, count, params, callback2);
}
function callback2() {
process.exit();
}
setTimeout(callback0, 100);
};
function ourTest(db, count, params, done) {
var completed = 0;
var failures = 0;
var t0 = process.hrtime();
for (var i=0; i<count; ++i) {
db.statement('INSERT INTO entries VALUES (?, ?, ?)').run(params, callback);
}
function callback(err) {
if (err) {++failures;}
if (++completed === count) {
var td = process.hrtime(t0);
report('better-sqlite3', count - failures, td);
done();
}
}
}
function theirTest(db, count, params, done) {
var completed = 0;
var failures = 0;
var t0 = process.hrtime();
for (var i=0; i<count; ++i) {
db.run('INSERT INTO entries VALUES (?, ?, ?)', params, callback);
}
function callback(err) {
if (err) {++failures;}
if (++completed === count) {
var td = process.hrtime(t0);
report('node-sqlite3', count - failures, td);
done();
}
}
}
function report(name, count, time) {
var ms = time[0] * 1000 + Math.round(time[1] / 1000000);
console.log(name + '\t' + count + ' INSERTs in ' + ms + 'ms');
}
+68
View File
@@ -0,0 +1,68 @@
'use strict';
exports = module.exports = function (ourDb, theirDb, count, small) {
var SQL = small ? 'SELECT aaaa FROM entries WHERE rowid=?'
: 'SELECT * FROM entries WHERE rowid=?';
function callback0() {
global.gc();
ourTest(ourDb, count, SQL, small, callback1);
}
function callback1() {
global.gc();
theirTest(theirDb, count, SQL, small, callback2);
}
function callback2() {
process.exit();
}
setTimeout(callback0, 100);
};
exports.data = undefined;
function ourTest(db, count, SQL, pluck, done) {
var completed = 0;
var failures = 0;
if (pluck) {
var t0 = process.hrtime();
for (var i=0; i<count; ++i) {
db.statement(SQL).pluck().get(i + 1, callback);
}
} else {
var t0 = process.hrtime();
for (var i=0; i<count; ++i) {
db.statement(SQL).get(i + 1, callback);
}
}
function callback(err, data) {
exports.data = data;
if (err) {++failures;}
if (++completed === count) {
var td = process.hrtime(t0);
report('better-sqlite3', count - failures, td);
done();
}
}
}
function theirTest(db, count, SQL, pluck, done) {
var completed = 0;
var failures = 0;
var t0 = process.hrtime();
for (var i=0; i<count; ++i) {
db.get(SQL, i + 1, callback);
}
function callback(err, data) {
exports.data = data;
if (err) {++failures;}
if (++completed === count) {
var td = process.hrtime(t0);
report('node-sqlite3', count - failures, td);
done();
}
}
}
function report(name, count, time) {
var ms = time[0] * 1000 + Math.round(time[1] / 1000000);
console.log(name + '\t' + count + ' SELECTs in ' + ms + 'ms');
}
+49
View File
@@ -0,0 +1,49 @@
'use strict';
module.exports = function (ourDb, theirDb, count, params) {
function callback0() {
global.gc();
ourTest(ourDb, count, params, callback1);
}
function callback1() {
global.gc();
theirTest(theirDb, count, params, callback2);
}
function callback2() {
process.exit();
}
setTimeout(callback0, 100);
};
function ourTest(db, count, params, done) {
params = {a: params[0], b: params[1], c: params[2]};
var statements = new Array(count).fill('INSERT INTO entries VALUES (@a, @b, @c)');
var t0 = process.hrtime();
db.transaction(statements).run(params, callback);
function callback(err) {
var td = process.hrtime(t0);
report('better-sqlite3', err ? 0 : count, td);
done();
}
}
function theirTest(db, count, params, done) {
function run() {
db.run('BEGIN TRANSACTION');
for (var i=0; i<count; ++i) {
db.run('INSERT INTO entries VALUES (?, ?, ?)', params);
}
db.run('COMMIT TRANSACTION', callback);
}
var t0 = process.hrtime();
db.serialize(run);
function callback(err) {
var td = process.hrtime(t0);
report('node-sqlite3', err ? 0 : count, td);
done();
}
}
function report(name, count, time) {
var ms = time[0] * 1000 + Math.round(time[1] / 1000000);
console.log(name + '\t' + count + ' INSERTs (single TRANSACTION) in ' + ms + 'ms');
}
@@ -1,61 +0,0 @@
'use strict';
var createTable = require('../create-table');
var count = 2500;
createTable('CREATE TABLE people (name TEXT, age INTEGER)',
__filename, testOurs, testTheirs);
function testOurs(db, done) {
var len = count;
var completed = 0;
var failures = 0;
var stmts = new Array(len);
for (var i=0; i<len; ++i) {
stmts[i] = db.statement('INSERT INTO people VALUES (?, @age)');
}
var t0 = process.hrtime();
for (var i=0; i<len; ++i) {
stmts[i].run('John Smith', {age: 25}, callback);
}
function callback(err) {
if (err) {++failures;}
if (++completed === len) {
var td = process.hrtime(t0);
report('better-sqlite3', failures, td);
done();
}
}
}
function testTheirs(db, done) {
var len = count;
var completed = 0;
var failures = 0;
var preparedCount = 0;
var stmts = new Array(len);
var t0;
for (var i=0; i<len; ++i) {
stmts[i] = db.prepare('INSERT INTO people VALUES (?, @age)', prepared);
}
function prepared() {
if (++preparedCount === len) {
t0 = process.hrtime();
for (var i=0; i<len; ++i) {
stmts[i].run('John Smith', {age: 25}, callback);
}
}
}
function callback(err) {
if (err) {++failures;}
if (++completed === len) {
var td = process.hrtime(t0);
report('node-sqlite3', failures, td);
done();
}
}
}
function report(name, failures, time) {
var ms = time[0] * 1000 + Math.round(time[1] / 1000000);
console.log(name + '\t' + (count - failures) + ' INSERTs in ' + ms + 'ms');
}
@@ -1,47 +0,0 @@
'use strict';
var createTable = require('../create-table');
var count = 2500;
createTable('CREATE TABLE people (name TEXT, age INTEGER)',
__filename, testOurs, testTheirs);
function testOurs(db, done) {
var len = count;
var completed = 0;
var failures = 0;
var t0 = process.hrtime();
for (var i=0; i<len; ++i) {
db.statement('INSERT INTO people VALUES (?, @age)').run('John Smith', {age: 25}, callback);
}
function callback(err) {
if (err) {++failures;}
if (++completed === len) {
var td = process.hrtime(t0);
report('better-sqlite3', failures, td);
done();
}
}
}
function testTheirs(db, done) {
var len = count;
var completed = 0;
var failures = 0;
var t0 = process.hrtime();
for (var i=0; i<len; ++i) {
db.run('INSERT INTO people VALUES (?, @age)', 'John Smith', {age: 25}, callback);
}
function callback(err) {
if (err) {++failures;}
if (++completed === len) {
var td = process.hrtime(t0);
report('node-sqlite3', failures, td);
done();
}
}
}
function report(name, failures, time) {
var ms = time[0] * 1000 + Math.round(time[1] / 1000000);
console.log(name + '\t' + (count - failures) + ' INSERTs in ' + ms + 'ms');
}
+12
View File
@@ -0,0 +1,12 @@
'use strict';
require('../get-db')('insert-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = DELETE');
ourDb.pragma('synchronous = 2');
theirDb.exec('PRAGMA journal_mode = DELETE; PRAGMA synchronous = 2;', function () {
require('../insert-trial')(ourDb, theirDb, 250, [
'John Smith',
123456,
Buffer.alloc(1024*1024).fill(0xdd)
]);
});
});
+12
View File
@@ -0,0 +1,12 @@
'use strict';
require('../get-db')('insert-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = DELETE');
ourDb.pragma('synchronous = 2');
theirDb.exec('PRAGMA journal_mode = DELETE; PRAGMA synchronous = 2;', function () {
require('../insert-trial')(ourDb, theirDb, 500, [
'John Smith',
123456,
null
]);
});
});
+12
View File
@@ -0,0 +1,12 @@
'use strict';
require('../get-db')('insert-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = WAL');
ourDb.pragma('synchronous = 1');
theirDb.exec('PRAGMA journal_mode = WAL; PRAGMA synchronous = 1;', function () {
require('../insert-trial')(ourDb, theirDb, 250, [
'John Smith',
123456,
Buffer.alloc(1024*1024).fill(0xdd)
]);
});
});
+12
View File
@@ -0,0 +1,12 @@
'use strict';
require('../get-db')('insert-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = WAL');
ourDb.pragma('synchronous = 1');
theirDb.exec('PRAGMA journal_mode = WAL; PRAGMA synchronous = 1;', function () {
require('../insert-trial')(ourDb, theirDb, 500, [
'John Smith',
123456,
null
]);
});
});
+8
View File
@@ -0,0 +1,8 @@
'use strict';
require('../get-db')('select-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = DELETE');
ourDb.pragma('synchronous = 2');
theirDb.exec('PRAGMA journal_mode = DELETE; PRAGMA synchronous = 2;', function () {
require('../select-trial')(ourDb, theirDb, 1000, false);
});
});
+8
View File
@@ -0,0 +1,8 @@
'use strict';
require('../get-db')('select-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = DELETE');
ourDb.pragma('synchronous = 2');
theirDb.exec('PRAGMA journal_mode = DELETE; PRAGMA synchronous = 2;', function () {
require('../select-trial')(ourDb, theirDb, 10000, true);
});
});
+8
View File
@@ -0,0 +1,8 @@
'use strict';
require('../get-db')('select-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = WAL');
ourDb.pragma('synchronous = 1');
theirDb.exec('PRAGMA journal_mode = WAL; PRAGMA synchronous = 1;', function () {
require('../select-trial')(ourDb, theirDb, 1000, false);
});
});
+8
View File
@@ -0,0 +1,8 @@
'use strict';
require('../get-db')('select-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = WAL');
ourDb.pragma('synchronous = 1');
theirDb.exec('PRAGMA journal_mode = WAL; PRAGMA synchronous = 1;', function () {
require('../select-trial')(ourDb, theirDb, 10000, true);
});
});
@@ -0,0 +1,12 @@
'use strict';
require('../get-db')('insert-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = DELETE');
ourDb.pragma('synchronous = 2');
theirDb.exec('PRAGMA journal_mode = DELETE; PRAGMA synchronous = 2;', function () {
require('../transaction-trial')(ourDb, theirDb, 250, [
'John Smith',
123456,
Buffer.alloc(1024*1024).fill(0xdd)
]);
});
});
@@ -0,0 +1,12 @@
'use strict';
require('../get-db')('insert-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = DELETE');
ourDb.pragma('synchronous = 2');
theirDb.exec('PRAGMA journal_mode = DELETE; PRAGMA synchronous = 2;', function () {
require('../transaction-trial')(ourDb, theirDb, 500, [
'John Smith',
123456,
null
]);
});
});
+12
View File
@@ -0,0 +1,12 @@
'use strict';
require('../get-db')('insert-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = WAL');
ourDb.pragma('synchronous = 1');
theirDb.exec('PRAGMA journal_mode = WAL; PRAGMA synchronous = 1;', function () {
require('../transaction-trial')(ourDb, theirDb, 250, [
'John Smith',
123456,
Buffer.alloc(1024*1024).fill(0xdd)
]);
});
});
+12
View File
@@ -0,0 +1,12 @@
'use strict';
require('../get-db')('insert-db', function (ourDb, theirDb) {
ourDb.pragma('journal_mode = WAL');
ourDb.pragma('synchronous = 1');
theirDb.exec('PRAGMA journal_mode = WAL; PRAGMA synchronous = 1;', function () {
require('../transaction-trial')(ourDb, theirDb, 500, [
'John Smith',
123456,
null
]);
});
});