Skip to content

Commit

Permalink
offset index read integration (#1779)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shubham8287 authored Oct 7, 2024
1 parent d6bb05b commit c5a9a0d
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 85 deletions.
50 changes: 43 additions & 7 deletions crates/commitlog/src/commitlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ impl<R: Repo, T> Generic<R, T> {

pub fn commits_from(&self, offset: u64) -> Commits<R> {
let offsets = self.segment_offsets_from(offset);
let next_offset = offsets.first().cloned().unwrap_or(offset);
let segments = Segments {
offs: offsets.into_iter(),
repo: self.repo.clone(),
Expand All @@ -178,7 +177,7 @@ impl<R: Repo, T> Generic<R, T> {
Commits {
inner: None,
segments,
last_commit: CommitInfo::Initial { next_offset },
last_commit: CommitInfo::Initial { next_offset: offset },
last_error: None,
}
}
Expand Down Expand Up @@ -468,6 +467,26 @@ impl CommitInfo {
Self::LastSeen { tx_range, .. } => &tx_range.end,
}
}

// If initial offset falls within a commit, adjust it to the commit boundary.
//
// Returns `true` if the initial offset is past `commit`.
// Returns `false` if `self` isn't `Self::Initial`,
// or the initial offset has been adjusted to the starting offset of `commit`.
//
// For iteration, `true` means to skip the commit, `false` to yield it.
fn adjust_initial_offset(&mut self, commit: &StoredCommit) -> bool {
if let Self::Initial { next_offset } = self {
let last_tx_offset = commit.min_tx_offset + commit.n as u64 - 1;
if *next_offset > last_tx_offset {
return true;
} else {
*next_offset = commit.min_tx_offset;
}
}

false
}
}

pub struct Commits<R: Repo> {
Expand Down Expand Up @@ -496,8 +515,11 @@ impl<R: Repo> Commits<R> {
// interesting.
let prev_error = self.last_error.take();

// Skip entries before the initial commit.
if self.last_commit.adjust_initial_offset(&commit) {
self.next()
// Same offset: ignore if duplicate (same crc), else report a "fork".
if self.last_commit.same_offset_as(&commit) {
} else if self.last_commit.same_offset_as(&commit) {
if !self.last_commit.same_checksum_as(&commit) {
warn!(
"forked: commit={:?} last-error={:?} last-crc={:?}",
Expand Down Expand Up @@ -579,7 +601,23 @@ impl<R: Repo> Iterator for Commits<R> {
None => self.last_error.take().map(Err),
Some(segment) => segment.map_or_else(
|e| Some(Err(e.into())),
|segment| {
|mut segment| {
// Try to use offset index to advance segment to Intial commit
if let CommitInfo::Initial { next_offset } = self.last_commit {
let _ = self
.segments
.repo
.get_offset_index(segment.min_tx_offset)
.map_err(Into::into)
.and_then(|index_file| segment.seek_to_offset(&index_file, next_offset))
.inspect_err(|e| {
warn!(
"commitlog offset index is not used: {}, at: segment {}",
e, segment.min_tx_offset
);
});
}

self.inner = Some(segment.commits());
self.next()
},
Expand Down Expand Up @@ -671,9 +709,7 @@ mod tests {
assert!(commit.min_tx_offset >= offset);
}
}
// Nb.: the head commit is always returned,
// because we don't know its offset upper bound
assert_eq!(1, log.commits_from(10).count());
assert_eq!(0, log.commits_from(10).count());
}

#[test]
Expand Down
134 changes: 80 additions & 54 deletions crates/commitlog/src/index/indexfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,54 @@ pub struct IndexFileMut<Key: Into<u64> + From<u64>> {
}

impl<Key: Into<u64> + From<u64>> IndexFileMut<Key> {
pub fn create_index_file(path: &Path, offset: u64, cap: u64) -> io::Result<Self> {
File::options()
.write(true)
.read(true)
.create_new(true)
.open(offset_index_file_path(path, offset))
.and_then(|file| {
file.set_len(cap * ENTRY_SIZE as u64)?;
let mmap = unsafe { MmapMut::map_mut(&file) }?;

Ok(IndexFileMut {
inner: mmap,
num_entries: 0,
_marker: PhantomData,
})
})
.or_else(|e| {
if e.kind() == io::ErrorKind::AlreadyExists {
debug!("Index file {} already exists", path.display());
Self::open_index_file(path, offset, cap)
} else {
Err(e)
}
})
}

pub fn open_index_file(path: &Path, offset: u64, cap: u64) -> io::Result<Self> {
let file = File::options()
.read(true)
.write(true)
.open(offset_index_file_path(path, offset))?;
file.set_len(cap * ENTRY_SIZE as u64)?;
let mmap = unsafe { MmapMut::map_mut(&file)? };

let mut me = IndexFileMut {
inner: mmap,
num_entries: 0,
_marker: PhantomData,
};
me.num_entries = me.num_entries().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

Ok(me)
}

pub fn delete_index_file(path: &Path, offset: u64) -> io::Result<()> {
fs::remove_file(offset_index_file_path(path, offset)).map_err(Into::into)
}

// Searches for first 0-key, to count number of entries
fn num_entries(&self) -> Result<usize, IndexError> {
for index in 0.. {
Expand Down Expand Up @@ -189,60 +237,38 @@ impl<Key: Into<u64> + From<u64>> IndexFileMut<Key> {
}
}

pub fn create_index_file<Key: Into<u64> + From<u64>>(
path: &Path,
offset: u64,
cap: u64,
) -> io::Result<IndexFileMut<Key>> {
File::options()
.write(true)
.read(true)
.create_new(true)
.open(offset_index_file_path(path, offset))
.and_then(|file| {
file.set_len(cap * ENTRY_SIZE as u64)?;
let mmap = unsafe { MmapMut::map_mut(&file) }?;

Ok(IndexFileMut {
inner: mmap,
num_entries: 0,
_marker: PhantomData,
})
})
.or_else(|e| {
if e.kind() == io::ErrorKind::AlreadyExists {
debug!("Index file {} already exists", path.display());
open_index_file(path, offset, cap)
} else {
Err(e)
}
})
/// A wrapper over [`IndexFileMut`] to provide read-only access to the index file.
pub struct IndexFile<Key: Into<u64> + From<u64>> {
inner: IndexFileMut<Key>,
_marker: PhantomData<Key>,
}

pub fn open_index_file<Key: Into<u64> + From<u64>>(
path: &Path,
offset: u64,
cap: u64,
) -> io::Result<IndexFileMut<Key>> {
let file = File::options()
.read(true)
.write(true)
.open(offset_index_file_path(path, offset))?;
file.set_len(cap * ENTRY_SIZE as u64)?;
let mmap = unsafe { MmapMut::map_mut(&file)? };

let mut me = IndexFileMut {
inner: mmap,
num_entries: 0,
_marker: PhantomData,
};

me.num_entries = me.num_entries().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(me)
}
impl<Key: Into<u64> + From<u64>> IndexFile<Key> {
pub fn open_index_file(path: &Path, offset: u64) -> io::Result<Self> {
let file = File::options()
.read(true)
.append(true)
.open(offset_index_file_path(path, offset))?;
let mmap = unsafe { MmapMut::map_mut(&file)? };

let mut inner = IndexFileMut {
inner: mmap,
num_entries: 0,
_marker: PhantomData,
};
inner.num_entries = inner
.num_entries()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

Ok(Self {
inner,
_marker: PhantomData,
})
}

pub fn delete_index_file(path: &Path, offset: u64) -> io::Result<()> {
fs::remove_file(offset_index_file_path(path, offset)).map_err(Into::into)
pub fn key_lookup(&self, key: Key) -> Result<(Key, u64), IndexError> {
self.inner.key_lookup(key)
}
}

#[cfg(test)]
Expand All @@ -257,7 +283,7 @@ mod tests {
let path = temp_dir.path().to_path_buf();

// Create an index file
let mut index_file: IndexFileMut<u64> = create_index_file(&path, 0, cap)?;
let mut index_file: IndexFileMut<u64> = IndexFileMut::create_index_file(&path, 0, cap)?;

// Enter even number keys from 2
for i in 1..fill_till {
Expand Down Expand Up @@ -341,7 +367,7 @@ mod tests {
let path = temp_dir.path().to_path_buf();

// Create an index file
let mut index_file: IndexFileMut<u64> = create_index_file(&path, 0, 100)?;
let mut index_file: IndexFileMut<u64> = IndexFileMut::create_index_file(&path, 0, 100)?;

for i in 1..10 {
index_file.append(i * 2, i * 2 * 100)?;
Expand All @@ -350,7 +376,7 @@ mod tests {
assert_eq!(index_file.num_entries, 9);
drop(index_file);

let open_index_file: IndexFileMut<u64> = open_index_file(&path, 0, 100)?;
let open_index_file: IndexFileMut<u64> = IndexFileMut::open_index_file(&path, 0, 100)?;
assert_eq!(open_index_file.num_entries, 9);
assert_eq!(open_index_file.key_lookup(6)?, (6, 600));

Expand Down
4 changes: 1 addition & 3 deletions crates/commitlog/src/index/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ use std::io;
use thiserror::Error;
mod indexfile;

pub use indexfile::create_index_file;
pub use indexfile::delete_index_file;
pub use indexfile::offset_index_file_path;
pub use indexfile::IndexFileMut;
pub use indexfile::{IndexFile, IndexFileMut};

#[derive(Error, Debug)]
pub enum IndexError {
Expand Down
14 changes: 9 additions & 5 deletions crates/commitlog/src/repo/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use std::{

use log::{debug, warn};

use crate::index::{create_index_file, delete_index_file, offset_index_file_path};
use crate::index::offset_index_file_path;

use super::{Repo, TxOffset, TxOffsetIndex};
use super::{Repo, TxOffset, TxOffsetIndex, TxOffsetIndexMut};

const SEGMENT_FILE_EXT: &str = ".stdb.log";

Expand Down Expand Up @@ -128,11 +128,15 @@ impl Repo for Fs {
Ok(segments)
}

fn get_offset_index(&self, offset: TxOffset, cap: u64) -> io::Result<TxOffsetIndex> {
create_index_file(&self.root, offset, cap)
fn create_offset_index(&self, offset: TxOffset, cap: u64) -> io::Result<TxOffsetIndexMut> {
TxOffsetIndexMut::create_index_file(&self.root, offset, cap)
}

fn remove_offset_index(&self, offset: TxOffset) -> io::Result<()> {
delete_index_file(&self.root, offset)
TxOffsetIndexMut::delete_index_file(&self.root, offset)
}

fn get_offset_index(&self, offset: TxOffset) -> io::Result<TxOffsetIndex> {
TxOffsetIndex::open_index_file(&self.root, offset)
}
}
26 changes: 16 additions & 10 deletions crates/commitlog/src/repo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use log::{debug, warn};
use crate::{
commit::Commit,
error,
index::IndexFileMut,
index::{IndexFile, IndexFileMut},
segment::{FileLike, Header, Metadata, OffsetIndexWriter, Reader, Writer},
Options,
};
Expand All @@ -19,15 +19,16 @@ pub use fs::Fs;
pub use mem::Memory;

pub type TxOffset = u64;
pub type TxOffsetIndex = IndexFileMut<TxOffset>;
pub type TxOffsetIndexMut = IndexFileMut<TxOffset>;
pub type TxOffsetIndex = IndexFile<TxOffset>;

/// A repository of log segments.
///
/// This is mainly an internal trait to allow testing against an in-memory
/// representation.
pub trait Repo: Clone {
/// The type of log segments managed by this repo, which must behave like a file.
type Segment: io::Read + io::Write + FileLike;
type Segment: io::Read + io::Write + FileLike + io::Seek;

/// Create a new segment with the minimum transaction offset `offset`.
///
Expand Down Expand Up @@ -58,24 +59,29 @@ pub trait Repo: Clone {
/// offsets, sorted in ascending order.
fn existing_offsets(&self) -> io::Result<Vec<u64>>;

/// Create or get an existing `TxOffsetIndex` for the given `offset`.
/// Create [`TxOffsetIndexMut`] for the given `offset` or open it if already exist.
/// The `cap` parameter is the maximum number of entries in the index.
fn get_offset_index(&self, _offset: TxOffset, _cap: u64) -> io::Result<TxOffsetIndex> {
fn create_offset_index(&self, _offset: TxOffset, _cap: u64) -> io::Result<TxOffsetIndexMut> {
Err(io::Error::new(io::ErrorKind::Other, "not implemented"))
}

/// Remove `TxOffsetIndex` named with `offset`.
/// Remove [`TxOffsetIndexMut`] named with `offset`.
fn remove_offset_index(&self, _offset: TxOffset) -> io::Result<()> {
Err(io::Error::new(io::ErrorKind::Other, "not implemented"))
}

/// Get [`TxOffsetIndex`] for the given `offset`.
fn get_offset_index(&self, _offset: TxOffset) -> io::Result<TxOffsetIndex> {
Err(io::Error::new(io::ErrorKind::Other, "not implemented"))
}
}

fn offset_index_len(opts: Options) -> u64 {
opts.max_segment_size / opts.offset_index_interval_bytes
}

fn get_offset_index_writer<R: Repo>(repo: &R, offset: u64, opts: Options) -> Option<OffsetIndexWriter> {
repo.get_offset_index(offset, offset_index_len(opts))
fn create_offset_index_writer<R: Repo>(repo: &R, offset: u64, opts: Options) -> Option<OffsetIndexWriter> {
repo.create_offset_index(offset, offset_index_len(opts))
.map(|index| OffsetIndexWriter::new(index, opts))
.map_err(|e| {
warn!("failed to get offset index for segment {offset}: {e}");
Expand Down Expand Up @@ -111,7 +117,7 @@ pub fn create_segment_writer<R: Repo>(repo: &R, opts: Options, offset: u64) -> i

max_records_in_commit: opts.max_records_in_commit,

offset_index_head: get_offset_index_writer(repo, offset, opts),
offset_index_head: create_offset_index_writer(repo, offset, opts),
})
}

Expand Down Expand Up @@ -166,7 +172,7 @@ pub fn resume_segment_writer<R: Repo>(

max_records_in_commit: opts.max_records_in_commit,

offset_index_head: get_offset_index_writer(repo, offset, opts),
offset_index_head: create_offset_index_writer(repo, offset, opts),
}))
}

Expand Down
Loading

2 comments on commit c5a9a0d

@github-actions
Copy link

@github-actions github-actions bot commented on c5a9a0d Oct 7, 2024

Choose a reason for hiding this comment

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

Criterion benchmark results

Criterion benchmark report

YOU SHOULD PROBABLY IGNORE THESE RESULTS.

Criterion is a wall time based benchmarking system that is extremely noisy when run on CI. We collect these results for longitudinal analysis, but they are not reliable for comparing individual PRs.

Go look at the callgrind report instead.

empty

db on disk new latency old latency new throughput old throughput
sqlite 💿 417.1±2.13ns 419.9±2.96ns - -
sqlite 🧠 408.9±2.87ns 429.2±1.90ns - -
stdb_raw 💿 627.0±0.17ns 622.8±1.69ns - -
stdb_raw 🧠 627.0±0.18ns 622.7±1.41ns - -

insert_1

db on disk schema indices preload new latency old latency new throughput old throughput

insert_bulk

db on disk schema indices preload count new latency old latency new throughput old throughput
sqlite 💿 u32_u64_str btree_each_column 2048 256 595.4±42.98µs 583.8±2.21µs 1679 tx/sec 1712 tx/sec
sqlite 💿 u32_u64_str unique_0 2048 256 151.3±0.43µs 149.3±0.36µs 6.5 Ktx/sec 6.5 Ktx/sec
sqlite 💿 u32_u64_u64 btree_each_column 2048 256 466.2±0.58µs 476.6±25.87µs 2.1 Ktx/sec 2.0 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 2048 256 134.2±0.27µs 141.2±16.32µs 7.3 Ktx/sec 6.9 Ktx/sec
sqlite 🧠 u32_u64_str btree_each_column 2048 256 446.9±0.98µs 450.0±0.41µs 2.2 Ktx/sec 2.2 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 2048 256 123.0±0.45µs 125.1±0.42µs 7.9 Ktx/sec 7.8 Ktx/sec
sqlite 🧠 u32_u64_u64 btree_each_column 2048 256 365.1±0.41µs 365.2±0.64µs 2.7 Ktx/sec 2.7 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 2048 256 104.3±0.16µs 107.8±0.40µs 9.4 Ktx/sec 9.1 Ktx/sec
stdb_raw 💿 u32_u64_str btree_each_column 2048 256 617.2±39.39µs 603.0±20.79µs 1620 tx/sec 1658 tx/sec
stdb_raw 💿 u32_u64_str unique_0 2048 256 502.5±21.90µs 411.0±14.22µs 1990 tx/sec 2.4 Ktx/sec
stdb_raw 💿 u32_u64_u64 btree_each_column 2048 256 375.3±11.73µs 341.9±9.43µs 2.6 Ktx/sec 2.9 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 2048 256 353.4±7.28µs 356.3±7.02µs 2.8 Ktx/sec 2.7 Ktx/sec
stdb_raw 🧠 u32_u64_str btree_each_column 2048 256 308.0±0.45µs 321.2±0.17µs 3.2 Ktx/sec 3.0 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 2048 256 239.0±0.33µs 237.8±0.22µs 4.1 Ktx/sec 4.1 Ktx/sec
stdb_raw 🧠 u32_u64_u64 btree_each_column 2048 256 242.7±0.37µs 250.8±0.30µs 4.0 Ktx/sec 3.9 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 2048 256 219.1±0.10µs 219.2±0.64µs 4.5 Ktx/sec 4.5 Ktx/sec

iterate

db on disk schema indices new latency old latency new throughput old throughput
sqlite 💿 u32_u64_str unique_0 22.1±0.20µs 21.9±0.07µs 44.2 Ktx/sec 44.6 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 21.5±0.13µs 20.9±0.14µs 45.5 Ktx/sec 46.8 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 19.5±0.31µs 19.4±0.05µs 50.1 Ktx/sec 50.4 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 18.0±0.28µs 17.7±0.09µs 54.2 Ktx/sec 55.2 Ktx/sec
stdb_raw 💿 u32_u64_str unique_0 3.8±0.00µs 4.0±0.00µs 253.7 Ktx/sec 246.9 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 3.7±0.00µs 3.9±0.00µs 263.6 Ktx/sec 252.6 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 3.8±0.00µs 4.0±0.00µs 254.1 Ktx/sec 246.4 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 3.7±0.00µs 3.9±0.00µs 263.7 Ktx/sec 252.8 Ktx/sec

find_unique

db on disk key type preload new latency old latency new throughput old throughput

filter

db on disk key type index strategy load count new latency old latency new throughput old throughput
sqlite 💿 string index 2048 256 69.1±0.12µs 70.6±0.26µs 14.1 Ktx/sec 13.8 Ktx/sec
sqlite 💿 u64 index 2048 256 64.7±0.10µs 66.3±0.11µs 15.1 Ktx/sec 14.7 Ktx/sec
sqlite 🧠 string index 2048 256 65.8±0.09µs 66.0±0.26µs 14.8 Ktx/sec 14.8 Ktx/sec
sqlite 🧠 u64 index 2048 256 61.0±0.60µs 60.0±0.29µs 16.0 Ktx/sec 16.3 Ktx/sec
stdb_raw 💿 string index 2048 256 4.8±0.00µs 4.9±0.00µs 204.2 Ktx/sec 198.8 Ktx/sec
stdb_raw 💿 u64 index 2048 256 4.8±0.00µs 4.8±0.00µs 201.7 Ktx/sec 204.8 Ktx/sec
stdb_raw 🧠 string index 2048 256 4.8±0.00µs 4.9±0.00µs 204.7 Ktx/sec 198.6 Ktx/sec
stdb_raw 🧠 u64 index 2048 256 4.8±0.00µs 4.8±0.00µs 201.8 Ktx/sec 205.0 Ktx/sec

serialize

schema format count new latency old latency new throughput old throughput
u32_u64_str bflatn_to_bsatn_fast_path 100 3.3±0.01µs 3.3±0.01µs 28.7 Mtx/sec 29.1 Mtx/sec
u32_u64_str bflatn_to_bsatn_slow_path 100 3.0±0.01µs 3.1±0.01µs 31.8 Mtx/sec 31.1 Mtx/sec
u32_u64_str bsatn 100 2.4±0.01µs 2.4±0.00µs 39.2 Mtx/sec 39.7 Mtx/sec
u32_u64_str bsatn 100 40.4±0.14ns 40.5±0.10ns 2.3 Gtx/sec 2.3 Gtx/sec
u32_u64_str json 100 5.1±0.05µs 5.4±0.02µs 18.8 Mtx/sec 17.6 Mtx/sec
u32_u64_str json 100 7.0±0.03µs 7.3±0.09µs 13.6 Mtx/sec 13.1 Mtx/sec
u32_u64_str product_value 100 1018.4±7.50ns 1018.8±3.33ns 93.6 Mtx/sec 93.6 Mtx/sec
u32_u64_u64 bflatn_to_bsatn_fast_path 100 1115.2±4.69ns 1107.7±7.61ns 85.5 Mtx/sec 86.1 Mtx/sec
u32_u64_u64 bflatn_to_bsatn_slow_path 100 2.4±0.01µs 2.5±0.00µs 39.1 Mtx/sec 38.6 Mtx/sec
u32_u64_u64 bsatn 100 1688.0±31.88ns 1729.2±37.38ns 56.5 Mtx/sec 55.2 Mtx/sec
u32_u64_u64 bsatn 100 39.4±0.07ns 39.3±0.25ns 2.4 Gtx/sec 2.4 Gtx/sec
u32_u64_u64 json 100 3.2±0.07µs 3.4±0.23µs 30.0 Mtx/sec 28.1 Mtx/sec
u32_u64_u64 json 100 4.9±0.02µs 5.1±0.12µs 19.5 Mtx/sec 18.6 Mtx/sec
u32_u64_u64 product_value 100 1014.7±1.07ns 1014.7±1.31ns 94.0 Mtx/sec 94.0 Mtx/sec
u64_u64_u32 bflatn_to_bsatn_fast_path 100 894.4±4.27ns 905.4±2.93ns 106.6 Mtx/sec 105.3 Mtx/sec
u64_u64_u32 bflatn_to_bsatn_slow_path 100 2.4±0.00µs 2.5±0.01µs 39.1 Mtx/sec 38.5 Mtx/sec
u64_u64_u32 bsatn 100 1730.7±110.63ns 1719.1±37.85ns 55.1 Mtx/sec 55.5 Mtx/sec
u64_u64_u32 bsatn 100 706.8±0.87ns 749.7±0.49ns 134.9 Mtx/sec 127.2 Mtx/sec
u64_u64_u32 json 100 3.4±0.06µs 3.4±0.04µs 28.4 Mtx/sec 28.3 Mtx/sec
u64_u64_u32 json 100 5.1±0.02µs 4.9±0.00µs 18.6 Mtx/sec 19.3 Mtx/sec
u64_u64_u32 product_value 100 1015.6±0.72ns 1018.8±0.49ns 93.9 Mtx/sec 93.6 Mtx/sec

stdb_module_large_arguments

arg size new latency old latency new throughput old throughput
64KiB 115.1±7.42µs 109.1±11.28µs - -

stdb_module_print_bulk

line count new latency old latency new throughput old throughput
1 54.9±6.98µs 52.9±6.07µs - -
100 604.0±9.46µs 602.5±7.22µs - -
1000 4.0±0.86ms 5.4±0.04ms - -

remaining

name new latency old latency new throughput old throughput
special/db_game/circles/load=10 294.7±2.59µs 308.1±4.29µs - -
special/db_game/circles/load=100 291.7±3.13µs 310.0±2.97µs - -
special/db_game/ia_loop/load=10 0.0±0.00ns 0.0±0.00ns - -
special/db_game/ia_loop/load=100 0.0±0.00ns 0.0±0.00ns - -
sqlite/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 54.0±0.15µs 55.7±0.25µs 18.1 Ktx/sec 17.5 Ktx/sec
sqlite/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 46.4±0.23µs 51.3±10.61µs 21.0 Ktx/sec 19.0 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 38.8±0.18µs 40.5±0.36µs 25.2 Ktx/sec 24.1 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 35.2±0.12µs 35.6±0.33µs 27.7 Ktx/sec 27.5 Ktx/sec
stdb_module/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 1292.0±14.88µs 1292.0±12.48µs 774 tx/sec 773 tx/sec
stdb_module/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 1033.2±14.29µs 1025.4±12.56µs 967 tx/sec 975 tx/sec
stdb_raw/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 636.9±26.08µs 652.5±19.35µs 1570 tx/sec 1532 tx/sec
stdb_raw/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 486.9±13.72µs 489.9±10.60µs 2.0 Ktx/sec 2041 tx/sec
stdb_raw/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 370.3±0.73µs 374.6±1.36µs 2.6 Ktx/sec 2.6 Ktx/sec
stdb_raw/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 337.9±0.55µs 335.4±0.45µs 2.9 Ktx/sec 2.9 Ktx/sec

@github-actions
Copy link

@github-actions github-actions bot commented on c5a9a0d Oct 7, 2024

Choose a reason for hiding this comment

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

Callgrind benchmark results

Callgrind Benchmark Report

These benchmarks were run using callgrind,
an instruction-level profiler. They allow comparisons between sqlite (sqlite), SpacetimeDB running through a module (stdb_module), and the underlying SpacetimeDB data storage engine (stdb_raw). Callgrind emulates a CPU to collect the below estimates.

Measurement changes larger than five percent are in bold.

In-memory benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 5395 5395 0.00% 5475 5475 0.00%
sqlite 5509 5509 0.00% 5921 5909 0.20%

callgrind: filter

db schema indices count preload _column data_type total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str no_index 64 128 2 string 117799 117909 -0.09% 118339 118481 -0.12%
stdb_raw u32_u64_str no_index 64 128 1 u64 75388 75498 -0.15% 75826 75848 -0.03%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 24062 24057 0.02% 24610 24541 0.28%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 23029 23025 0.02% 23431 23459 -0.12%
sqlite u32_u64_str no_index 64 128 2 string 144677 144677 0.00% 146203 146291 -0.06%
sqlite u32_u64_str no_index 64 128 1 u64 124027 124027 0.00% 125303 125407 -0.08%
sqlite u32_u64_str btree_each_column 64 128 2 string 134476 134476 0.00% 136102 136290 -0.14%
sqlite u32_u64_str btree_each_column 64 128 1 u64 131354 131354 0.00% 132822 132918 -0.07%

callgrind: insert bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 64 128 895841 893652 0.24% 943751 943330 0.04%
stdb_raw u32_u64_str btree_each_column 64 128 1047434 1049283 -0.18% 1121224 1112521 0.78%
sqlite u32_u64_str unique_0 64 128 398158 398158 0.00% 416890 414742 0.52%
sqlite u32_u64_str btree_each_column 64 128 983485 983485 0.00% 1018741 1024309 -0.54%

callgrind: iterate

db schema indices count total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 152681 152791 -0.07% 152765 152871 -0.07%
stdb_raw u32_u64_str unique_0 64 15706 15816 -0.70% 15782 15884 -0.64%
sqlite u32_u64_str unique_0 1024 1046665 1046653 0.00% 1050083 1049929 0.01%
sqlite u32_u64_str unique_0 64 74799 74799 0.00% 75829 75833 -0.01%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47374 47374 0.00% 49992 49992 0.00%
64 bsatn 25716 25716 0.00% 27960 27960 0.00%
16 json 12126 12126 0.00% 13996 13996 0.00%
16 bsatn 8117 8117 0.00% 9477 9477 0.00%

callgrind: update bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 1024 20585638 20601480 -0.08% 21269336 21265272 0.02%
stdb_raw u32_u64_str unique_0 64 128 1302631 1300269 0.18% 1380667 1344785 2.67%
sqlite u32_u64_str unique_0 1024 1024 1802091 1802091 0.00% 1811359 1811311 0.00%
sqlite u32_u64_str unique_0 64 128 128437 128437 0.00% 131341 131225 0.09%
On-disk benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 5405 5405 0.00% 5485 5485 0.00%
sqlite 5551 5551 0.00% 6019 6019 0.00%

callgrind: filter

db schema indices count preload _column data_type total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str no_index 64 128 2 string 117809 117919 -0.09% 118357 118423 -0.06%
stdb_raw u32_u64_str no_index 64 128 1 u64 75398 75508 -0.15% 75784 75806 -0.03%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 24073 24068 0.02% 24613 24544 0.28%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 23039 23035 0.02% 23425 23461 -0.15%
sqlite u32_u64_str no_index 64 128 2 string 146598 146598 0.00% 148464 148472 -0.01%
sqlite u32_u64_str no_index 64 128 1 u64 125966 125948 0.01% 127578 127540 0.03%
sqlite u32_u64_str btree_each_column 64 128 2 string 136598 136598 0.00% 138650 138786 -0.10%
sqlite u32_u64_str btree_each_column 64 128 1 u64 133440 133440 0.00% 135342 135382 -0.03%

callgrind: insert bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 64 128 845842 844052 0.21% 892494 894800 -0.26%
stdb_raw u32_u64_str btree_each_column 64 128 996643 994904 0.17% 1068751 1058648 0.95%
sqlite u32_u64_str unique_0 64 128 415695 415695 0.00% 433675 431611 0.48%
sqlite u32_u64_str btree_each_column 64 128 1021736 1021736 0.00% 1055706 1061278 -0.53%

callgrind: iterate

db schema indices count total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 152691 152801 -0.07% 152763 152893 -0.09%
stdb_raw u32_u64_str unique_0 64 15716 15826 -0.70% 15788 15894 -0.67%
sqlite u32_u64_str unique_0 1024 1049721 1049721 0.00% 1053515 1053519 -0.00%
sqlite u32_u64_str unique_0 64 76571 76571 0.00% 77921 77897 0.03%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47374 47374 0.00% 49992 49992 0.00%
64 bsatn 25716 25716 0.00% 27960 27960 0.00%
16 json 12126 12126 0.00% 13996 13996 0.00%
16 bsatn 8117 8117 0.00% 9477 9477 0.00%

callgrind: update bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 1024 19332385 19302722 0.15% 20104055 19986094 0.59%
stdb_raw u32_u64_str unique_0 64 128 1254552 1252885 0.13% 1332920 1326797 0.46%
sqlite u32_u64_str unique_0 1024 1024 1809658 1809652 0.00% 1818474 1818344 0.01%
sqlite u32_u64_str unique_0 64 128 132563 132563 0.00% 135539 135567 -0.02%

Please sign in to comment.