Skip to content

Commit

Permalink
incr-join, find_updates: avoid unncecessary clones & use partition (#988
Browse files Browse the repository at this point in the history
)

* incr-join, find_updates: avoid unncecessary clones & use partition

* JoinSide: store 'Vec<PV>'s instead

* address joshua & phoebe's reviews
  • Loading branch information
Centril committed Mar 19, 2024
1 parent a21b1bc commit 5cbdcae
Showing 1 changed file with 56 additions and 101 deletions.
157 changes: 56 additions & 101 deletions crates/core/src/subscription/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use spacetimedb_lib::identity::AuthCtx;
use spacetimedb_lib::ProductValue;
use spacetimedb_primitives::TableId;
use spacetimedb_sats::db::auth::{StAccess, StTableType};
use spacetimedb_sats::relation::{DbTable, Header};
use spacetimedb_sats::relation::DbTable;
use spacetimedb_vm::expr::{self, IndexJoin, Query, QueryExpr, SourceSet};
use spacetimedb_vm::rel_ops::RelOps;
use spacetimedb_vm::relation::MemTable;
Expand Down Expand Up @@ -209,32 +209,21 @@ pub struct IncrementalJoin {

/// One side of an [`IncrementalJoin`].
///
/// Holds the "physical" [`DbTable`] this side of the join operates on, as well
/// as the [`DatabaseTableUpdate`]s pertaining that table.
/// Holds the updates pertaining to a table on one side of the join.
struct JoinSide {
table_id: TableId,
table_name: String,
inserts: Vec<TableOp>,
deletes: Vec<TableOp>,
inserts: Vec<ProductValue>,
deletes: Vec<ProductValue>,
}

impl JoinSide {
/// Return a [`DatabaseTableUpdate`] consisting of only insert operations.
pub fn inserts(&self) -> DatabaseTableUpdate {
DatabaseTableUpdate {
table_id: self.table_id,
table_name: self.table_name.clone(),
ops: self.inserts.to_vec(),
}
/// Return a list of updates consisting of only insert operations.
pub fn inserts(&self) -> Vec<ProductValue> {
self.inserts.clone()
}

/// Return a [`DatabaseTableUpdate`] with only delete operations.
pub fn deletes(&self) -> DatabaseTableUpdate {
DatabaseTableUpdate {
table_id: self.table_id,
table_name: self.table_name.clone(),
ops: self.deletes.to_vec(),
}
/// Return a list of updates with only delete operations.
pub fn deletes(&self) -> Vec<ProductValue> {
self.deletes.clone()
}

/// Does this table update include inserts?
Expand All @@ -249,18 +238,6 @@ impl JoinSide {
}

impl IncrementalJoin {
/// Construct an empty [`DatabaseTableUpdate`] with the schema of `table`
/// to use as a source when pre-compiling `eval_incr` queries.
fn dummy_table_update(table: &DbTable) -> DatabaseTableUpdate {
let table_id = table.table_id;
let table_name = table.head.table_name.clone();
DatabaseTableUpdate {
table_id,
table_name,
ops: vec![],
}
}

fn optimize_query(join: IndexJoin) -> QueryExpr {
let expr = QueryExpr::from(join);
// Because (at least) one of the two tables will be a `MemTable`,
Expand Down Expand Up @@ -313,21 +290,15 @@ impl IncrementalJoin {
.context("expected a physical database table")?
.clone();

let (virtual_index_plan, _sources) =
with_delta_table(join.clone(), Some(Self::dummy_table_update(&index_table)), None);
let (virtual_index_plan, _sources) = with_delta_table(join.clone(), Some(Vec::new()), None);
debug_assert_eq!(_sources.len(), 1);
let virtual_index_plan = Self::optimize_query(virtual_index_plan);

let (virtual_probe_plan, _sources) =
with_delta_table(join.clone(), None, Some(Self::dummy_table_update(&probe_table)));
let (virtual_probe_plan, _sources) = with_delta_table(join.clone(), None, Some(Vec::new()));
debug_assert_eq!(_sources.len(), 1);
let virtual_probe_plan = Self::optimize_query(virtual_probe_plan);

let (virtual_plan, _sources) = with_delta_table(
join.clone(),
Some(Self::dummy_table_update(&index_table)),
Some(Self::dummy_table_update(&probe_table)),
);
let (virtual_plan, _sources) = with_delta_table(join.clone(), Some(Vec::new()), Some(Vec::new()));
debug_assert_eq!(_sources.len(), 2);
let virtual_plan = virtual_plan.to_inner_join();

Expand Down Expand Up @@ -360,46 +331,53 @@ impl IncrementalJoin {
&self,
updates: impl IntoIterator<Item = &'a DatabaseTableUpdate>,
) -> Option<(JoinSide, JoinSide)> {
let mut lhs_ops = Vec::new();
let mut rhs_ops = Vec::new();
let mut lhs_inserts = Vec::new();
let mut lhs_deletes = Vec::new();
let mut rhs_inserts = Vec::new();
let mut rhs_deletes = Vec::new();

// Partitions `updates` into `deletes` and `inserts`.
let partition_into = |deletes: &mut Vec<_>, inserts: &mut Vec<_>, updates: &DatabaseTableUpdate| {
for update in &updates.ops {
if update.op_type == 0 {
&mut *deletes
} else {
&mut *inserts
}
.push(update.row.clone());
}
};

// Partitions all updates into the `(l|r)hs_(insert|delete)_ops` above.
for update in updates {
if update.table_id == self.lhs.table_id {
lhs_ops.extend(update.ops.iter().cloned());
partition_into(&mut lhs_deletes, &mut lhs_inserts, update);
} else if update.table_id == self.rhs.table_id {
rhs_ops.extend(update.ops.iter().cloned());
partition_into(&mut rhs_deletes, &mut rhs_inserts, update);
}
}

if lhs_ops.is_empty() && rhs_ops.is_empty() {
// No updates at all? Return `None`.
if [&lhs_inserts, &lhs_deletes, &rhs_inserts, &rhs_deletes]
.iter()
.all(|ops| ops.is_empty())
{
return None;
}

let lhs = JoinSide {
table_id: self.lhs.table_id,
table_name: self.lhs.head.table_name.clone(),
inserts: lhs_ops.iter().filter(|op| op.op_type == 1).cloned().collect(),
deletes: lhs_ops.iter().filter(|op| op.op_type == 0).cloned().collect(),
};

let rhs = JoinSide {
table_id: self.rhs.table_id,
table_name: self.rhs.head.table_name.clone(),
inserts: rhs_ops.iter().filter(|op| op.op_type == 1).cloned().collect(),
deletes: rhs_ops.iter().filter(|op| op.op_type == 0).cloned().collect(),
};

Some((lhs, rhs))
// Stich together the `JoinSide`s.
let join_side = |deletes, inserts| JoinSide { deletes, inserts };
Some((join_side(lhs_deletes, lhs_inserts), join_side(rhs_deletes, rhs_inserts)))
}

/// Evaluate join plan for lhs updates.
fn eval_lhs(
&self,
db: &RelationalDB,
tx: &Tx,
lhs: DatabaseTableUpdate,
lhs: Vec<ProductValue>,
) -> Result<impl Iterator<Item = ProductValue>, DBError> {
let lhs = to_mem_table(self.lhs.head.clone(), self.lhs.table_access, lhs);
let lhs = MemTable::new(self.lhs.head.clone(), self.lhs.table_access, lhs);
let mut sources = SourceSet::default();
sources.add_mem_table(lhs);
eval_updates(db, tx, self.plan_for_delta_lhs(), sources)
Expand All @@ -410,9 +388,9 @@ impl IncrementalJoin {
&self,
db: &RelationalDB,
tx: &Tx,
rhs: DatabaseTableUpdate,
rhs: Vec<ProductValue>,
) -> Result<impl Iterator<Item = ProductValue>, DBError> {
let rhs = to_mem_table(self.rhs.head.clone(), self.rhs.table_access, rhs);
let rhs = MemTable::new(self.rhs.head.clone(), self.rhs.table_access, rhs);
let mut sources = SourceSet::default();
sources.add_mem_table(rhs);
eval_updates(db, tx, self.plan_for_delta_rhs(), sources)
Expand All @@ -423,11 +401,11 @@ impl IncrementalJoin {
&self,
db: &RelationalDB,
tx: &Tx,
lhs: DatabaseTableUpdate,
rhs: DatabaseTableUpdate,
lhs: Vec<ProductValue>,
rhs: Vec<ProductValue>,
) -> Result<impl Iterator<Item = ProductValue>, DBError> {
let lhs = to_mem_table(self.lhs.head.clone(), self.lhs.table_access, lhs);
let rhs = to_mem_table(self.rhs.head.clone(), self.rhs.table_access, rhs);
let lhs = MemTable::new(self.lhs.head.clone(), self.lhs.table_access, lhs);
let rhs = MemTable::new(self.rhs.head.clone(), self.rhs.table_access, rhs);
let mut sources = SourceSet::default();
let (index_side, probe_side) = if self.return_index_rows { (lhs, rhs) } else { (rhs, lhs) };
sources.add_mem_table(index_side);
Expand Down Expand Up @@ -571,39 +549,25 @@ impl IncrementalJoin {
}
}

/// Construct a [`MemTable`] containing the updates from `delta`,
/// which must be derived from a table with `head` and `table_access`.
fn to_mem_table(head: Arc<Header>, table_access: StAccess, delta: DatabaseTableUpdate) -> MemTable {
MemTable::new(
head,
table_access,
delta.ops.into_iter().map(|op| op.row).collect::<Vec<_>>(),
)
}

/// Replace an [IndexJoin]'s scan or fetch operation with a delta table.
/// A delta table consists purely of updates or changes to the base table.
fn with_delta_table(
mut join: IndexJoin,
index_side: Option<DatabaseTableUpdate>,
probe_side: Option<DatabaseTableUpdate>,
index_side: Option<Vec<ProductValue>>,
probe_side: Option<Vec<ProductValue>>,
) -> (IndexJoin, SourceSet) {
let mut sources = SourceSet::default();

if let Some(index_side) = index_side {
let head = join.index_side.head().clone();
let table_access = join.index_side.table_access();
let mem_table = to_mem_table(head, table_access, index_side);
let source_expr = sources.add_mem_table(mem_table);
join.index_side = source_expr;
join.index_side = sources.add_mem_table(MemTable::new(head, table_access, index_side));
}

if let Some(probe_side) = probe_side {
let head = join.probe_side.source.head().clone();
let table_access = join.probe_side.source.table_access();
let mem_table = to_mem_table(head, table_access, probe_side);
let source_expr = sources.add_mem_table(mem_table);
join.probe_side.source = source_expr;
join.probe_side.source = sources.add_mem_table(MemTable::new(head, table_access, probe_side));
}

(join, sources)
Expand Down Expand Up @@ -720,7 +684,6 @@ pub(crate) fn get_all(relational_db: &RelationalDB, tx: &Tx, auth: &AuthCtx) ->
mod tests {
use super::*;
use crate::db::relational_db::tests_utils::make_test_db;
use crate::host::module_host::TableOp;
use crate::sql::compiler::compile_sql;
use spacetimedb_lib::error::ResultTest;
use spacetimedb_sats::relation::{DbTable, FieldName};
Expand All @@ -736,7 +699,7 @@ mod tests {
// Create table [lhs] with index on [b]
let schema = &[("a", AlgebraicType::U64), ("b", AlgebraicType::U64)];
let indexes = &[(1.into(), "b")];
let lhs_id = db.create_table_for_test("lhs", schema, indexes)?;
let _ = db.create_table_for_test("lhs", schema, indexes)?;

// Create table [rhs] with index on [b, c]
let schema = &[
Expand Down Expand Up @@ -766,11 +729,7 @@ mod tests {
};

// Create an insert for an incremental update.
let delta = DatabaseTableUpdate {
table_id: lhs_id,
table_name: String::from("lhs"),
ops: vec![TableOp::insert(product![0u64, 0u64])],
};
let delta = vec![product![0u64, 0u64]];

// Optimize the query plan for the incremental update.
let (expr, _sources) = with_delta_table(join, Some(delta), None);
Expand Down Expand Up @@ -834,7 +793,7 @@ mod tests {
("d", AlgebraicType::U64),
];
let indexes = &[(0.into(), "b"), (1.into(), "c")];
let rhs_id = db.create_table_for_test("rhs", schema, indexes)?;
let _ = db.create_table_for_test("rhs", schema, indexes)?;

let tx = db.begin_tx();
// Should generate an index join since there is an index on `lhs.b`.
Expand All @@ -855,11 +814,7 @@ mod tests {
};

// Create an insert for an incremental update.
let delta = DatabaseTableUpdate {
table_id: rhs_id,
table_name: String::from("rhs"),
ops: vec![TableOp::insert(product![0u64, 0u64, 0u64])],
};
let delta = vec![product![0u64, 0u64, 0u64]];

// Optimize the query plan for the incremental update.
let (expr, _sources) = with_delta_table(join, None, Some(delta));
Expand Down

2 comments on commit 5cbdcae

@github-actions
Copy link

@github-actions github-actions bot commented on 5cbdcae Mar 19, 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 💿 411.2±2.23ns 422.0±3.85ns - -
sqlite 🧠 403.8±1.16ns 411.4±1.40ns - -
stdb_raw 💿 716.6±2.03ns 707.4±1.83ns - -
stdb_raw 🧠 697.2±0.96ns 677.4±0.89ns - -

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 505.5±0.68µs 510.3±1.43µs 1978 tx/sec 1959 tx/sec
sqlite 💿 u32_u64_str unique_0 2048 256 133.8±0.51µs 130.2±0.52µs 7.3 Ktx/sec 7.5 Ktx/sec
sqlite 💿 u32_u64_u64 btree_each_column 2048 256 416.5±0.62µs 416.8±0.48µs 2.3 Ktx/sec 2.3 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 2048 256 121.6±0.62µs 119.7±0.28µs 8.0 Ktx/sec 8.2 Ktx/sec
sqlite 🧠 u32_u64_str btree_each_column 2048 256 438.2±0.90µs 444.2±0.89µs 2.2 Ktx/sec 2.2 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 2048 256 115.8±0.29µs 116.0±0.97µs 8.4 Ktx/sec 8.4 Ktx/sec
sqlite 🧠 u32_u64_u64 btree_each_column 2048 256 359.0±1.16µs 364.3±0.65µs 2.7 Ktx/sec 2.7 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 2048 256 104.1±0.63µs 102.6±0.46µs 9.4 Ktx/sec 9.5 Ktx/sec
stdb_raw 💿 u32_u64_str btree_each_column 2048 256 712.7±0.40µs 710.8±0.86µs 1403 tx/sec 1406 tx/sec
stdb_raw 💿 u32_u64_str unique_0 2048 256 623.3±0.97µs 617.9±0.73µs 1604 tx/sec 1618 tx/sec
stdb_raw 💿 u32_u64_u64 btree_each_column 2048 256 423.0±0.23µs 418.2±0.84µs 2.3 Ktx/sec 2.3 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 2048 256 382.4±0.32µs 375.4±0.46µs 2.6 Ktx/sec 2.6 Ktx/sec
stdb_raw 🧠 u32_u64_str btree_each_column 2048 256 486.6±0.23µs 485.1±0.40µs 2.0 Ktx/sec 2.0 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 2048 256 402.9±0.50µs 400.4±0.73µs 2.4 Ktx/sec 2.4 Ktx/sec
stdb_raw 🧠 u32_u64_u64 btree_each_column 2048 256 319.9±0.24µs 314.1±0.32µs 3.1 Ktx/sec 3.1 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 2048 256 284.9±0.69µs 279.0±0.22µs 3.4 Ktx/sec 3.5 Ktx/sec

iterate

db on disk schema indices new latency old latency new throughput old throughput
sqlite 💿 u32_u64_str unique_0 19.8±0.21µs 20.7±0.08µs 49.2 Ktx/sec 47.1 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 18.2±0.08µs 19.3±0.21µs 53.6 Ktx/sec 50.7 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 19.0±0.12µs 19.5±0.21µs 51.5 Ktx/sec 50.2 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 17.1±0.10µs 17.9±0.24µs 57.3 Ktx/sec 54.6 Ktx/sec
stdb_raw 💿 u32_u64_str unique_0 17.7±0.00µs 18.7±0.00µs 55.2 Ktx/sec 52.3 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 14.8±0.00µs 15.9±0.00µs 65.8 Ktx/sec 61.6 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 17.7±0.00µs 18.7±0.01µs 55.3 Ktx/sec 52.3 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 14.8±0.00µs 15.8±0.02µs 65.9 Ktx/sec 61.7 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 68.3±0.16µs 67.7±0.33µs 14.3 Ktx/sec 14.4 Ktx/sec
sqlite 💿 u64 index 2048 256 63.6±0.11µs 62.9±0.15µs 15.4 Ktx/sec 15.5 Ktx/sec
sqlite 🧠 string index 2048 256 65.8±0.08µs 65.5±0.27µs 14.8 Ktx/sec 14.9 Ktx/sec
sqlite 🧠 u64 index 2048 256 59.8±0.06µs 58.5±0.24µs 16.3 Ktx/sec 16.7 Ktx/sec
stdb_raw 💿 string index 2048 256 5.6±0.00µs 5.6±0.00µs 175.3 Ktx/sec 174.8 Ktx/sec
stdb_raw 💿 u64 index 2048 256 5.6±0.00µs 5.5±0.00µs 173.7 Ktx/sec 177.9 Ktx/sec
stdb_raw 🧠 string index 2048 256 5.5±0.00µs 5.5±0.01µs 176.7 Ktx/sec 176.0 Ktx/sec
stdb_raw 🧠 u64 index 2048 256 5.6±0.00µs 5.5±0.00µs 174.5 Ktx/sec 179.0 Ktx/sec

serialize

schema format count new latency old latency new throughput old throughput
u32_u64_str bsatn 100 2.4±0.04µs 2.4±0.04µs 39.2 Mtx/sec 39.7 Mtx/sec
u32_u64_str json 100 4.9±0.04µs 5.1±0.02µs 19.3 Mtx/sec 18.6 Mtx/sec
u32_u64_str product_value 100 651.6±2.11ns 624.9±3.49ns 146.4 Mtx/sec 152.6 Mtx/sec
u32_u64_u64 bsatn 100 1751.4±34.27ns 1692.4±34.74ns 54.5 Mtx/sec 56.4 Mtx/sec
u32_u64_u64 json 100 3.3±0.05µs 3.5±0.08µs 28.8 Mtx/sec 27.3 Mtx/sec
u32_u64_u64 product_value 100 600.5±1.57ns 598.5±0.95ns 158.8 Mtx/sec 159.3 Mtx/sec

stdb_module_large_arguments

arg size new latency old latency new throughput old throughput
64KiB 89.6±4.80µs 85.3±5.99µs - -

stdb_module_print_bulk

line count new latency old latency new throughput old throughput
1 47.5±4.25µs 41.1±5.81µs - -
100 362.8±65.94µs 354.4±5.24µs - -
1000 2.4±0.56ms 2.4±0.47ms - -

remaining

name new latency old latency new throughput old throughput
sqlite/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 46.2±0.34µs 46.3±0.20µs 21.1 Ktx/sec 21.1 Ktx/sec
sqlite/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 38.9±0.10µs 39.8±0.04µs 25.1 Ktx/sec 24.6 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 37.8±0.20µs 40.3±0.39µs 25.8 Ktx/sec 24.2 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 33.7±0.12µs 35.0±0.19µs 29.0 Ktx/sec 27.9 Ktx/sec
stdb_module/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 3.0±0.01ms 3.0±0.01ms 328 tx/sec 330 tx/sec
stdb_module/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 2.1±0.03ms 2.0±0.01ms 486 tx/sec 496 tx/sec
stdb_raw/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 1113.7±1.18µs 1113.1±1.50µs 897 tx/sec 898 tx/sec
stdb_raw/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 751.1±0.99µs 741.8±0.34µs 1331 tx/sec 1348 tx/sec
stdb_raw/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 788.9±0.45µs 783.4±1.96µs 1267 tx/sec 1276 tx/sec
stdb_raw/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 558.9±1.03µs 550.4±0.37µs 1789 tx/sec 1816 tx/sec

@github-actions
Copy link

@github-actions github-actions bot commented on 5cbdcae Mar 19, 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 6173 6159 0.23% 6887 6991 -1.49%
sqlite 5558 5558 0.00% 6058 5944 1.92%

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 1 u64 84056 84058 -0.00% 84910 84898 0.01%
stdb_raw u32_u64_str no_index 64 128 2 string 126748 126750 -0.00% 127924 127906 0.01%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 29445 29447 -0.01% 30649 30563 0.28%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 28403 28405 -0.01% 29373 29261 0.38%
sqlite u32_u64_str no_index 64 128 1 u64 118033 118033 0.00% 119253 119137 0.10%
sqlite u32_u64_str no_index 64 128 2 string 138612 138612 0.00% 140154 140020 0.10%
sqlite u32_u64_str btree_each_column 64 128 2 string 128475 128475 0.00% 130195 130125 0.05%
sqlite u32_u64_str btree_each_column 64 128 1 u64 125350 125350 0.00% 126700 126836 -0.11%

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 1464002 1463393 0.04% 1499054 1502479 -0.23%
stdb_raw u32_u64_str btree_each_column 64 128 1605927 1605256 0.04% 1649425 1652568 -0.19%
sqlite u32_u64_str unique_0 64 128 396342 396342 0.00% 415852 415840 0.00%
sqlite u32_u64_str btree_each_column 64 128 981664 981664 0.00% 1018046 1020094 -0.20%

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 179198 179200 -0.00% 179602 179600 0.00%
stdb_raw u32_u64_str unique_0 64 18808 18810 -0.01% 19092 19090 0.01%
sqlite u32_u64_str unique_0 1024 1044669 1044699 -0.00% 1047871 1047805 0.01%
sqlite u32_u64_str unique_0 64 74735 74735 0.00% 75817 75749 0.09%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 49521 49521 0.00% 52173 52241 -0.13%
64 bsatn 26627 26627 0.00% 28871 28973 -0.35%
16 json 12643 12643 0.00% 14547 14615 -0.47%
16 bsatn 8357 8357 0.00% 9649 9785 -1.39%

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 46256166 46237250 0.04% 47632880 47833894 -0.42%
stdb_raw u32_u64_str unique_0 64 128 2709263 2708318 0.03% 2802857 2813956 -0.39%
sqlite u32_u64_str unique_0 1024 1024 1801971 1801971 0.00% 1811349 1811719 -0.02%
sqlite u32_u64_str unique_0 64 128 128479 128479 0.00% 131421 131443 -0.02%
On-disk benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 6407 6409 -0.03% 7153 7293 -1.92%
sqlite 5600 5600 0.00% 6126 6012 1.90%

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 1 u64 84298 84300 -0.00% 85264 85252 0.01%
stdb_raw u32_u64_str no_index 64 128 2 string 126990 126992 -0.00% 128394 128312 0.06%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 28645 28647 -0.01% 29635 29559 0.26%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 29687 29689 -0.01% 30891 30813 0.25%
sqlite u32_u64_str no_index 64 128 1 u64 119969 119954 0.01% 121573 121486 0.07%
sqlite u32_u64_str no_index 64 128 2 string 140533 140533 0.00% 142379 142429 -0.04%
sqlite u32_u64_str btree_each_column 64 128 2 string 130525 130525 0.00% 132579 132641 -0.05%
sqlite u32_u64_str btree_each_column 64 128 1 u64 127456 127456 0.00% 129264 129340 -0.06%

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 2274110 2273104 0.04% 2311332 2314324 -0.13%
stdb_raw u32_u64_str btree_each_column 64 128 2424497 2421433 0.13% 2470733 2468111 0.11%
sqlite u32_u64_str unique_0 64 128 413848 413848 0.00% 432560 432668 -0.02%
sqlite u32_u64_str btree_each_column 64 128 1019854 1019854 0.00% 1057082 1059366 -0.22%

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 179440 179442 -0.00% 179876 179878 -0.00%
stdb_raw u32_u64_str unique_0 64 19050 19082 -0.17% 19362 19394 -0.16%
sqlite u32_u64_str unique_0 1024 1047747 1047737 0.00% 1051531 1051431 0.01%
sqlite u32_u64_str unique_0 64 76517 76507 0.01% 77821 77789 0.04%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 49521 49521 0.00% 52173 52241 -0.13%
64 bsatn 26627 26627 0.00% 28871 28973 -0.35%
16 json 12643 12643 0.00% 14547 14615 -0.47%
16 bsatn 8357 8357 0.00% 9649 9785 -1.39%

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 71482639 71442946 0.06% 73201655 73379554 -0.24%
stdb_raw u32_u64_str unique_0 64 128 4145392 4144288 0.03% 4248978 4260400 -0.27%
sqlite u32_u64_str unique_0 1024 1024 1809726 1809726 0.00% 1818684 1818626 0.00%
sqlite u32_u64_str unique_0 64 128 132627 132627 0.00% 135697 135647 0.04%

Please sign in to comment.