From e6c9f189342619d1b36ff875d52fcb063400da04 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 4 Apr 2025 10:56:12 +0100 Subject: [PATCH] Benchmark libc. --- ext/blobio/blob_test.go | 2 +- ext/bloom/bloom_test.go | 2 +- ext/closure/closure_test.go | 3 +- ext/csv/csv_test.go | 3 +- ext/pivot/pivot_test.go | 3 +- ext/statement/stmt_test.go | 3 +- ext/stats/stats_test.go | 3 +- sqlite3/libc/build.sh | 31 ++++++++ sqlite3/libc/libc.wasm | Bin 0 -> 428 bytes sqlite3/libc/libc.wat | 137 ++++++++++++++++++++++++++++++++ sqlite3/libc/libc_test.go | 96 ++++++++++++++++++++++ sqlite3/strings.c | 16 +++- tests/parallel/parallel_test.go | 2 +- 13 files changed, 290 insertions(+), 11 deletions(-) create mode 100755 sqlite3/libc/build.sh create mode 100755 sqlite3/libc/libc.wasm create mode 100644 sqlite3/libc/libc.wat create mode 100644 sqlite3/libc/libc_test.go diff --git a/ext/blobio/blob_test.go b/ext/blobio/blob_test.go index 5bb868b..447e83c 100644 --- a/ext/blobio/blob_test.go +++ b/ext/blobio/blob_test.go @@ -64,7 +64,7 @@ func Example() { func TestMain(m *testing.M) { sqlite3.AutoExtension(blobio.Register) sqlite3.AutoExtension(array.Register) - m.Run() + os.Exit(m.Run()) } func Test_readblob(t *testing.T) { diff --git a/ext/bloom/bloom_test.go b/ext/bloom/bloom_test.go index fb10131..5076575 100644 --- a/ext/bloom/bloom_test.go +++ b/ext/bloom/bloom_test.go @@ -14,7 +14,7 @@ import ( func TestMain(m *testing.M) { sqlite3.AutoExtension(bloom.Register) - m.Run() + os.Exit(m.Run()) } func TestRegister(t *testing.T) { diff --git a/ext/closure/closure_test.go b/ext/closure/closure_test.go index cbe2d9c..fcd8bf7 100644 --- a/ext/closure/closure_test.go +++ b/ext/closure/closure_test.go @@ -4,6 +4,7 @@ import ( _ "embed" "fmt" "log" + "os" "testing" "github.com/ncruces/go-sqlite3" @@ -14,7 +15,7 @@ import ( func TestMain(m *testing.M) { sqlite3.AutoExtension(closure.Register) - m.Run() + os.Exit(m.Run()) } func Example() { diff --git a/ext/csv/csv_test.go b/ext/csv/csv_test.go index 3732772..15c8fc2 100644 --- a/ext/csv/csv_test.go +++ b/ext/csv/csv_test.go @@ -3,6 +3,7 @@ package csv_test import ( "fmt" "log" + "os" "testing" "github.com/ncruces/go-sqlite3" @@ -56,7 +57,7 @@ func Example() { func TestMain(m *testing.M) { sqlite3.AutoExtension(csv.Register) - m.Run() + os.Exit(m.Run()) } func TestRegister(t *testing.T) { diff --git a/ext/pivot/pivot_test.go b/ext/pivot/pivot_test.go index 16c1b6d..1e4a84f 100644 --- a/ext/pivot/pivot_test.go +++ b/ext/pivot/pivot_test.go @@ -3,6 +3,7 @@ package pivot_test import ( "fmt" "log" + "os" "strings" "testing" @@ -85,7 +86,7 @@ func Example() { func TestMain(m *testing.M) { sqlite3.AutoExtension(pivot.Register) - m.Run() + os.Exit(m.Run()) } func TestRegister(t *testing.T) { diff --git a/ext/statement/stmt_test.go b/ext/statement/stmt_test.go index 2475fff..48a1d98 100644 --- a/ext/statement/stmt_test.go +++ b/ext/statement/stmt_test.go @@ -3,6 +3,7 @@ package statement_test import ( "fmt" "log" + "os" "testing" "github.com/ncruces/go-sqlite3" @@ -50,7 +51,7 @@ func Example() { func TestMain(m *testing.M) { sqlite3.AutoExtension(statement.Register) - m.Run() + os.Exit(m.Run()) } func TestRegister(t *testing.T) { diff --git a/ext/stats/stats_test.go b/ext/stats/stats_test.go index 4008ce4..9862728 100644 --- a/ext/stats/stats_test.go +++ b/ext/stats/stats_test.go @@ -2,6 +2,7 @@ package stats_test import ( "math" + "os" "testing" "github.com/ncruces/go-sqlite3" @@ -12,7 +13,7 @@ import ( func TestMain(m *testing.M) { sqlite3.AutoExtension(stats.Register) - m.Run() + os.Exit(m.Run()) } func TestRegister_variance(t *testing.T) { diff --git a/sqlite3/libc/build.sh b/sqlite3/libc/build.sh new file mode 100755 index 0000000..56102b6 --- /dev/null +++ b/sqlite3/libc/build.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd -P -- "$(dirname -- "$0")" + +ROOT=../../ +BINARYEN="$ROOT/tools/binaryen/bin" +WASI_SDK="$ROOT/tools/wasi-sdk/bin" + +trap 'rm -f libc.tmp' EXIT + +"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -g0 -O2 \ + -o libc.wasm ../strings.c \ + -mexec-model=reactor \ + -msimd128 -mmutable-globals -mmultivalue \ + -mbulk-memory -mreference-types \ + -mnontrapping-fptoint -msign-ext \ + -fno-stack-protector -fno-stack-clash-protection \ + -Wl,--initial-memory=16777216 \ + -Wl,--export=memset \ + -Wl,--export=memcpy \ + -Wl,--export=memcmp + +"$BINARYEN/wasm-ctor-eval" -g -c _initialize libc.wasm -o libc.tmp +"$BINARYEN/wasm-opt" -g --strip --strip-producers -c -O3 \ + libc.tmp -o libc.wasm \ + --enable-simd --enable-mutable-globals --enable-multivalue \ + --enable-bulk-memory --enable-reference-types \ + --enable-nontrapping-float-to-int --enable-sign-ext + +"$BINARYEN/wasm-dis" -o libc.wat libc.wasm \ No newline at end of file diff --git a/sqlite3/libc/libc.wasm b/sqlite3/libc/libc.wasm new file mode 100755 index 0000000000000000000000000000000000000000..2db1427e28f8d69b89694c230d15e03cbbf95980 GIT binary patch literal 428 zcmYk2F;9gs6ov0?QILuZPHxr2!Bk9ia(oQN7&rfb7VIPVC{!pa6VJgP;{SB8_2ng| zY0o*${f2Wv4jKUfBYdLsJmX9Q>hey2uqNSYu&b5s`iX!ygR<~vN;3iapHdqj`-t&3 z(gZkg?>s-mrB~PS3@LHJz7~bDyy2Vkj)?t+pe{}G^TE=Y-wy) zw^oEht)DNit_3T`W|eJrDWajS+Dv^|k@jk-dexRHvy)MSi1pam`b{>Y66rrC>x}&c D!z5sz literal 0 HcmV?d00001 diff --git a/sqlite3/libc/libc.wat b/sqlite3/libc/libc.wat new file mode 100644 index 0000000..ae9334c --- /dev/null +++ b/sqlite3/libc/libc.wat @@ -0,0 +1,137 @@ +(module $libc.wasm + (type $0 (func (param i32 i32 i32) (result i32))) + (memory $0 256) + (data $0 (i32.const 1024) "\01") + (export "memory" (memory $0)) + (export "memset" (func $memset)) + (export "memcpy" (func $memcpy)) + (export "memcmp" (func $memcmp)) + (func $memset (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (memory.fill + (local.get $0) + (local.get $1) + (local.get $2) + ) + (local.get $0) + ) + (func $memcpy (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (memory.copy + (local.get $0) + (local.get $1) + (local.get $2) + ) + (local.get $0) + ) + (func $memcmp (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + (block $block2 + (block $block1 + (block $block + (br_if $block + (i32.lt_u + (local.get $2) + (i32.const 8) + ) + ) + (br_if $block + (i32.and + (i32.or + (local.get $0) + (local.get $1) + ) + (i32.const 7) + ) + ) + (loop $label + (br_if $block1 + (i64.ne + (i64.load + (local.get $0) + ) + (i64.load + (local.get $1) + ) + ) + ) + (local.set $1 + (i32.add + (local.get $1) + (i32.const 8) + ) + ) + (local.set $0 + (i32.add + (local.get $0) + (i32.const 8) + ) + ) + (br_if $label + (i32.gt_u + (local.tee $2 + (i32.sub + (local.get $2) + (i32.const 8) + ) + ) + (i32.const 7) + ) + ) + ) + ) + (br_if $block2 + (i32.eqz + (local.get $2) + ) + ) + ) + (loop $label1 + (if + (i32.ne + (local.tee $3 + (i32.load8_u + (local.get $0) + ) + ) + (local.tee $4 + (i32.load8_u + (local.get $1) + ) + ) + ) + (then + (return + (i32.sub + (local.get $3) + (local.get $4) + ) + ) + ) + ) + (local.set $1 + (i32.add + (local.get $1) + (i32.const 1) + ) + ) + (local.set $0 + (i32.add + (local.get $0) + (i32.const 1) + ) + ) + (br_if $label1 + (local.tee $2 + (i32.sub + (local.get $2) + (i32.const 1) + ) + ) + ) + ) + ) + (i32.const 0) + ) + ;; features section: mutable-globals, nontrapping-float-to-int, simd, bulk-memory, sign-ext, reference-types, multivalue, bulk-memory-opt +) + diff --git a/sqlite3/libc/libc_test.go b/sqlite3/libc/libc_test.go new file mode 100644 index 0000000..480e758 --- /dev/null +++ b/sqlite3/libc/libc_test.go @@ -0,0 +1,96 @@ +package libc + +import ( + "context" + _ "embed" + "os" + "testing" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" +) + +//go:embed libc.wasm +var binary []byte + +const ( + size = 1024 * 1024 * 4 + ptr1 = 1024 * 1024 + ptr2 = ptr1 + size +) + +var ( + memory []byte + module api.Module + memset api.Function + memcpy api.Function + memcmp api.Function +) + +func call(fn api.Function, arg ...uint64) { + fn.CallWithStack(context.Background(), arg) +} + +func TestMain(m *testing.M) { + ctx := context.Background() + + runtime := wazero.NewRuntime(ctx) + mod, err := runtime.Instantiate(ctx, binary) + if err != nil { + panic(err) + } + + module = mod + memset = mod.ExportedFunction("memset") + memcpy = mod.ExportedFunction("memcpy") + memcmp = mod.ExportedFunction("memcmp") + memory, _ = mod.Memory().Read(0, mod.Memory().Size()) + + os.Exit(m.Run()) +} + +func Benchmark_memset(b *testing.B) { + clear(memory) + + b.ResetTimer() + for range b.N { + call(memset, ptr1, 3, size) + } + b.StopTimer() + + for i, v := range memory[ptr1 : ptr1+size] { + if v != 3 { + b.Fatal(i, v) + } + } +} + +func Benchmark_memcpy(b *testing.B) { + clear(memory) + call(memset, ptr2, 5, size) + + b.ResetTimer() + for range b.N { + call(memcpy, ptr1, ptr2, size) + } + b.StopTimer() + + for i, v := range memory[ptr1 : ptr1+size] { + if v != 5 { + b.Fatal(i, v) + } + } +} + +func Benchmark_memcmp(b *testing.B) { + clear(memory) + call(memset, ptr1, 7, size) + call(memset, ptr2, 7, size) + call(memset, ptr2+size/2, 5, size) + + b.ResetTimer() + for range b.N { + call(memcmp, ptr1, ptr2, size) + } + b.StopTimer() +} diff --git a/sqlite3/strings.c b/sqlite3/strings.c index 1320b15..9a4ab18 100644 --- a/sqlite3/strings.c +++ b/sqlite3/strings.c @@ -3,9 +3,19 @@ #include #if defined(__wasm_bulk_memory__) -#define memset __builtin_memset -#define memcpy __builtin_memcpy -#define memmove __builtin_memmove + +void *memset(void *dest, int c, size_t n) { + return __builtin_memset(dest, c, n); +} + +void *memcpy(void *restrict dest, const void *restrict src, size_t n) { + return __builtin_memcpy(dest, src, n); +} + +void *memmove(void *dest, const void *src, size_t n) { + return __builtin_memmove(dest, src, n); +} + #endif #define ONES (~(uintmax_t)(0) / UCHAR_MAX) diff --git a/tests/parallel/parallel_test.go b/tests/parallel/parallel_test.go index 6cdb682..cb59874 100644 --- a/tests/parallel/parallel_test.go +++ b/tests/parallel/parallel_test.go @@ -35,7 +35,7 @@ func TestMain(m *testing.M) { log.Printf("%v (%d): %s", code, code, msg) } }) - m.Run() + os.Exit(m.Run()) } func Test_parallel(t *testing.T) {