diff --git a/README.md b/README.md index 0a087ec..8a4f397 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ embeds a build of SQLite into your application. This module replaces the SQLite [OS Interface](https://www.sqlite.org/vfs.html) (aka VFS) with a pure Go implementation. -This has numerous benefits, but also comes with some caveats. +This has numerous benefits, but also comes with some drawbacks. #### Write-Ahead Logging @@ -55,6 +55,9 @@ The pure Go VFS is stress tested by running an unmodified build of SQLite's [mptest](https://github.com/sqlite/sqlite/blob/master/mptest/mptest.c) on Linux, macOS and Windows. +Performance is tested by running +[speedtest1](https://github.com/sqlite/sqlite/blob/master/test/speedtest1.c). + ### Roadmap - [ ] advanced SQLite features diff --git a/tests/mptest/mptest_test.go b/tests/mptest/mptest_test.go index 5eff633..23b18de 100644 --- a/tests/mptest/mptest_test.go +++ b/tests/mptest/mptest_test.go @@ -99,11 +99,12 @@ func Test_config01(t *testing.T) { ctx, vfs := vfsContext(newContext(t)) name := filepath.Join(t.TempDir(), "test.db") cfg := config(ctx).WithArgs("mptest", name, "config01.test") - _, err := rt.InstantiateModule(ctx, module, cfg) + mod, err := rt.InstantiateModule(ctx, module, cfg) if err != nil { t.Error(err) } vfs.Close() + mod.Close(ctx) } func Test_config02(t *testing.T) { @@ -117,11 +118,12 @@ func Test_config02(t *testing.T) { ctx, vfs := vfsContext(newContext(t)) name := filepath.Join(t.TempDir(), "test.db") cfg := config(ctx).WithArgs("mptest", name, "config02.test") - _, err := rt.InstantiateModule(ctx, module, cfg) + mod, err := rt.InstantiateModule(ctx, module, cfg) if err != nil { t.Error(err) } vfs.Close() + mod.Close(ctx) } func Test_crash01(t *testing.T) { @@ -132,11 +134,12 @@ func Test_crash01(t *testing.T) { ctx, vfs := vfsContext(newContext(t)) name := filepath.Join(t.TempDir(), "test.db") cfg := config(ctx).WithArgs("mptest", name, "crash01.test") - _, err := rt.InstantiateModule(ctx, module, cfg) + mod, err := rt.InstantiateModule(ctx, module, cfg) if err != nil { t.Error(err) } vfs.Close() + mod.Close(ctx) } func Test_multiwrite01(t *testing.T) { @@ -147,11 +150,12 @@ func Test_multiwrite01(t *testing.T) { ctx, vfs := vfsContext(newContext(t)) name := filepath.Join(t.TempDir(), "test.db") cfg := config(ctx).WithArgs("mptest", name, "multiwrite01.test") - _, err := rt.InstantiateModule(ctx, module, cfg) + mod, err := rt.InstantiateModule(ctx, module, cfg) if err != nil { t.Error(err) } vfs.Close() + mod.Close(ctx) } func newContext(t *testing.T) context.Context { diff --git a/tests/mptest/testdata/build.sh b/tests/mptest/testdata/build.sh index a7e42d3..7a081f8 100755 --- a/tests/mptest/testdata/build.sh +++ b/tests/mptest/testdata/build.sh @@ -4,12 +4,12 @@ set -eo pipefail cd -P -- "$(dirname -- "$0")" if [ ! -f "mptest.c" ]; then - curl -sOL "https://github.com/sqlite/sqlite/raw/master/mptest/mptest.c" - curl -sOL "https://github.com/sqlite/sqlite/raw/master/mptest/config01.test" - curl -sOL "https://github.com/sqlite/sqlite/raw/master/mptest/config02.test" - curl -sOL "https://github.com/sqlite/sqlite/raw/master/mptest/crash01.test" - curl -sOL "https://github.com/sqlite/sqlite/raw/master/mptest/crash02.subtest" - curl -sOL "https://github.com/sqlite/sqlite/raw/master/mptest/multiwrite01.test" + curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/mptest.c" + curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/config01.test" + curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/config02.test" + curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/crash01.test" + curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/crash02.subtest" + curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/multiwrite01.test" fi zig cc --target=wasm32-wasi -flto -g0 -Os \ diff --git a/tests/speedtest1/speedtest1_test.go b/tests/speedtest1/speedtest1_test.go new file mode 100644 index 0000000..9a3f913 --- /dev/null +++ b/tests/speedtest1/speedtest1_test.go @@ -0,0 +1,92 @@ +package speedtest1 + +import ( + "bytes" + "context" + "crypto/rand" + "io" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "testing" + + _ "embed" + _ "unsafe" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" + + _ "github.com/ncruces/go-sqlite3" +) + +//go:embed testdata/speedtest1.wasm +var binary []byte + +//go:linkname vfsNewEnvModuleBuilder github.com/ncruces/go-sqlite3.vfsNewEnvModuleBuilder +func vfsNewEnvModuleBuilder(r wazero.Runtime) wazero.HostModuleBuilder + +//go:linkname vfsContext github.com/ncruces/go-sqlite3.vfsContext +func vfsContext(ctx context.Context) (context.Context, io.Closer) + +var ( + rt wazero.Runtime + module wazero.CompiledModule + output bytes.Buffer + options []string +) + +func init() { + ctx := context.TODO() + + rt = wazero.NewRuntime(ctx) + wasi_snapshot_preview1.MustInstantiate(ctx, rt) + env := vfsNewEnvModuleBuilder(rt) + _, err := env.Instantiate(ctx) + if err != nil { + panic(err) + } + + module, err = rt.CompileModule(ctx, binary) + if err != nil { + panic(err) + } +} + +func TestMain(m *testing.M) { + i := 1 + options = append(options, "speedtest1") + for _, arg := range os.Args[1:] { + if strings.HasPrefix(arg, "-test.") { + os.Args[i] = arg + i++ + } else { + options = append(options, arg) + } + } + os.Args = os.Args[:i] + + code := m.Run() + io.Copy(os.Stderr, &output) + os.Exit(code) +} + +func Benchmark_speedtest1(b *testing.B) { + output.Reset() + ctx, vfs := vfsContext(context.Background()) + name := filepath.Join(b.TempDir(), "test.db") + args := append(options, "--size", strconv.Itoa(b.N), name) + cfg := wazero.NewModuleConfig(). + WithArgs(args...).WithName("speedtest1"). + WithStdout(&output).WithStderr(&output). + WithSysWalltime().WithSysNanotime().WithSysNanosleep(). + WithOsyield(runtime.Gosched). + WithRandSource(rand.Reader) + mod, err := rt.InstantiateModule(ctx, module, cfg) + if err != nil { + b.Error(err) + } + vfs.Close() + mod.Close(ctx) +} diff --git a/tests/speedtest1/testdata/.gitattributes b/tests/speedtest1/testdata/.gitattributes new file mode 100644 index 0000000..f4493d4 --- /dev/null +++ b/tests/speedtest1/testdata/.gitattributes @@ -0,0 +1 @@ +speedtest1.wasm filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/tests/speedtest1/testdata/.gitignore b/tests/speedtest1/testdata/.gitignore new file mode 100644 index 0000000..6e07e2d --- /dev/null +++ b/tests/speedtest1/testdata/.gitignore @@ -0,0 +1 @@ +speedtest1.c \ No newline at end of file diff --git a/tests/speedtest1/testdata/build.sh b/tests/speedtest1/testdata/build.sh new file mode 100755 index 0000000..0d6e272 --- /dev/null +++ b/tests/speedtest1/testdata/build.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -eo pipefail + +cd -P -- "$(dirname -- "$0")" + +if [ ! -f "mptest.c" ]; then + curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/test/speedtest1.c" +fi + +zig cc --target=wasm32-wasi -flto -g0 -Os \ + -o speedtest1.wasm main.c \ + -I../../../sqlite3 \ + -mmutable-globals \ + -mbulk-memory -mreference-types \ + -mnontrapping-fptoint -msign-ext \ + -D_HAVE_SQLITE_CONFIG_H + +if which wasm-opt; then + wasm-opt -g -O -o speedtest1.tmp speedtest1.wasm + mv speedtest1.tmp speedtest1.wasm +fi \ No newline at end of file diff --git a/tests/speedtest1/testdata/main.c b/tests/speedtest1/testdata/main.c new file mode 100644 index 0000000..fb5ed35 --- /dev/null +++ b/tests/speedtest1/testdata/main.c @@ -0,0 +1,20 @@ +#include +#include + +#include "sqlite3.c" +// +#include "os.c" +#include "qsort.c" +#include "time.c" + +sqlite3_destructor_type malloc_destructor = &free; +size_t sqlite3_interrupt_offset = offsetof(sqlite3, u1.isInterrupted); + +int sqlite3_os_init() { + return sqlite3_vfs_register(os_vfs(), /*default=*/true); +} + +__attribute__((constructor)) void premain() { sqlite3_initialize(); } + +#define randomFunc(args...) randomFunc2(args) +#include "speedtest1.c" diff --git a/tests/speedtest1/testdata/speedtest1.wasm b/tests/speedtest1/testdata/speedtest1.wasm new file mode 100644 index 0000000..8637f34 --- /dev/null +++ b/tests/speedtest1/testdata/speedtest1.wasm @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:996c2445ad12c91dce1a59ab5929cfaa3a09a4ac82859fecc7de5e5f9c955f80 +size 1001607