Skip to content

Commit

Permalink
Refactor commit encoding / decoding (#495)
Browse files Browse the repository at this point in the history
* core: Refactor commit encoding / decoding

Use `sats::{BufReader, BufWriter}` for decoding / encoding of `Commit`
and associated types. This makes `decode` fallible (which is quite
desirable, instead of panicking).

As the `DecodeError` from sats is fairly sparse, also add some context
about where exactly decoding failed.

Lastly, add some documentation and (property) tests.
  • Loading branch information
kim authored Nov 6, 2023
1 parent 82fe0f7 commit 9a263b0
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 150 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ postgres-types = "0.2.5"
proc-macro2 = "1.0"
prometheus = "0.13.0"
proptest = "1.2.0"
proptest-derive = "0.4.0"
prost = "0.10"
prost-build = { version = "0.10" }
quick-junit = { version = "0.3.2" }
Expand Down
2 changes: 2 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ default = ["odb_sled"]
[dev-dependencies]
rusqlite.workspace = true
criterion.workspace = true
proptest.workspace = true
proptest-derive.workspace = true
rand.workspace = true

[build-dependencies]
Expand Down
9 changes: 3 additions & 6 deletions crates/core/src/db/commit_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl CommitLog {
}
}

let mut bytes = Vec::new();
let mut bytes = Vec::with_capacity(unwritten_commit.encoded_len());
unwritten_commit.encode(&mut bytes);

unwritten_commit.parent_commit_hash = Some(hash_bytes(&bytes));
Expand Down Expand Up @@ -279,11 +279,8 @@ impl Iterator for IterSegment {

fn next(&mut self) -> Option<Self::Item> {
let next = self.inner.next()?;
Some(next.map(|bytes| {
// It seems very improbable that `decode` is infallible...
let (commit, _) = Commit::decode(bytes);
commit
}))
let io = |e| io::Error::new(io::ErrorKind::InvalidData, e);
Some(next.and_then(|bytes| Commit::decode(&mut bytes.as_slice()).map_err(io)))
}
}

Expand Down
207 changes: 136 additions & 71 deletions crates/core/src/db/messages/commit.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,119 @@
use anyhow::{bail, Context as _};
use spacetimedb_sats::buffer::{BufReader, BufWriter};
use std::{fmt, sync::Arc};

use super::transaction::Transaction;
use crate::hash::Hash;
use std::sync::Arc;

// aka "Block" from blockchain, aka RecordBatch, aka TxBatch
#[derive(Debug)]
#[cfg(test)]
use proptest::prelude::*;
#[cfg(test)]
use proptest_derive::Arbitrary;

/// A commit is one record in the write-ahead log.
///
/// Encoding:
///
/// ```text
/// [0u8 | 1u8<hash(32)>]<commit_offset(8)><min_tx_offset<8>[<transaction>...]
/// ```
#[derive(Debug, Default, PartialEq)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct Commit {
/// The [`Hash`] over the encoded bytes of the previous commit, or `None` if
/// it is the very first commit.
#[cfg_attr(test, proptest(strategy = "arbitrary::parent_commit_hash()"))]
pub parent_commit_hash: Option<Hash>,
/// Counter of all commits in a log.
pub commit_offset: u64,
/// Counter of all transactions in a log.
///
/// That is, a per-log value which is incremented by `transactions.len()`
/// when the [`Commit`] is constructed.
pub min_tx_offset: u64,
/// The [`Transaction`]s in this commit, usually only one.
#[cfg_attr(test, proptest(strategy = "arbitrary::transactions()"))]
pub transactions: Vec<Arc<Transaction>>,
}

// TODO: Maybe a transaction buffer hash?
// commit: <parent_commit_hash(32)><commit_offset(8)><min_tx_offset(8)>[<transaction>...]*
impl Commit {
pub fn decode(bytes: impl AsRef<[u8]>) -> (Self, usize) {
let bytes = &mut bytes.as_ref();
if bytes.is_empty() {
return (
Commit {
parent_commit_hash: None,
commit_offset: 0,
min_tx_offset: 0,
transactions: Vec::new(),
},
0,
);
#[cfg(test)]
mod arbitrary {
use super::*;

// [`Hash`] is defined in `lib`, so we can't have an [`Arbitrary`] impl for
// it just yet due to orphan rules.
pub fn parent_commit_hash() -> impl Strategy<Value = Option<Hash>> {
any::<Option<[u8; 32]>>().prop_map(|maybe_hash| maybe_hash.map(|data| Hash { data }))
}

// Custom strategy to apply an upper bound on the number of [`Transaction`]s
// generated.
//
// We only ever commit a single transaction in practice.
pub fn transactions() -> impl Strategy<Value = Vec<Arc<Transaction>>> {
prop::collection::vec(any::<Arc<Transaction>>(), 1..8)
}
}

/// Error context for [`Commit::decode`]
enum Context {
Parent,
Hash,
CommitOffset,
MinTxOffset,
Transaction(usize),
}

impl fmt::Display for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Failed to decode `Commit`: ")?;
match self {
Self::Parent => f.write_str("parent commit hash tag"),
Self::Hash => f.write_str("parent commit hash"),
Self::CommitOffset => f.write_str("commit offset"),
Self::MinTxOffset => f.write_str("min transaction offset"),
Self::Transaction(n) => f.write_str(&format!("transaction {n}")),
}
}
}

let mut read_count = 0;
impl Commit {
pub fn decode<'a>(reader: &mut impl BufReader<'a>) -> anyhow::Result<Self> {
if reader.remaining() == 0 {
return Ok(Self::default());
}

let parent_commit_hash = if bytes[read_count] != 0 {
read_count += 1;
let parent_commit_hash = Hash::from_slice(&bytes[read_count..read_count + 32]);
read_count += 32;
Some(parent_commit_hash)
} else {
read_count += 1;
None
let parent_commit_hash = match reader.get_u8().context(Context::Parent)? {
0 => None,
1 => reader
.get_array()
.map(|data| Hash { data })
.map(Some)
.context(Context::Hash)?,
x => bail!("Invalid tag for `Option<Hash>`: {x}"),
};

let mut dst = [0u8; 8];
dst.copy_from_slice(&bytes[read_count..read_count + 8]);
let commit_offset = u64::from_le_bytes(dst);
read_count += 8;

let mut dst = [0u8; 8];
dst.copy_from_slice(&bytes[read_count..read_count + 8]);
let min_tx_offset = u64::from_le_bytes(dst);
read_count += 8;

let mut transactions: Vec<Arc<Transaction>> = Vec::new();
while read_count < bytes.len() {
let (tx, read) = Transaction::decode(&bytes[read_count..]);
read_count += read;
transactions.push(Arc::new(tx));
let commit_offset = reader.get_u64().context(Context::CommitOffset)?;
let min_tx_offset = reader.get_u64().context(Context::MinTxOffset)?;
let mut transactions = Vec::new();
while reader.remaining() > 0 {
let tx = Transaction::decode(reader)
.map(Arc::new)
.with_context(|| Context::Transaction(transactions.len() + 1))?;
transactions.push(tx);
}

(
Commit {
parent_commit_hash,
commit_offset,
min_tx_offset,
transactions,
},
read_count,
)
Ok(Self {
parent_commit_hash,
commit_offset,
min_tx_offset,
transactions,
})
}

pub fn encoded_len(&self) -> usize {
let mut count = 0;

if self.parent_commit_hash.is_none() {
count += 1;
} else {
count += 1;
count += self.parent_commit_hash.unwrap().data.len();
let mut count = 1; // tag for option
if let Some(hash) = self.parent_commit_hash {
count += hash.data.len();
}

// 8 for commit_offset
Expand All @@ -91,21 +129,48 @@ impl Commit {
count
}

pub fn encode(&self, bytes: &mut Vec<u8>) {
bytes.reserve(self.encoded_len());

if self.parent_commit_hash.is_none() {
bytes.push(0);
} else {
bytes.push(1);
bytes.extend(self.parent_commit_hash.unwrap().data);
pub fn encode(&self, writer: &mut impl BufWriter) {
match self.parent_commit_hash {
Some(hash) => {
writer.put_u8(1);
writer.put_slice(&hash.data);
}
None => writer.put_u8(0),
}
writer.put_u64(self.commit_offset);
writer.put_u64(self.min_tx_offset);
for tx in &self.transactions {
tx.encode(writer);
}
}
}

bytes.extend(self.commit_offset.to_le_bytes());
bytes.extend(self.min_tx_offset.to_le_bytes());
#[cfg(test)]
mod tests {
use super::*;

proptest! {
// Generating arbitrary commits is quite slow, so limit to just a few
// cases.
//
// Note that this config applies to all `#[test]`s within the enclosing
// `proptest!`.
#![proptest_config(ProptestConfig::with_cases(64))]


#[test]
fn prop_commit_encoding_roundtrip(commit in any::<Commit>()) {
let mut buf = Vec::new();
commit.encode(&mut buf);
let decoded = Commit::decode(&mut buf.as_slice()).unwrap();
prop_assert_eq!(commit, decoded)
}

for tx in &self.transactions {
tx.encode(bytes);
#[test]
fn prop_encoded_len_is_encoded_len(commit in any::<Commit>()) {
let mut buf = Vec::new();
commit.encode(&mut buf);
prop_assert_eq!(buf.len(), commit.encoded_len())
}
}
}
Loading

1 comment on commit 9a263b0

@github-actions
Copy link

@github-actions github-actions bot commented on 9a263b0 Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark results

Benchmark Report

Legend:

  • load: number of rows pre-loaded into the database
  • count: number of rows touched by the transaction
  • index types:
    • unique: a single index on the id column
    • non_unique: no indexes
    • multi_index: non-unique index on every column
  • schemas:
    • person(id: u32, name: String, age: u64)
    • location(id: u32, x: u64, y: u64)

All throughputs are single-threaded.

Empty transaction

db on disk new latency old latency new throughput old throughput
sqlite 💿 429.2±1.77ns 435.1±2.35ns - -
sqlite 🧠 422.1±1.31ns 433.5±1.42ns - -
stdb_module 💿 18.1±1.40µs 17.4±0.76µs - -
stdb_module 🧠 18.4±1.26µs 18.4±1.21µs - -
stdb_raw 💿 721.1±1.05ns 721.5±1.54ns - -
stdb_raw 🧠 714.2±1.25ns 717.4±3.64ns - -

Single-row insertions

db on disk schema index type load new latency old latency new throughput old throughput
sqlite 💿 location multi_index 0 15.0±1.75µs 14.5±0.05µs 65.0 Ktx/sec 67.1 Ktx/sec
sqlite 💿 location multi_index 1000 16.0±0.12µs 15.7±0.13µs 61.2 Ktx/sec 62.1 Ktx/sec
sqlite 💿 location non_unique 0 7.4±2.34µs 7.1±0.04µs 131.1 Ktx/sec 137.3 Ktx/sec
sqlite 💿 location non_unique 1000 7.1±0.04µs 7.1±0.03µs 138.2 Ktx/sec 138.2 Ktx/sec
sqlite 💿 location unique 0 7.3±0.88µs 7.1±0.02µs 134.2 Ktx/sec 138.1 Ktx/sec
sqlite 💿 location unique 1000 7.1±0.04µs 7.1±0.13µs 137.8 Ktx/sec 138.1 Ktx/sec
sqlite 💿 person multi_index 0 14.5±0.03µs 14.2±0.04µs 67.5 Ktx/sec 68.7 Ktx/sec
sqlite 💿 person multi_index 1000 16.1±0.11µs 16.1±0.15µs 60.5 Ktx/sec 60.5 Ktx/sec
sqlite 💿 person non_unique 0 7.3±0.31µs 7.2±0.03µs 133.5 Ktx/sec 135.9 Ktx/sec
sqlite 💿 person non_unique 1000 7.3±0.04µs 7.2±0.04µs 134.1 Ktx/sec 135.0 Ktx/sec
sqlite 💿 person unique 0 7.3±0.27µs 7.3±0.48µs 133.5 Ktx/sec 134.1 Ktx/sec
sqlite 💿 person unique 1000 7.3±0.04µs 7.2±0.05µs 133.2 Ktx/sec 135.0 Ktx/sec
sqlite 🧠 location multi_index 0 4.0±0.01µs 4.0±0.01µs 245.5 Ktx/sec 247.0 Ktx/sec
sqlite 🧠 location multi_index 1000 5.2±0.05µs 5.2±0.05µs 187.3 Ktx/sec 189.5 Ktx/sec
sqlite 🧠 location non_unique 0 1869.4±5.32ns 1827.6±6.58ns 522.4 Ktx/sec 534.3 Ktx/sec
sqlite 🧠 location non_unique 1000 1903.5±9.40ns 1853.9±10.98ns 513.0 Ktx/sec 526.8 Ktx/sec
sqlite 🧠 location unique 0 1835.2±5.18ns 1803.2±6.80ns 532.1 Ktx/sec 541.6 Ktx/sec
sqlite 🧠 location unique 1000 1933.9±12.95ns 1913.5±14.41ns 505.0 Ktx/sec 510.4 Ktx/sec
sqlite 🧠 person multi_index 0 3.6±0.01µs 3.7±0.01µs 269.4 Ktx/sec 266.9 Ktx/sec
sqlite 🧠 person multi_index 1000 5.4±0.04µs 5.5±0.04µs 180.5 Ktx/sec 177.0 Ktx/sec
sqlite 🧠 person non_unique 0 1933.6±6.35ns 1919.6±5.95ns 505.1 Ktx/sec 508.7 Ktx/sec
sqlite 🧠 person non_unique 1000 1997.7±9.39ns 1961.3±26.58ns 488.9 Ktx/sec 497.9 Ktx/sec
sqlite 🧠 person unique 0 1930.1±5.63ns 1912.0±7.19ns 506.0 Ktx/sec 510.8 Ktx/sec
sqlite 🧠 person unique 1000 2.0±0.01µs 2.0±0.02µs 477.1 Ktx/sec 484.7 Ktx/sec
stdb_module 💿 location multi_index 0 52.6±5.07µs 50.7±4.74µs 18.6 Ktx/sec 19.3 Ktx/sec
stdb_module 💿 location multi_index 1000 177.3±105.72µs 161.3±12.03µs 5.5 Ktx/sec 6.1 Ktx/sec
stdb_module 💿 location non_unique 0 45.2±4.10µs 44.5±4.90µs 21.6 Ktx/sec 21.9 Ktx/sec
stdb_module 💿 location non_unique 1000 118.9±28.34µs 265.3±36.87µs 8.2 Ktx/sec 3.7 Ktx/sec
stdb_module 💿 location unique 0 51.3±6.27µs 49.9±6.31µs 19.0 Ktx/sec 19.6 Ktx/sec
stdb_module 💿 location unique 1000 136.8±9.92µs 117.2±20.61µs 7.1 Ktx/sec 8.3 Ktx/sec
stdb_module 💿 person multi_index 0 64.8±6.74µs 63.3±6.00µs 15.1 Ktx/sec 15.4 Ktx/sec
stdb_module 💿 person multi_index 1000 178.2±14.55µs 160.6±15.60µs 5.5 Ktx/sec 6.1 Ktx/sec
stdb_module 💿 person non_unique 0 47.1±4.98µs 46.3±6.15µs 20.7 Ktx/sec 21.1 Ktx/sec
stdb_module 💿 person non_unique 1000 182.0±14.30µs 271.6±110.56µs 5.4 Ktx/sec 3.6 Ktx/sec
stdb_module 💿 person unique 0 59.0±5.76µs 56.4±6.08µs 16.5 Ktx/sec 17.3 Ktx/sec
stdb_module 💿 person unique 1000 274.5±59.07µs 142.8±86.16µs 3.6 Ktx/sec 6.8 Ktx/sec
stdb_module 🧠 location multi_index 0 39.0±3.34µs 34.5±3.01µs 25.0 Ktx/sec 28.3 Ktx/sec
stdb_module 🧠 location multi_index 1000 158.3±21.67µs 184.4±9.94µs 6.2 Ktx/sec 5.3 Ktx/sec
stdb_module 🧠 location non_unique 0 30.3±2.16µs 30.1±2.44µs 32.2 Ktx/sec 32.5 Ktx/sec
stdb_module 🧠 location non_unique 1000 162.9±4.16µs 189.0±9.18µs 6.0 Ktx/sec 5.2 Ktx/sec
stdb_module 🧠 location unique 0 34.4±2.49µs 33.9±3.20µs 28.4 Ktx/sec 28.8 Ktx/sec
stdb_module 🧠 location unique 1000 193.1±8.16µs 175.6±11.33µs 5.1 Ktx/sec 5.6 Ktx/sec
stdb_module 🧠 person multi_index 0 49.0±4.46µs 47.0±5.14µs 19.9 Ktx/sec 20.8 Ktx/sec
stdb_module 🧠 person multi_index 1000 306.4±11.10µs 225.2±27.84µs 3.2 Ktx/sec 4.3 Ktx/sec
stdb_module 🧠 person non_unique 0 32.3±2.73µs 32.2±2.29µs 30.2 Ktx/sec 30.3 Ktx/sec
stdb_module 🧠 person non_unique 1000 252.5±4.96µs 117.3±10.85µs 3.9 Ktx/sec 8.3 Ktx/sec
stdb_module 🧠 person unique 0 38.7±3.19µs 36.7±2.74µs 25.3 Ktx/sec 26.6 Ktx/sec
stdb_module 🧠 person unique 1000 202.1±2.22µs 193.0±35.62µs 4.8 Ktx/sec 5.1 Ktx/sec
stdb_raw 💿 location multi_index 0 7.1±0.02µs 7.1±0.01µs 137.3 Ktx/sec 137.7 Ktx/sec
stdb_raw 💿 location multi_index 1000 33.2±233.19µs 47.9±381.22µs 29.4 Ktx/sec 20.4 Ktx/sec
stdb_raw 💿 location non_unique 0 4.7±0.02µs 4.7±0.01µs 208.3 Ktx/sec 206.9 Ktx/sec
stdb_raw 💿 location non_unique 1000 6.1±0.19µs 6.3±0.42µs 160.2 Ktx/sec 155.7 Ktx/sec
stdb_raw 💿 location unique 0 6.4±0.18µs 6.1±0.07µs 151.6 Ktx/sec 160.9 Ktx/sec
stdb_raw 💿 location unique 1000 24.8±163.99µs 24.9±165.34µs 39.3 Ktx/sec 39.2 Ktx/sec
stdb_raw 💿 person multi_index 0 10.8±0.03µs 10.8±0.02µs 90.5 Ktx/sec 90.6 Ktx/sec
stdb_raw 💿 person multi_index 1000 85.6±501.60µs 42.1±281.81µs 11.4 Ktx/sec 23.2 Ktx/sec
stdb_raw 💿 person non_unique 0 5.3±0.04µs 5.3±0.01µs 185.8 Ktx/sec 184.9 Ktx/sec
stdb_raw 💿 person non_unique 1000 7.0±0.21µs 17.2±100.66µs 140.1 Ktx/sec 56.8 Ktx/sec
stdb_raw 💿 person unique 0 7.7±0.03µs 7.7±0.02µs 126.2 Ktx/sec 127.5 Ktx/sec
stdb_raw 💿 person unique 1000 33.9±235.51µs 19.1±88.70µs 28.8 Ktx/sec 51.1 Ktx/sec
stdb_raw 🧠 location multi_index 0 4.2±0.01µs 4.1±0.01µs 230.1 Ktx/sec 235.5 Ktx/sec
stdb_raw 🧠 location multi_index 1000 5.8±0.05µs 5.8±0.04µs 167.9 Ktx/sec 169.5 Ktx/sec
stdb_raw 🧠 location non_unique 0 1943.0±5.89ns 1916.6±5.23ns 502.6 Ktx/sec 509.5 Ktx/sec
stdb_raw 🧠 location non_unique 1000 2.4±0.02µs 2.5±0.03µs 402.1 Ktx/sec 397.4 Ktx/sec
stdb_raw 🧠 location unique 0 3.2±0.01µs 3.2±0.01µs 301.3 Ktx/sec 307.2 Ktx/sec
stdb_raw 🧠 location unique 1000 4.5±0.04µs 4.5±0.03µs 217.3 Ktx/sec 216.8 Ktx/sec
stdb_raw 🧠 person multi_index 0 7.9±0.01µs 7.8±0.02µs 123.8 Ktx/sec 124.9 Ktx/sec
stdb_raw 🧠 person multi_index 1000 10.0±0.09µs 9.8±0.08µs 97.8 Ktx/sec 100.1 Ktx/sec
stdb_raw 🧠 person non_unique 0 2.5±0.01µs 2.4±0.01µs 392.9 Ktx/sec 398.9 Ktx/sec
stdb_raw 🧠 person non_unique 1000 3.2±0.02µs 3.2±0.03µs 304.1 Ktx/sec 304.7 Ktx/sec
stdb_raw 🧠 person unique 0 4.9±0.01µs 4.7±0.01µs 201.0 Ktx/sec 205.8 Ktx/sec
stdb_raw 🧠 person unique 1000 6.4±0.04µs 6.2±0.07µs 152.3 Ktx/sec 156.3 Ktx/sec

Multi-row insertions

db on disk schema index type load count new latency old latency new throughput old throughput
sqlite 💿 location multi_index 0 100 131.3±2.43µs 130.1±9.36µs 7.4 Ktx/sec 7.5 Ktx/sec
sqlite 💿 location multi_index 1000 100 202.6±1.12µs 202.1±1.78µs 4.8 Ktx/sec 4.8 Ktx/sec
sqlite 💿 location non_unique 0 100 50.3±1.23µs 49.2±1.68µs 19.4 Ktx/sec 19.9 Ktx/sec
sqlite 💿 location non_unique 1000 100 53.9±0.58µs 52.7±0.20µs 18.1 Ktx/sec 18.5 Ktx/sec
sqlite 💿 location unique 0 100 52.3±0.43µs 52.2±1.51µs 18.7 Ktx/sec 18.7 Ktx/sec
sqlite 💿 location unique 1000 100 57.9±10.34µs 56.5±0.46µs 16.9 Ktx/sec 17.3 Ktx/sec
sqlite 💿 person multi_index 0 100 118.6±8.47µs 119.2±3.55µs 8.2 Ktx/sec 8.2 Ktx/sec
sqlite 💿 person multi_index 1000 100 231.8±0.56µs 240.6±30.79µs 4.2 Ktx/sec 4.1 Ktx/sec
sqlite 💿 person non_unique 0 100 48.8±1.70µs 48.4±1.17µs 20.0 Ktx/sec 20.2 Ktx/sec
sqlite 💿 person non_unique 1000 100 60.7±0.26µs 59.4±0.50µs 16.1 Ktx/sec 16.4 Ktx/sec
sqlite 💿 person unique 0 100 51.4±1.75µs 51.0±0.93µs 19.0 Ktx/sec 19.2 Ktx/sec
sqlite 💿 person unique 1000 100 57.0±0.22µs 55.7±0.27µs 17.1 Ktx/sec 17.5 Ktx/sec
sqlite 🧠 location multi_index 0 100 118.7±0.37µs 118.6±0.70µs 8.2 Ktx/sec 8.2 Ktx/sec
sqlite 🧠 location multi_index 1000 100 170.5±0.51µs 170.6±0.47µs 5.7 Ktx/sec 5.7 Ktx/sec
sqlite 🧠 location non_unique 0 100 43.8±0.25µs 43.1±0.64µs 22.3 Ktx/sec 22.7 Ktx/sec
sqlite 🧠 location non_unique 1000 100 44.6±0.40µs 43.3±0.40µs 21.9 Ktx/sec 22.6 Ktx/sec
sqlite 🧠 location unique 0 100 44.7±0.24µs 44.6±0.29µs 21.9 Ktx/sec 21.9 Ktx/sec
sqlite 🧠 location unique 1000 100 48.4±0.34µs 48.3±0.41µs 20.2 Ktx/sec 20.2 Ktx/sec
sqlite 🧠 person multi_index 0 100 106.1±0.31µs 107.2±0.41µs 9.2 Ktx/sec 9.1 Ktx/sec
sqlite 🧠 person multi_index 1000 100 188.3±0.42µs 190.7±0.33µs 5.2 Ktx/sec 5.1 Ktx/sec
sqlite 🧠 person non_unique 0 100 41.9±0.31µs 42.2±0.26µs 23.3 Ktx/sec 23.2 Ktx/sec
sqlite 🧠 person non_unique 1000 100 45.9±0.40µs 44.9±0.28µs 21.3 Ktx/sec 21.8 Ktx/sec
sqlite 🧠 person unique 0 100 43.9±0.33µs 44.5±0.34µs 22.2 Ktx/sec 21.9 Ktx/sec
sqlite 🧠 person unique 1000 100 48.2±0.37µs 48.3±0.23µs 20.3 Ktx/sec 20.2 Ktx/sec
stdb_module 💿 location multi_index 0 100 710.7±74.48µs 945.1±13.44µs 1407 tx/sec 1058 tx/sec
stdb_module 💿 location multi_index 1000 100 830.9±162.16µs 787.3±34.92µs 1203 tx/sec 1270 tx/sec
stdb_module 💿 location non_unique 0 100 436.0±49.72µs 362.9±0.60µs 2.2 Ktx/sec 2.7 Ktx/sec
stdb_module 💿 location non_unique 1000 100 583.9±54.53µs 575.7±73.56µs 1712 tx/sec 1737 tx/sec
stdb_module 💿 location unique 0 100 667.8±137.14µs 539.5±38.60µs 1497 tx/sec 1853 tx/sec
stdb_module 💿 location unique 1000 100 670.8±63.93µs 625.7±87.67µs 1490 tx/sec 1598 tx/sec
stdb_module 💿 person multi_index 0 100 996.7±7.06µs 951.4±2.97µs 1003 tx/sec 1051 tx/sec
stdb_module 💿 person multi_index 1000 100 1144.8±39.87µs 1044.0±42.91µs 873 tx/sec 957 tx/sec
stdb_module 💿 person non_unique 0 100 553.3±118.21µs 664.9±66.60µs 1807 tx/sec 1503 tx/sec
stdb_module 💿 person non_unique 1000 100 781.1±56.48µs 664.7±210.67µs 1280 tx/sec 1504 tx/sec
stdb_module 💿 person unique 0 100 766.4±4.77µs 701.8±61.43µs 1304 tx/sec 1424 tx/sec
stdb_module 💿 person unique 1000 100 891.0±80.08µs 738.4±51.79µs 1122 tx/sec 1354 tx/sec
stdb_module 🧠 location multi_index 0 100 803.5±4.13µs 536.2±123.02µs 1244 tx/sec 1865 tx/sec
stdb_module 🧠 location multi_index 1000 100 675.0±148.13µs 798.2±12.57µs 1481 tx/sec 1252 tx/sec
stdb_module 🧠 location non_unique 0 100 354.1±57.13µs 387.0±40.88µs 2.8 Ktx/sec 2.5 Ktx/sec
stdb_module 🧠 location non_unique 1000 100 440.3±30.93µs 380.1±26.63µs 2.2 Ktx/sec 2.6 Ktx/sec
stdb_module 🧠 location unique 0 100 438.3±6.63µs 368.6±22.58µs 2.2 Ktx/sec 2.6 Ktx/sec
stdb_module 🧠 location unique 1000 100 506.2±8.02µs 705.3±27.02µs 1975 tx/sec 1417 tx/sec
stdb_module 🧠 person multi_index 0 100 813.6±10.78µs 829.0±52.64µs 1229 tx/sec 1206 tx/sec
stdb_module 🧠 person multi_index 1000 100 900.4±37.30µs 885.6±12.73µs 1110 tx/sec 1129 tx/sec
stdb_module 🧠 person non_unique 0 100 404.4±12.72µs 471.3±92.38µs 2.4 Ktx/sec 2.1 Ktx/sec
stdb_module 🧠 person non_unique 1000 100 476.4±141.52µs 342.1±3.47µs 2.0 Ktx/sec 2.9 Ktx/sec
stdb_module 🧠 person unique 0 100 598.8±1.12µs 531.7±11.63µs 1669 tx/sec 1880 tx/sec
stdb_module 🧠 person unique 1000 100 802.1±62.14µs 583.2±23.09µs 1246 tx/sec 1714 tx/sec
stdb_raw 💿 location multi_index 0 100 388.1±25.26µs 385.8±0.59µs 2.5 Ktx/sec 2.5 Ktx/sec
stdb_raw 💿 location multi_index 1000 100 432.2±232.75µs 412.2±1.20µs 2.3 Ktx/sec 2.4 Ktx/sec
stdb_raw 💿 location non_unique 0 100 157.7±6.42µs 159.7±0.29µs 6.2 Ktx/sec 6.1 Ktx/sec
stdb_raw 💿 location non_unique 1000 100 159.2±0.95µs 162.4±1.01µs 6.1 Ktx/sec 6.0 Ktx/sec
stdb_raw 💿 location unique 0 100 286.6±1.00µs 285.7±0.30µs 3.4 Ktx/sec 3.4 Ktx/sec
stdb_raw 💿 location unique 1000 100 320.0±140.40µs 304.1±1.25µs 3.1 Ktx/sec 3.2 Ktx/sec
stdb_raw 💿 person multi_index 0 100 717.1±13.24µs 707.1±1.58µs 1394 tx/sec 1414 tx/sec
stdb_raw 💿 person multi_index 1000 100 743.1±2.48µs 767.6±320.92µs 1345 tx/sec 1302 tx/sec
stdb_raw 💿 person non_unique 0 100 218.5±38.46µs 216.6±9.80µs 4.5 Ktx/sec 4.5 Ktx/sec
stdb_raw 💿 person non_unique 1000 100 218.0±1.19µs 228.6±105.91µs 4.5 Ktx/sec 4.3 Ktx/sec
stdb_raw 💿 person unique 0 100 433.3±1.68µs 428.7±1.21µs 2.3 Ktx/sec 2.3 Ktx/sec
stdb_raw 💿 person unique 1000 100 465.0±151.38µs 445.2±0.86µs 2.1 Ktx/sec 2.2 Ktx/sec
stdb_raw 🧠 location multi_index 0 100 302.8±0.63µs 297.8±0.66µs 3.2 Ktx/sec 3.3 Ktx/sec
stdb_raw 🧠 location multi_index 1000 100 325.4±0.54µs 324.0±0.53µs 3.0 Ktx/sec 3.0 Ktx/sec
stdb_raw 🧠 location non_unique 0 100 74.8±0.73µs 75.7±0.10µs 13.1 Ktx/sec 12.9 Ktx/sec
stdb_raw 🧠 location non_unique 1000 100 76.6±0.23µs 77.1±0.11µs 12.8 Ktx/sec 12.7 Ktx/sec
stdb_raw 🧠 location unique 0 100 204.1±0.38µs 201.4±0.34µs 4.8 Ktx/sec 4.8 Ktx/sec
stdb_raw 🧠 location unique 1000 100 222.6±0.31µs 219.0±0.34µs 4.4 Ktx/sec 4.5 Ktx/sec
stdb_raw 🧠 person multi_index 0 100 625.8±0.36µs 616.4±1.02µs 1597 tx/sec 1622 tx/sec
stdb_raw 🧠 person multi_index 1000 100 654.0±0.51µs 645.7±0.59µs 1529 tx/sec 1548 tx/sec
stdb_raw 🧠 person non_unique 0 100 127.2±0.13µs 125.9±0.16µs 7.7 Ktx/sec 7.8 Ktx/sec
stdb_raw 🧠 person non_unique 1000 100 129.9±0.23µs 128.7±0.40µs 7.5 Ktx/sec 7.6 Ktx/sec
stdb_raw 🧠 person unique 0 100 343.1±0.39µs 338.2±0.47µs 2.8 Ktx/sec 2.9 Ktx/sec
stdb_raw 🧠 person unique 1000 100 362.2±0.86µs 356.3±0.53µs 2.7 Ktx/sec 2.7 Ktx/sec

Full table iterate

db on disk schema index type new latency old latency new throughput old throughput
sqlite 💿 location unique 9.0±0.07µs 8.8±0.09µs 108.9 Ktx/sec 111.4 Ktx/sec
sqlite 💿 person unique 9.5±0.14µs 9.4±0.11µs 103.1 Ktx/sec 104.2 Ktx/sec
sqlite 🧠 location unique 7.7±0.10µs 7.6±0.08µs 126.5 Ktx/sec 129.2 Ktx/sec
sqlite 🧠 person unique 8.2±0.10µs 8.2±0.10µs 119.0 Ktx/sec 119.6 Ktx/sec
stdb_module 💿 location unique 45.1±5.58µs 50.5±4.03µs 21.7 Ktx/sec 19.3 Ktx/sec
stdb_module 💿 person unique 60.0±9.53µs 57.3±8.81µs 16.3 Ktx/sec 17.0 Ktx/sec
stdb_module 🧠 location unique 47.4±4.87µs 50.2±4.20µs 20.6 Ktx/sec 19.4 Ktx/sec
stdb_module 🧠 person unique 51.2±9.53µs 63.3±5.83µs 19.1 Ktx/sec 15.4 Ktx/sec
stdb_raw 💿 location unique 9.0±0.02µs 9.1±0.34µs 108.0 Ktx/sec 107.5 Ktx/sec
stdb_raw 💿 person unique 9.0±0.01µs 9.1±0.23µs 108.1 Ktx/sec 107.9 Ktx/sec
stdb_raw 🧠 location unique 9.0±0.01µs 9.0±0.02µs 108.2 Ktx/sec 108.1 Ktx/sec
stdb_raw 🧠 person unique 9.0±0.02µs 9.0±0.01µs 108.2 Ktx/sec 108.2 Ktx/sec

Find unique key

db on disk key type load new latency old latency new throughput old throughput
sqlite 💿 u32 1000 2.4±0.01µs 2.3±0.01µs 413.7 Ktx/sec 422.7 Ktx/sec
sqlite 🧠 u32 1000 1145.4±4.16ns 1097.8±5.09ns 852.6 Ktx/sec 889.6 Ktx/sec
stdb_module 💿 u32 1000 25.2±2.66µs 24.4±1.84µs 38.8 Ktx/sec 39.9 Ktx/sec
stdb_module 🧠 u32 1000 25.1±1.74µs 23.6±1.52µs 38.9 Ktx/sec 41.5 Ktx/sec
stdb_raw 💿 u32 1000 1937.7±19.77ns 1920.2±5.56ns 504.0 Ktx/sec 508.6 Ktx/sec
stdb_raw 🧠 u32 1000 1920.5±6.03ns 1904.6±2.81ns 508.5 Ktx/sec 512.7 Ktx/sec

Filter

db on disk key type index strategy load count new latency old latency new throughput old throughput
sqlite 💿 string indexed 1000 10 5.8±0.02µs 5.6±0.02µs 169.5 Ktx/sec 173.8 Ktx/sec
sqlite 💿 string non_indexed 1000 10 52.0±0.46µs 47.9±0.31µs 18.8 Ktx/sec 20.4 Ktx/sec
sqlite 💿 u64 indexed 1000 10 5.5±0.02µs 5.4±0.01µs 178.4 Ktx/sec 181.4 Ktx/sec
sqlite 💿 u64 non_indexed 1000 10 32.9±0.08µs 32.8±0.09µs 29.7 Ktx/sec 29.8 Ktx/sec
sqlite 🧠 string indexed 1000 10 4.3±0.02µs 4.2±0.01µs 227.5 Ktx/sec 234.1 Ktx/sec
sqlite 🧠 string non_indexed 1000 10 49.9±0.71µs 47.2±0.32µs 19.6 Ktx/sec 20.7 Ktx/sec
sqlite 🧠 u64 indexed 1000 10 4.1±0.01µs 4.0±0.01µs 239.1 Ktx/sec 244.7 Ktx/sec
sqlite 🧠 u64 non_indexed 1000 10 31.5±0.09µs 31.5±0.11µs 31.0 Ktx/sec 31.0 Ktx/sec
stdb_module 💿 string indexed 1000 10 36.5±2.14µs 35.6±2.81µs 26.7 Ktx/sec 27.5 Ktx/sec
stdb_module 💿 string non_indexed 1000 10 176.1±6.86µs 171.4±2.10µs 5.5 Ktx/sec 5.7 Ktx/sec
stdb_module 💿 u64 indexed 1000 10 34.0±2.22µs 31.2±2.90µs 28.7 Ktx/sec 31.3 Ktx/sec
stdb_module 💿 u64 non_indexed 1000 10 138.5±1.27µs 141.3±2.67µs 7.1 Ktx/sec 6.9 Ktx/sec
stdb_module 🧠 string indexed 1000 10 36.3±2.67µs 35.1±2.37µs 26.9 Ktx/sec 27.8 Ktx/sec
stdb_module 🧠 string non_indexed 1000 10 169.5±6.74µs 165.6±1.55µs 5.8 Ktx/sec 5.9 Ktx/sec
stdb_module 🧠 u64 indexed 1000 10 33.4±2.42µs 32.4±2.68µs 29.2 Ktx/sec 30.2 Ktx/sec
stdb_module 🧠 u64 non_indexed 1000 10 136.0±9.45µs 137.5±5.00µs 7.2 Ktx/sec 7.1 Ktx/sec
stdb_raw 💿 string indexed 1000 10 4.5±0.01µs 4.5±0.01µs 217.2 Ktx/sec 216.0 Ktx/sec
stdb_raw 💿 string non_indexed 1000 10 138.0±0.37µs 144.9±0.32µs 7.1 Ktx/sec 6.7 Ktx/sec
stdb_raw 💿 u64 indexed 1000 10 4.4±0.01µs 4.4±0.01µs 222.1 Ktx/sec 222.2 Ktx/sec
stdb_raw 💿 u64 non_indexed 1000 10 114.5±0.12µs 125.2±0.18µs 8.5 Ktx/sec 7.8 Ktx/sec
stdb_raw 🧠 string indexed 1000 10 4.5±0.01µs 4.5±0.02µs 217.4 Ktx/sec 217.6 Ktx/sec
stdb_raw 🧠 string non_indexed 1000 10 136.6±1.60µs 143.8±0.40µs 7.1 Ktx/sec 6.8 Ktx/sec
stdb_raw 🧠 u64 indexed 1000 10 4.4±0.01µs 4.4±0.02µs 223.0 Ktx/sec 222.9 Ktx/sec
stdb_raw 🧠 u64 non_indexed 1000 10 114.2±0.12µs 124.7±0.34µs 8.5 Ktx/sec 7.8 Ktx/sec

Serialize

schema format count new latency old latency new throughput old throughput
location bsatn 100 1866.2±34.26ns 1665.2±31.57ns 51.1 Mtx/sec 57.3 Mtx/sec
location json 100 3.2±0.01µs 3.7±0.04µs 30.3 Mtx/sec 25.5 Mtx/sec
location product_value 100 577.8±0.37ns 577.3±0.24ns 165.1 Mtx/sec 165.2 Mtx/sec
person bsatn 100 2.6±0.03µs 2.9±0.01µs 36.7 Mtx/sec 32.5 Mtx/sec
person json 100 4.8±0.04µs 5.0±0.02µs 20.0 Mtx/sec 19.1 Mtx/sec
person product_value 100 1002.7±0.67ns 1028.9±1.58ns 95.1 Mtx/sec 92.7 Mtx/sec

Module: invoke with large arguments

arg size new latency old latency new throughput old throughput
64KiB 79.1±5.31µs 72.6±8.96µs - -

Module: print bulk

line count new latency old latency new throughput old throughput
1 22.6±1.16µs 22.7±0.95µs - -
100 201.2±3.35µs 201.2±4.83µs - -
1000 1872.5±68.65µs 1915.6±46.44µs - -

Remaining benchmarks

name new latency old latency new throughput old throughput

Please sign in to comment.