diff options
author | John Turner <jturner.usa@gmail.com> | 2025-09-14 00:06:33 -0400 |
---|---|---|
committer | John Turner <jturner.usa@gmail.com> | 2025-09-14 00:06:33 -0400 |
commit | c6bd9c80ef5038cca580191148d9b88ab505bcf6 (patch) | |
tree | 596ab05ee81a5d5ad5c351fdef5e78eb50efe708 | |
download | sqlite-kv-bench-c6bd9c80ef5038cca580191148d9b88ab505bcf6.tar.gz |
init
-rw-r--r-- | .dir-locals.el | 19 | ||||
-rw-r--r-- | README.org | 12 | ||||
-rw-r--r-- | meson.build | 14 | ||||
-rwxr-xr-x | scripts/mkdb.py | 73 | ||||
-rw-r--r-- | src/main.cpp | 46 | ||||
-rw-r--r-- | subprojects/.wraplock | 0 | ||||
m--------- | subprojects/boost-sqlite | 6 |
7 files changed, 170 insertions, 0 deletions
diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..a1a3538 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,19 @@ +((c++-ts-mode + . ((fmt-executable . "clang-format") + (eval . (setq-local fmt-args `("--assume-filename" ,(buffer-file-name)))) + (eval . (add-hook 'before-save-hook 'fmt-current-buffer nil t)) + (eval . (add-to-list 'eglot-server-programs '(c++-mode . ("clangd" "-header-insertion=never" "-clang-tidy")))) + (eval . (eglot-ensure)) + (eval . (add-hook 'eglot-managed-mode-hook (lambda () + (eglot-inlay-hints-mode -1)))) + (eval . (company-mode 1)))) + (meson-mode + . ((fmt-executable . "meson") + (fmt-args . ("format" "-")) + (eval . (add-hook 'before-save-hook 'fmt-current-buffer nil t)))) + (python-ts-mode . ((eval . (flycheck-mode 1)) + (eval . (flycheck-select-checker 'python-mypy)) + (eval . (flycheck-add-next-checker 'python-mypy (cons t 'python-flake8))) + (eval . (add-hook 'before-save-hook 'fmt-current-buffer nil t)) + (eval . (setq-local fmt-executable "black" + fmt-args '("-")))))) diff --git a/README.org b/README.org new file mode 100644 index 0000000..0a4609e --- /dev/null +++ b/README.org @@ -0,0 +1,12 @@ +* how to use +First create the test database with ~scripts/mkdb.py~. + +The benchmark executable reads keys from stdin. + +An example run of the benchmark: + +#+BEGIN_SRC_BASH +sqlite3 "${database}" "select kv.key from kv" | shuf | bench ${database} +#+END_SRC + + diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..642de1e --- /dev/null +++ b/meson.build @@ -0,0 +1,14 @@ +project( + 'sqlite-kv-bench', + 'cpp', + meson_version: '>=1.4.0', + default_options: ['warning_level=3', 'cpp_std=c++23'], +) + +cmake = import('cmake') + +boost_sqlite = cmake.subproject('boost-sqlite').dependency('boost-sqlite') + +sources = files('src/main.cpp') + +executable('bench', sources, dependencies: [boost_sqlite]) diff --git a/scripts/mkdb.py b/scripts/mkdb.py new file mode 100755 index 0000000..d3786f1 --- /dev/null +++ b/scripts/mkdb.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 + +import sys +import sqlite3 +import string +from argparse import ArgumentParser +from pathlib import Path +from random import choice, randrange + +characters = string.ascii_letters + string.digits + + +class Range: + + def __init__(self, arg: str): + start, end = arg.split("-") + + self.start = int(start) + self.end = int(end) + + +def truncate(path: Path) -> None: + with open(path, "w") as file: + file.truncate(0) + + +def main() -> int: + parser = ArgumentParser() + + parser.add_argument("--database", type=Path, required=True) + parser.add_argument("--word-list", type=Path, required=True) + parser.add_argument("--num-records", type=int, required=True) + parser.add_argument("--key-length", type=Range, required=True) + parser.add_argument("--value-length", type=Range, required=True) + + args = parser.parse_args() + + words = args.word_list.read_text().split("\n") + + truncate(args.database) + + def generate_key(length: int) -> str: + return "".join(choice(characters) for _ in range(length)) + + def generate_value(length: int) -> str: + return "-".join(choice(words) for _ in range(length)) + + with sqlite3.connect(args.database) as connection: + connection.execute("PRAGMA jorunal_mode = WAL") + connection.execute("create table kv(key text, value text)") + connection.execute("create index keys on kv(key)") + + for i in range(args.num_records): + key = generate_key(randrange(args.key_length.start, args.key_length.end)) + + val = generate_value( + randrange(args.value_length.start, args.value_length.end) + ) + + print(f"inserting record number {i}", file=sys.stderr) + + connection.execute( + "insert into kv values(?, ?)", + ( + key, + val, + ), + ) + + return 0 + + +sys.exit(main()) diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..b75640d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,46 @@ +#include <chrono> +#include <iostream> +#include <print> +#include <string> +#include <vector> + +#include <boost/sqlite.hpp> + +int main(int argc, char **argv) { + if (argc < 2) { + std::println(stderr, "usage: bench <database>"); + return 1; + } + + std::println(stderr, "starting benchmark"); + + boost::sqlite::connection connection{argv[1]}; + + std::vector<std::string> keys; + + std::string line; + while (std::getline(std::cin, line)) { + keys.push_back(line); + } + + std::println(stderr, "slurped {} keys into memory", keys.size()); + + auto start = std::chrono::system_clock::now(); + + auto st = connection.prepare("select kv.value from kv where kv.key=?"); + + for (const auto &key : keys) { + auto row = st.execute({key}); + + auto value = row.current().at(0).get_text(); + } + + auto end = std::chrono::system_clock::now(); + + std::chrono::duration<double> d = end - start; + + std::println(stderr, "selected {} keys in {} ({})", keys.size(), d, + keys.size() / d.count()); + + return 0; +} diff --git a/subprojects/.wraplock b/subprojects/.wraplock new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/subprojects/.wraplock diff --git a/subprojects/boost-sqlite b/subprojects/boost-sqlite new file mode 160000 +Subproject 3378e353705271e569cf4ba15c467b840a39798 |