Merge pull request #30 from danleh/sqlite-scoring
Move sqlite Wasm benchmark to first, worst, average scoring
diff --git a/JetStreamDriver.js b/JetStreamDriver.js
index e6abc30..35bb018 100644
--- a/JetStreamDriver.js
+++ b/JetStreamDriver.js
@@ -2001,17 +2001,17 @@
worstCaseCount: 2,
testGroup: WasmGroup
}),
- new WasmLegacyBenchmark({
+ new WasmEMCCBenchmark({
name: "sqlite3-wasm",
files: [
- "./sqlite3/polyfills.js",
- "./sqlite3/build/jswasm/speedtest1.js",
"./sqlite3/benchmark.js",
+ "./sqlite3/build/jswasm/speedtest1.js",
],
preload: {
wasmBinary: "./sqlite3/build/jswasm/speedtest1.wasm"
},
- benchmarkClass: WasmLegacyBenchmark,
+ iterations: 15,
+ worstCaseCount: 2,
testGroup: WasmGroup
}),
new WasmLegacyBenchmark({
diff --git a/sqlite3/benchmark.js b/sqlite3/benchmark.js
index 1ac0a45..9de15dd 100644
--- a/sqlite3/benchmark.js
+++ b/sqlite3/benchmark.js
@@ -2,18 +2,45 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-const inJetStreamRunner = typeof globalThis.benchmarkTime !== "undefined";
-if (!inJetStreamRunner) {
- load("polyfills.js");
+// First, some polyfills for missing browser APIs in JavaScript shells.
+// Since the generated JavaScript code of SQLite immediately uses some of them,
+// we need to load and run this code before the generated `speedtest1.js` in the
+// JetStream driver.
- // Exports `sqlite3InitModule()` and contains the main code.
- load("build/jswasm/speedtest1.js");
+// Empty `URLSearchParams` has just the same interface as a `Map`.
+globalThis.URLSearchParams = Map;
- // Load Wasm binary from disk.
- globalThis.Module = {
- wasmBinary: read("build/jswasm/speedtest1.wasm", "binary"),
- };
+// `TextEncoder` and `TextDecoder`. These are called only a few times with short
+// ASCII strings, so this is sufficient and not performance-critical.
+class TextEncoder {
+ encode(string) {
+ return Uint8Array.from(string, (char) => {
+ let byte = char.codePointAt(0);
+ if (byte > 0x7f)
+ throw new Error("TextEncoder polyfill only supports ASCII");
+ return byte;
+ });
+ }
}
+class TextDecoder {
+ decode(array) {
+ for (let byte of array) {
+ if (byte > 0x7f)
+ throw new Error("TextDecoder polyfill only supports ASCII");
+ }
+ return String.fromCharCode.apply(null, array);
+ }
+}
+
+// Now, some configuration options for when we initialize SQLite.
+
+// Use JetStream functions instead of `console.log` and friends.
+globalThis.sqlite3ApiConfig = {
+ log: print,
+ debug: print,
+ warn: print,
+ error: print,
+};
// Make sure we never initialize OPFS by removing one of it's APIs (see
// `installOpfsVfs` in the generated JavaScript code of sqlite).
@@ -21,53 +48,51 @@
// waste cycles on the browser runner to initialize it.
delete globalThis.FileSystemHandle;
-// Simplified from inline JavaScript in `speedtest1.html`.
-function runTests(sqlite3Module) {
- // Configure the VFS to use.
- // Don't use OPFS, WASMFS (which is on top of OPFS), or kvvfs, since they all
- // use persistent browser storage (localStorage or OPFS), which is not
- // available in JavaScript shells.
- // Also don't use memfs, since that crashes with a NULL function pointer.
- // Instead, make the default VFS explicit.
- const capi = sqlite3Module.capi
- console.log("Available SQLite VFS:", capi.sqlite3_js_vfs_list());
- const vfs = "unix";
- console.log("Using VFS:", vfs);
- const pVfs = capi.sqlite3_vfs_find(vfs);
- if (!pVfs) {
- console.error("Error: Unknown VFS:", vfs);
- return;
+class Benchmark {
+ sqlite3Module;
+
+ async runIteration() {
+ if (!this.sqlite3Module) {
+ // Defined in the generated SQLite JavaScript code.
+ // Different in details but seemingly related/inspired by Emscripten code.
+ this.sqlite3Module = await sqlite3InitModule(Module);
+ }
+
+ // The following is simplified from inline JavaScript in `speedtest1.html`.
+
+ // Configure the VFS to use.
+ // Don't use OPFS, WASMFS (which is on top of OPFS), or kvvfs, since they
+ // all use persistent browser storage (localStorage or OPFS), which is not
+ // available in JavaScript shells.
+ // Also don't use memfs, since that crashes with a NULL function pointer.
+ // Instead, make the default VFS explicit.
+ const capi = this.sqlite3Module.capi
+ print("Available SQLite VFS:", capi.sqlite3_js_vfs_list());
+ const vfs = "unix";
+ print("Using VFS:", vfs);
+ const pVfs = capi.sqlite3_vfs_find(vfs);
+ if (!pVfs) {
+ throw new Error("Unknown VFS:", vfs);
+ }
+
+ // These arguments should match the upstream browser runner in
+ // `speedtest1.html`, except for the --size parameter.
+ let argv = [
+ "speedtest1",
+ "--singlethread",
+ //"--nomutex",
+ //"--nosync",
+ //"--memdb", // note that memdb trumps the filename arg
+ "--nomemstat",
+ "--big-transactions" /*important for tests 410 and 510!*/,
+ "--size", "10", // To speedup, default is 100 (and takes about 4s).
+ "--vfs", vfs, // See VFS comment above.
+ ];
+
+ print("Calling main with argv:", argv);
+ const wasm = this.sqlite3Module.wasm;
+ wasm.scopedAllocPush(); // Required for `scopedAllocMainArgv()`.
+ wasm.xCall("wasm_main", argv.length, wasm.scopedAllocMainArgv(argv));
+ wasm.scopedAllocPop();
}
-
- // These arguments should match the upstream browser runner `speedtest1.html`.
- let argv = [
- "speedtest1",
- "--singlethread",
- //"--nomutex",
- //"--nosync",
- //"--memdb", // note that memdb trumps the filename arg
- "--nomemstat",
- "--big-transactions" /*important for tests 410 and 510!*/,
- "--size", "20", // To speedup, default is 100 (and takes about 4s).
- "--vfs", vfs, // See VFS comment above.
- ];
-
- console.log("Calling main with argv:", argv);
- const wasm = sqlite3Module.wasm;
- wasm.scopedAllocPush(); // Required for `scopedAllocMainArgv()`.
- wasm.xCall("wasm_main", argv.length, wasm.scopedAllocMainArgv(argv));
- wasm.scopedAllocPop();
-}
-
-async function doRun() {
- let start = benchmarkTime();
- const sqliteModule = await sqlite3InitModule(Module);
- reportCompileTime(benchmarkTime() - start);
-
- start = benchmarkTime();
- runTests(sqliteModule);
- reportRunTime(benchmarkTime() - start);
-}
-if (!inJetStreamRunner) {
- sqlite3InitModule(Module).then(runTests);
}
diff --git a/sqlite3/build.log b/sqlite3/build.log
index 8c57c22..c32b220 100644
--- a/sqlite3/build.log
+++ b/sqlite3/build.log
@@ -1,10 +1,10 @@
-Built on 2024-12-05 14:24:19+01:00
+Built on 2025-01-21T15:10:24Z
Toolchain versions
-emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.71 (4171ae200b77a6c266b0e1ebb507d61d1ade3501)
+emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.73 (ac676d5e437525d15df5fd46bc2c208ec6d376a3)
wasm-strip 1.0.34
-Getting sources from https://sqlite.org/2024/sqlite-src-3470100.zip
+Getting sources from https://sqlite.org/2025/sqlite-src-3480000.zip
Building...
Copying files from sqlite-src-*/ext/wasm/ into build/
diff --git a/sqlite3/build.sh b/sqlite3/build.sh
index ff7b067..27c6142 100755
--- a/sqlite3/build.sh
+++ b/sqlite3/build.sh
@@ -1,10 +1,9 @@
#!/bin/bash
-set -e
-set -o pipefail
+set -eo pipefail
# Cleanup old files.
-rm sqlite-src-*.zip
+rm -f sqlite-src-*.zip
rm -rf sqlite-src-*/
rm -rf build/
@@ -17,7 +16,7 @@
echo -e "wasm-strip $(wasm-strip --version)\n" | tee -a "$BUILD_LOG"
# Check https://sqlite.org/download.html and update the source link, if needed.
-SQLITE_SRC_URL="https://sqlite.org/2024/sqlite-src-3470100.zip"
+SQLITE_SRC_URL="https://sqlite.org/2025/sqlite-src-3480000.zip"
echo -e "Getting sources from $SQLITE_SRC_URL\n" | tee -a "$BUILD_LOG"
SQLITE_SRC_FILE="$(basename $SQLITE_SRC_URL)"
curl -o "$SQLITE_SRC_FILE" $SQLITE_SRC_URL
diff --git a/sqlite3/build/jswasm/speedtest1.js b/sqlite3/build/jswasm/speedtest1.js
index ef21275..3d592ba 100644
--- a/sqlite3/build/jswasm/speedtest1.js
+++ b/sqlite3/build/jswasm/speedtest1.js
@@ -26,11 +26,11 @@
/*
** This code was built from sqlite3 version...
**
-** SQLITE_VERSION "3.47.1"
-** SQLITE_VERSION_NUMBER 3047001
-** SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e"
+** SQLITE_VERSION "3.48.0"
+** SQLITE_VERSION_NUMBER 3048000
+** SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010"
**
-** Using the Emscripten SDK version 3.1.71.
+** Using the Emscripten SDK version 3.1.73.
*/
var sqlite3InitModule = (() => {
@@ -73,7 +73,7 @@
// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
-// include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3470100/ext/wasm/bld/pre-js.speedtest1-vanilla.js
+// include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3480000/ext/wasm/bld/pre-js.speedtest1-vanilla.js
/**
BEGIN FILE: api/pre-js.js
@@ -132,7 +132,7 @@
/* END FILE: api/pre-js.js, noting that the build process may add a
line after this one to change the above .uri to a build-specific
one. */
-// end include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3470100/ext/wasm/bld/pre-js.speedtest1-vanilla.js
+// end include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3480000/ext/wasm/bld/pre-js.speedtest1-vanilla.js
// Sometimes an existing Module object exists with properties
@@ -558,7 +558,6 @@
// Create the wasm instance.
// Receives the wasm imports, returns the exports.
function createWasm() {
- var info = getWasmImports();
// Load the wasm module and create an instance of using native support in the JS engine.
// handle a generated wasm instance, receiving its exports and
// performing other necessary setup
@@ -589,6 +588,8 @@
receiveInstance(result['instance']);
}
+ var info = getWasmImports();
+
// User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback
// to manually instantiate the Wasm module themselves. This allows pages to
// run the instantiation parallel to any other async startup actions they are
@@ -751,9 +752,8 @@
var UTF8ToString = (ptr, maxBytesToRead) => {
return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '';
};
- var ___assert_fail = (condition, filename, line, func) => {
+ var ___assert_fail = (condition, filename, line, func) =>
abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']);
- };
var PATH = {
isAbs:(path) => path.charAt(0) === '/',
@@ -1149,7 +1149,7 @@
var MEMFS = {
ops_table:null,
mount(mount) {
- return MEMFS.createNode(null, '/', 16384 | 511 /* 0777 */, 0);
+ return MEMFS.createNode(null, '/', 16895, 0);
},
createNode(parent, name, mode, dev) {
if (FS.isBlkdev(mode) || FS.isFIFO(mode)) {
@@ -1304,7 +1304,7 @@
}
},
lookup(parent, name) {
- throw FS.genericErrors[44];
+ throw MEMFS.doesNotExistError;
},
mknod(parent, name, mode, dev) {
return MEMFS.createNode(parent, name, mode, dev);
@@ -1350,7 +1350,7 @@
return entries;
},
symlink(parent, newname, oldpath) {
- var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | 40960, 0);
+ var node = MEMFS.createNode(parent, newname, 0o777 | 40960, 0);
node.link = oldpath;
return node;
},
@@ -1590,8 +1590,6 @@
this.errno = errno;
}
},
- genericErrors:{
- },
filesystems:null,
syncFSRequests:0,
readFiles:{
@@ -2098,14 +2096,35 @@
}
return parent.node_ops.mknod(parent, name, mode, dev);
},
- create(path, mode) {
- mode = mode !== undefined ? mode : 438 /* 0666 */;
+ statfs(path) {
+
+ // NOTE: None of the defaults here are true. We're just returning safe and
+ // sane values.
+ var rtn = {
+ bsize: 4096,
+ frsize: 4096,
+ blocks: 1e6,
+ bfree: 5e5,
+ bavail: 5e5,
+ files: FS.nextInode,
+ ffree: FS.nextInode - 1,
+ fsid: 42,
+ flags: 2,
+ namelen: 255,
+ };
+
+ var parent = FS.lookupPath(path, {follow: true}).node;
+ if (parent?.node_ops.statfs) {
+ Object.assign(rtn, parent.node_ops.statfs(parent.mount.opts.root));
+ }
+ return rtn;
+ },
+ create(path, mode = 0o666) {
mode &= 4095;
mode |= 32768;
return FS.mknod(path, mode, 0);
},
- mkdir(path, mode) {
- mode = mode !== undefined ? mode : 511 /* 0777 */;
+ mkdir(path, mode = 0o777) {
mode &= 511 | 512;
mode |= 16384;
return FS.mknod(path, mode, 0);
@@ -2126,7 +2145,7 @@
mkdev(path, mode, dev) {
if (typeof dev == 'undefined') {
dev = mode;
- mode = 438 /* 0666 */;
+ mode = 0o666;
}
mode |= 8192;
return FS.mknod(path, mode, dev);
@@ -2224,7 +2243,7 @@
// do the underlying fs rename
try {
old_dir.node_ops.rename(old_node, new_dir, new_name);
- // update old node (we do this here to avoid each backend
+ // update old node (we do this here to avoid each backend
// needing to)
old_node.parent = new_dir;
} catch (e) {
@@ -2294,7 +2313,7 @@
if (!link.node_ops.readlink) {
throw new FS.ErrnoError(28);
}
- return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link));
+ return link.node_ops.readlink(link);
},
stat(path, dontFollow) {
var lookup = FS.lookupPath(path, { follow: !dontFollow });
@@ -2399,13 +2418,12 @@
timestamp: Math.max(atime, mtime)
});
},
- open(path, flags, mode) {
+ open(path, flags, mode = 0o666) {
if (path === "") {
throw new FS.ErrnoError(44);
}
flags = typeof flags == 'string' ? FS_modeStringToFlags(flags) : flags;
if ((flags & 64)) {
- mode = typeof mode == 'undefined' ? 438 /* 0666 */ : mode;
mode = (mode & 4095) | 32768;
} else {
mode = 0;
@@ -2691,6 +2709,7 @@
FS.registerDevice(FS.makedev(1, 3), {
read: () => 0,
write: (stream, buffer, offset, length, pos) => length,
+ llseek: () => 0,
});
FS.mkdev('/dev/null', FS.makedev(1, 3));
// setup /dev/tty and /dev/tty1
@@ -2724,7 +2743,7 @@
FS.mkdir('/proc/self/fd');
FS.mount({
mount() {
- var node = FS.createNode(proc_self, 'fd', 16384 | 511 /* 0777 */, 73);
+ var node = FS.createNode(proc_self, 'fd', 16895, 73);
node.node_ops = {
lookup(parent, name) {
var fd = +name;
@@ -2773,12 +2792,6 @@
var stderr = FS.open('/dev/stderr', 1);
},
staticInit() {
- // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info)
- [44].forEach((code) => {
- FS.genericErrors[code] = new FS.ErrnoError(code);
- FS.genericErrors[code].stack = '<generic error, no stack>';
- });
-
FS.nameTable = new Array(4096);
FS.mount(MEMFS, {}, '/');
@@ -3837,9 +3850,7 @@
var ENV = {
};
- var getExecutableName = () => {
- return thisProgram || './this.program';
- };
+ var getExecutableName = () => thisProgram || './this.program';
var getEnvStrings = () => {
if (!getEnvStrings.strings) {
// Default values.
@@ -4059,6 +4070,13 @@
FS.staticInit();
// Set module methods based on EXPORTED_RUNTIME_METHODS
;
+
+ // This error may happen quite a bit. To avoid overhead we reuse it (and
+ // suffer a lack of stack info).
+ MEMFS.doesNotExistError = new FS.ErrnoError(44);
+ /** @suppress {checkTypes} */
+ MEMFS.doesNotExistError.stack = '<generic error, no stack>';
+ ;
var wasmImports = {
/** @export */
__assert_fail: ___assert_fail,
@@ -4400,7 +4418,7 @@
// end include: postamble.js
-// include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3470100/ext/wasm/bld/post-js.speedtest1-vanilla.js
+// include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3480000/ext/wasm/bld/post-js.speedtest1-vanilla.js
/* BEGIN FILE: api/post-js-header.js */
/**
post-js-header.js is to be prepended to other code to create
@@ -4461,11 +4479,11 @@
/*
** This code was built from sqlite3 version...
**
-** SQLITE_VERSION "3.47.1"
-** SQLITE_VERSION_NUMBER 3047001
-** SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e"
+** SQLITE_VERSION "3.48.0"
+** SQLITE_VERSION_NUMBER 3048000
+** SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010"
**
-** Using the Emscripten SDK version 3.1.71.
+** Using the Emscripten SDK version 3.1.73.
*/
/* END FILE: ./bld/sqlite3-license-version.js */
/* BEGIN FILE: api/sqlite3-api-prologue.js */
@@ -8723,7 +8741,7 @@
if(1===argc) return xcvPart.get(typeName);
else if(2===argc){
if(!adapter){
- delete xcvPart.get(typeName);
+ xcvPart.delete(typeName);
return func;
}else if(!(adapter instanceof Function)){
toss(modeName,"requires a function argument.");
@@ -11561,7 +11579,7 @@
/* END FILE: api/sqlite3-api-glue.c-pp.js */
/* BEGIN FILE: ./bld/sqlite3-api-build-version.js */
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
- sqlite3.version = {"libVersion": "3.47.1", "libVersionNumber": 3047001, "sourceId": "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e","downloadVersion": 3470100};
+ sqlite3.version = {"libVersion": "3.48.0", "libVersionNumber": 3048000, "sourceId": "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010","downloadVersion": 3480000};
});
/* END FILE: ./bld/sqlite3-api-build-version.js */
/* BEGIN FILE: api/sqlite3-api-oo1.c-pp.js */
@@ -17606,7 +17624,7 @@
scope. */
})/*postRun.push(...)*/;
/* END FILE: api/post-js-footer.js */
-// end include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3470100/ext/wasm/bld/post-js.speedtest1-vanilla.js
+// end include: /usr/local/google/home/dlehmann/JetStream/sqlite3/sqlite-src-3480000/ext/wasm/bld/post-js.speedtest1-vanilla.js
// include: postamble_modularize.js
// In MODULARIZE mode we wrap the generated code in a factory function
diff --git a/sqlite3/build/jswasm/speedtest1.wasm b/sqlite3/build/jswasm/speedtest1.wasm
index 6a6e16a..94c755b 100644
--- a/sqlite3/build/jswasm/speedtest1.wasm
+++ b/sqlite3/build/jswasm/speedtest1.wasm
Binary files differ
diff --git a/sqlite3/polyfills.js b/sqlite3/polyfills.js
deleted file mode 100644
index a972f8f..0000000
--- a/sqlite3/polyfills.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2024 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Polyfills for missing browser APIs in JavaScript shells.
-
-// `TextEncoder` and `TextDecoder`. These are called only a few times with short
-// ASCII strings, so this is sufficient and not performance-critical.
-class TextEncoder {
- encode(string) {
- return Uint8Array.from(string, (char) => {
- let byte = char.codePointAt(0);
- if (byte > 0x7f)
- throw new Error("TextEncoder polyfill only supports ASCII");
- return byte;
- });
- }
-}
-class TextDecoder {
- decode(array) {
- for (let byte of array) {
- if (byte > 0x7f)
- throw new Error("TextDecoder polyfill only supports ASCII");
- }
- return String.fromCharCode.apply(null, array);
- }
-}
-
-// `crypto.getRandomValues`. This is called only once during setup.
-// The implementation is copied from an Emscripten error message proposing this.
-globalThis.crypto = {
- getRandomValues: (array) => {
- for (var i = 0; i < array.length; i++) array[i] = (Math.random() * 256) | 0;
- },
-};
-
-// Empty `URLSearchParams` has just the same interface as a `Map`.
-globalThis.URLSearchParams = Map;
-
-// `self` global object.
-globalThis.self = this;
-
-globalThis.console = {
- log: print,
- debug: print,
- warn: print,
- error: print,
-};