From e38332623e538b2e8cb1d99703d007929c87ca5c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 20 Dec 2025 19:22:31 +0000 Subject: [PATCH 1/2] Add SQLite benchmark test Tests database operations: create table, insert, query, update, delete using in-memory SQLite databases for consistent benchmarking. - Hemlock uses @stdlib/sqlite with FFI bindings - Python uses sqlite3 stdlib - JavaScript uses better-sqlite3 - C uses libsqlite3 directly - Ruby uses sqlite3 gem Input sizes: 100 rows (quick), 1000 rows (default) --- benchmarks/sqlite_bench/sqlite_bench.c | 107 +++++++++++++++++++++++ benchmarks/sqlite_bench/sqlite_bench.hml | 63 +++++++++++++ benchmarks/sqlite_bench/sqlite_bench.js | 47 ++++++++++ benchmarks/sqlite_bench/sqlite_bench.py | 41 +++++++++ benchmarks/sqlite_bench/sqlite_bench.rb | 39 +++++++++ run.sh | 7 +- 6 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 benchmarks/sqlite_bench/sqlite_bench.c create mode 100644 benchmarks/sqlite_bench/sqlite_bench.hml create mode 100644 benchmarks/sqlite_bench/sqlite_bench.js create mode 100644 benchmarks/sqlite_bench/sqlite_bench.py create mode 100644 benchmarks/sqlite_bench/sqlite_bench.rb diff --git a/benchmarks/sqlite_bench/sqlite_bench.c b/benchmarks/sqlite_bench/sqlite_bench.c new file mode 100644 index 0000000..a49e993 --- /dev/null +++ b/benchmarks/sqlite_bench/sqlite_bench.c @@ -0,0 +1,107 @@ +// SQLite benchmark - C +// Tests: create table, insert, query, update, delete + +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + int n = 1000; + if (argc > 1) { + n = atoi(argv[1]); + } + + sqlite3 *db; + sqlite3_stmt *stmt; + char sql[256]; + int rc; + long long total_score = 0; + + // Use in-memory database for benchmarking + rc = sqlite3_open(":memory:", &db); + if (rc != SQLITE_OK) { + fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db)); + return 1; + } + + // Create table + rc = sqlite3_exec(db, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, score INTEGER)", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return 1; + } + + // Begin transaction for inserts + sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL); + + // Prepare insert statement + rc = sqlite3_prepare_v2(db, "INSERT INTO users (name, email, score) VALUES (?, ?, ?)", -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "Prepare error: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return 1; + } + + // Insert N rows + char name[32], email[64]; + for (int i = 0; i < n; i++) { + snprintf(name, sizeof(name), "user%d", i); + snprintf(email, sizeof(email), "user%d@example.com", i); + + sqlite3_bind_text(stmt, 1, name, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, email, -1, SQLITE_TRANSIENT); + sqlite3_bind_int(stmt, 3, i * 10); + + sqlite3_step(stmt); + sqlite3_reset(stmt); + } + sqlite3_finalize(stmt); + + // Commit transaction + sqlite3_exec(db, "COMMIT", NULL, NULL, NULL); + + // Query all rows + rc = sqlite3_prepare_v2(db, "SELECT * FROM users", -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "Query prepare error: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return 1; + } + + while (sqlite3_step(stmt) == SQLITE_ROW) { + total_score += sqlite3_column_int(stmt, 3); + } + sqlite3_finalize(stmt); + + // Begin transaction for updates + sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL); + + // Prepare update statement + rc = sqlite3_prepare_v2(db, "UPDATE users SET score = score + 1 WHERE id = ?", -1, &stmt, NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "Update prepare error: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return 1; + } + + // Update all rows + for (int i = 0; i < n; i++) { + sqlite3_bind_int(stmt, 1, i + 1); + sqlite3_step(stmt); + sqlite3_reset(stmt); + } + sqlite3_finalize(stmt); + + // Commit transaction + sqlite3_exec(db, "COMMIT", NULL, NULL, NULL); + + // Delete all rows + sqlite3_exec(db, "DELETE FROM users", NULL, NULL, NULL); + + sqlite3_close(db); + + printf("%lld\n", total_score); + return 0; +} diff --git a/benchmarks/sqlite_bench/sqlite_bench.hml b/benchmarks/sqlite_bench/sqlite_bench.hml new file mode 100644 index 0000000..8375098 --- /dev/null +++ b/benchmarks/sqlite_bench/sqlite_bench.hml @@ -0,0 +1,63 @@ +// SQLite benchmark - Hemlock +// Tests: create table, insert, query, update, delete + +import { open_db, exec, query, close_db, begin, commit } from "@stdlib/sqlite"; + +fn parse_int(s) { + let result = 0; + let i = 0; + while (i < s.length) { + let ch = s[i]; + let code: i32 = ch; + let zero: i32 = '0'; + result = result * 10 + (code - zero); + i = i + 1; + } + return result; +} + +let n = 1000; +if (args.length > 1) { + n = parse_int(args[1]); +} + +// Use in-memory database for benchmarking +let db = open_db(":memory:"); + +// Create table +exec(db, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, score INTEGER)"); + +// Insert N rows using transaction for performance +begin(db); +let i = 0; +while (i < n) { + exec(db, "INSERT INTO users (name, email, score) VALUES (?, ?, ?)", + ["user" + i, "user" + i + "@example.com", i * 10]); + i = i + 1; +} +commit(db); + +// Query all rows +let rows = query(db, "SELECT * FROM users"); +let total_score = 0; +i = 0; +while (i < rows.length) { + total_score = total_score + rows[i].score; + i = i + 1; +} + +// Update all rows +begin(db); +i = 0; +while (i < n) { + exec(db, "UPDATE users SET score = score + 1 WHERE id = ?", [i + 1]); + i = i + 1; +} +commit(db); + +// Delete all rows +exec(db, "DELETE FROM users"); + +close_db(db); + +print(total_score); diff --git a/benchmarks/sqlite_bench/sqlite_bench.js b/benchmarks/sqlite_bench/sqlite_bench.js new file mode 100644 index 0000000..0749c32 --- /dev/null +++ b/benchmarks/sqlite_bench/sqlite_bench.js @@ -0,0 +1,47 @@ +// SQLite benchmark - JavaScript (Node.js with better-sqlite3) +// Tests: create table, insert, query, update, delete + +const Database = require('better-sqlite3'); + +const n = parseInt(process.argv[2]) || 1000; + +// Use in-memory database for benchmarking +const db = new Database(':memory:'); + +// Create table +db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, score INTEGER)'); + +// Insert N rows using transaction for performance +const insert = db.prepare('INSERT INTO users (name, email, score) VALUES (?, ?, ?)'); +const insertMany = db.transaction((items) => { + for (const item of items) insert.run(item.name, item.email, item.score); +}); + +const items = []; +for (let i = 0; i < n; i++) { + items.push({ name: `user${i}`, email: `user${i}@example.com`, score: i * 10 }); +} +insertMany(items); + +// Query all rows +const rows = db.prepare('SELECT * FROM users').all(); +let totalScore = 0; +for (const row of rows) { + totalScore += row.score; +} + +// Update all rows +const update = db.prepare('UPDATE users SET score = score + 1 WHERE id = ?'); +const updateMany = db.transaction(() => { + for (let i = 0; i < n; i++) { + update.run(i + 1); + } +}); +updateMany(); + +// Delete all rows +db.exec('DELETE FROM users'); + +db.close(); + +console.log(totalScore); diff --git a/benchmarks/sqlite_bench/sqlite_bench.py b/benchmarks/sqlite_bench/sqlite_bench.py new file mode 100644 index 0000000..3a645e9 --- /dev/null +++ b/benchmarks/sqlite_bench/sqlite_bench.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# SQLite benchmark - Python +# Tests: create table, insert, query, update, delete + +import sqlite3 +import sys + +n = 1000 +if len(sys.argv) > 1: + n = int(sys.argv[1]) + +# Use in-memory database for benchmarking +conn = sqlite3.connect(":memory:") +cursor = conn.cursor() + +# Create table +cursor.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, score INTEGER)") + +# Insert N rows +for i in range(n): + cursor.execute("INSERT INTO users (name, email, score) VALUES (?, ?, ?)", + (f"user{i}", f"user{i}@example.com", i * 10)) +conn.commit() + +# Query all rows +cursor.execute("SELECT * FROM users") +rows = cursor.fetchall() +total_score = sum(row[3] for row in rows) + +# Update all rows +for i in range(n): + cursor.execute("UPDATE users SET score = score + 1 WHERE id = ?", (i + 1,)) +conn.commit() + +# Delete all rows +cursor.execute("DELETE FROM users") +conn.commit() + +conn.close() + +print(total_score) diff --git a/benchmarks/sqlite_bench/sqlite_bench.rb b/benchmarks/sqlite_bench/sqlite_bench.rb new file mode 100644 index 0000000..f861fc3 --- /dev/null +++ b/benchmarks/sqlite_bench/sqlite_bench.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby +# SQLite benchmark - Ruby +# Tests: create table, insert, query, update, delete + +require 'sqlite3' + +n = (ARGV[0] || 1000).to_i + +# Use in-memory database for benchmarking +db = SQLite3::Database.new(':memory:') + +# Create table +db.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, score INTEGER)') + +# Insert N rows using transaction for performance +db.transaction do + n.times do |i| + db.execute('INSERT INTO users (name, email, score) VALUES (?, ?, ?)', + ["user#{i}", "user#{i}@example.com", i * 10]) + end +end + +# Query all rows +rows = db.execute('SELECT * FROM users') +total_score = rows.sum { |row| row[3] } + +# Update all rows +db.transaction do + n.times do |i| + db.execute('UPDATE users SET score = score + 1 WHERE id = ?', [i + 1]) + end +end + +# Delete all rows +db.execute('DELETE FROM users') + +db.close + +puts total_score diff --git a/run.sh b/run.sh index 82c4a49..29973ed 100755 --- a/run.sh +++ b/run.sh @@ -28,7 +28,7 @@ usage() { echo " --timeout N Timeout per iteration in seconds (default: 60)" echo " --help, -h Show this help" echo "" - echo "Benchmarks: fib, array_sum, string_concat, primes_sieve, quicksort, binary_tree, graph_bfs, json_serialize, json_deserialize, hash_sha256" + echo "Benchmarks: fib, array_sum, string_concat, primes_sieve, quicksort, binary_tree, graph_bfs, json_serialize, json_deserialize, hash_sha256, sqlite_bench" echo " (leave empty to run all)" } @@ -94,6 +94,9 @@ get_input() { hash_sha256) [[ $QUICK_MODE -eq 1 ]] && echo 1000 || echo 10000 ;; + sqlite_bench) + [[ $QUICK_MODE -eq 1 ]] && echo 100 || echo 1000 + ;; esac } @@ -301,7 +304,7 @@ run_all() { if [[ -n "$BENCHMARK" ]]; then benchmarks="$BENCHMARK" else - benchmarks="fib array_sum string_concat primes_sieve quicksort binary_tree graph_bfs json_serialize json_deserialize hash_sha256" + benchmarks="fib array_sum string_concat primes_sieve quicksort binary_tree graph_bfs json_serialize json_deserialize hash_sha256 sqlite_bench" fi local languages="c hemlockc hemlock python javascript ruby" From a6bd7a8198d29e1112f05431c3ade6294aee889e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 20 Dec 2025 19:24:05 +0000 Subject: [PATCH 2/2] Add -lsqlite3 linker flag for sqlite_bench C compilation --- run.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/run.sh b/run.sh index 29973ed..ab16c54 100755 --- a/run.sh +++ b/run.sh @@ -146,7 +146,9 @@ run_benchmark() { local src="$bench_dir/${bench}.c" local bin="$BUILD_DIR/${bench}_c" [[ ! -f "$src" ]] && return 1 - error_output=$(gcc -O3 -o "$bin" "$src" 2>&1) + local extra_libs="" + [[ "$bench" == "sqlite_bench" ]] && extra_libs="-lsqlite3" + error_output=$(gcc -O3 -o "$bin" "$src" $extra_libs 2>&1) if [[ $? -ne 0 ]]; then echo "ERROR:compile:$error_output" return 2