Skip to content

Commit

Permalink
Split ColumnOp into one with row indices and one with FieldName &…
Browse files Browse the repository at this point in the history
… other enabled changes (#1207)

* 1. Split ColumnOp into ColumnOp & FieldOp, former storing ColId
2. Shrink SqlAst to 80 bytes, so it can be passed in registers
3. Store end-result Header in IndexSemiJoin
4. Remove operational use of Header in ColumnOp & build_query
5. Simplify RowRef::{get, project, project_owned}

* Make parts of build_query actually infallible.

1. Make IndexSemiJoin::filter infallible.
2. Make ColumnOp::compare and friends infallible.
3. Make RowRef::{get, project, project_owned} infallible.

* Make RelOps::next itself infallible

* 1. with_select{_cmp}: ensure type safety o query exec cannot panic
2. Document RelValue::{get, read_or_take_column, project_owned}
3. Refactor optimize_select
4. Ensure in optimize_select that conditions are merged with preceding selects

* remove RelOps::{head, row_count}; head is redundant & row_count is useless

* remove Relation trait; it does not carry its weight

* make build_query infallible

* simplify IndexSemiJoin, make it slightly less branchy

* simplify try_index_join

* split IndexSemiJoin into Left & Right parts

* move get_field_pos to test code

* move test version of build_query to test code
  • Loading branch information
Centril authored Jun 3, 2024
1 parent 2b66851 commit 89aecd1
Show file tree
Hide file tree
Showing 25 changed files with 1,017 additions and 1,121 deletions.
12 changes: 3 additions & 9 deletions crates/bench/benches/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn eval(c: &mut Criterion) {
let query = compile_read_only_query(&raw.db, &tx, sql).unwrap();
let query: ExecutionSet = query.into();
let ctx = &ExecutionContext::subscribe(raw.db.address(), SlowQueryConfig::default());
b.iter(|| drop(black_box(query.eval(ctx, Protocol::Binary, &raw.db, &tx).unwrap())))
b.iter(|| drop(black_box(query.eval(ctx, Protocol::Binary, &raw.db, &tx))))
});
};

Expand Down Expand Up @@ -140,10 +140,7 @@ fn eval(c: &mut Criterion) {
let query = ExecutionSet::from_iter(query_lhs.into_iter().chain(query_rhs));
let tx = &tx.into();

b.iter(|| {
let out = query.eval_incr(ctx_incr, &raw.db, tx, &update).unwrap();
black_box(out);
})
b.iter(|| drop(black_box(query.eval_incr(ctx_incr, &raw.db, tx, &update))))
});

// To profile this benchmark for 30s
Expand All @@ -161,10 +158,7 @@ fn eval(c: &mut Criterion) {
let query: ExecutionSet = query.into();
let tx = &tx.into();

b.iter(|| {
let out = query.eval_incr(ctx_incr, &raw.db, tx, &update).unwrap();
black_box(out);
})
b.iter(|| drop(black_box(query.eval_incr(ctx_incr, &raw.db, tx, &update))));
});

// To profile this benchmark for 30s
Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/db/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ use spacetimedb_sats::AlgebraicValue;

/// Common wrapper for relational iterators that work like cursors.
pub struct TableCursor<'a> {
pub table: DbTable,
pub table: &'a DbTable,
pub iter: Iter<'a>,
}

impl<'a> TableCursor<'a> {
pub fn new(table: DbTable, iter: Iter<'a>) -> Result<Self, DBError> {
pub fn new(table: &'a DbTable, iter: Iter<'a>) -> Result<Self, DBError> {
Ok(Self { table, iter })
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,10 @@ impl<'a> Iterator for Iter<'a> {
//
// As a result, in MVCC, this branch will need to check if the `row_ref`
// also exists in the `tx_state.insert_tables` and ensure it is yielded only once.
if !self
if self
.tx_state
.map(|tx_state| tx_state.is_deleted(table_id, row_ref.pointer()))
.unwrap_or(false)
.filter(|tx_state| tx_state.is_deleted(table_id, row_ref.pointer()))
.is_none()
{
// There either are no state changes for the current tx (`None`),
// or there are, but `row_id` specifically has not been changed.
Expand Down
1 change: 0 additions & 1 deletion crates/core/src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub mod cursor;
pub mod datastore;
pub mod db_metrics;
pub mod relational_db;
Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@ pub enum NodesError {
SystemName(Box<str>),
#[error("internal db error: {0}")]
Internal(#[source] Box<DBError>),
#[error(transparent)]
BadQuery(#[from] RelationError),
#[error("invalid index type: {0}")]
BadIndexType(u8),
}
Expand Down
30 changes: 15 additions & 15 deletions crates/core/src/host/instance_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ use spacetimedb_lib::operator::OpQuery;
use spacetimedb_lib::ProductValue;
use spacetimedb_primitives::{ColId, ColListBuilder, TableId};
use spacetimedb_sats::db::def::{IndexDef, IndexType};
use spacetimedb_sats::relation::{FieldExpr, FieldName};
use spacetimedb_sats::relation::FieldName;
use spacetimedb_sats::Typespace;
use spacetimedb_vm::expr::{ColumnOp, NoInMemUsed, QueryExpr};
use spacetimedb_vm::expr::{FieldExpr, FieldOp, NoInMemUsed, QueryExpr};

#[derive(Clone)]
pub struct InstanceEnv {
Expand Down Expand Up @@ -320,27 +320,27 @@ impl InstanceEnv {
) -> Result<Vec<Box<[u8]>>, NodesError> {
use spacetimedb_lib::filter;

fn filter_to_column_op(table_id: TableId, filter: filter::Expr) -> ColumnOp {
fn filter_to_column_op(table_id: TableId, filter: filter::Expr) -> FieldOp {
match filter {
filter::Expr::Cmp(filter::Cmp {
op,
args: CmpArgs { lhs_field, rhs },
}) => ColumnOp::Cmp {
}) => FieldOp::Cmp {
op: OpQuery::Cmp(op),
lhs: Box::new(ColumnOp::Field(FieldExpr::Name(FieldName::new(
lhs: Box::new(FieldOp::Field(FieldExpr::Name(FieldName::new(
table_id,
lhs_field.into(),
)))),
rhs: Box::new(ColumnOp::Field(match rhs {
rhs: Box::new(FieldOp::Field(match rhs {
filter::Rhs::Field(rhs_field) => FieldExpr::Name(FieldName::new(table_id, rhs_field.into())),
filter::Rhs::Value(rhs_value) => FieldExpr::Value(rhs_value),
})),
},
filter::Expr::Logic(filter::Logic { lhs, op, rhs }) => ColumnOp::Cmp {
op: OpQuery::Logic(op),
lhs: Box::new(filter_to_column_op(table_id, *lhs)),
rhs: Box::new(filter_to_column_op(table_id, *rhs)),
},
filter::Expr::Logic(filter::Logic { lhs, op, rhs }) => FieldOp::new(
OpQuery::Logic(op),
filter_to_column_op(table_id, *lhs),
filter_to_column_op(table_id, *rhs),
),
filter::Expr::Unary(_) => todo!("unary operations are not yet supported"),
}
}
Expand All @@ -363,19 +363,19 @@ impl InstanceEnv {

// TODO(Centril): consider caching from `filter: &[u8] -> query: QueryExpr`.
let query = QueryExpr::new(schema.as_ref())
.with_select(filter_to_column_op(table_id, filter))
.with_select(filter_to_column_op(table_id, filter))?
.optimize(&|table_id, table_name| stdb.row_count(table_id, table_name));

// TODO(Centril): Conditionally dump the `query` to a file and compare against integration test.
// Invent a system where we can make these kinds of "optimization path tests".

let tx: TxMode = tx.into();
// SQL queries can never reference `MemTable`s, so pass in an empty set.
let mut query = build_query(ctx, stdb, &tx, &query, &mut NoInMemUsed)?;
let mut query = build_query(ctx, stdb, &tx, &query, &mut NoInMemUsed);

// write all rows and flush at row boundaries.
let query_iter = std::iter::from_fn(|| query.next().transpose());
let chunks = itertools::process_results(query_iter, |it| ChunkedWriter::collect_iter(it))?;
let query_iter = std::iter::from_fn(|| query.next());
let chunks = ChunkedWriter::collect_iter(query_iter);
Ok(chunks)
}
}
Expand Down
68 changes: 35 additions & 33 deletions crates/core/src/sql/ast.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::config::ReadConfigOption;
use crate::db::relational_db::{MutTx, RelationalDB, Tx};
use crate::error::{DBError, PlanError};
use spacetimedb_data_structures::map::HashMap;
use spacetimedb_primitives::{ColList, ConstraintKind, Constraints};
use spacetimedb_data_structures::map::{HashCollectionExt as _, IntMap};
use spacetimedb_primitives::{ColId, ColList, ConstraintKind, Constraints};
use spacetimedb_sats::db::def::{ColumnDef, ConstraintDef, TableDef, TableSchema};
use spacetimedb_sats::db::error::RelationError;
use spacetimedb_sats::relation::{FieldExpr, FieldName};
use spacetimedb_sats::relation::{ColExpr, FieldName};
use spacetimedb_sats::{AlgebraicType, AlgebraicValue};
use spacetimedb_vm::errors::ErrorVm;
use spacetimedb_vm::expr::{ColumnOp, DbType, Expr};
use spacetimedb_vm::expr::{DbType, Expr, FieldExpr, FieldOp};
use spacetimedb_vm::operator::{OpCmp, OpLogic, OpQuery};
use spacetimedb_vm::ops::parse::{parse, parse_simple_enum};
use sqlparser::ast::{
Expand Down Expand Up @@ -103,12 +103,12 @@ pub enum Column {
/// The list of expressions for `SELECT expr1, expr2...` determining what data to extract.
#[derive(Debug, Clone)]
pub struct Selection {
pub(crate) clause: ColumnOp,
pub(crate) clause: FieldOp,
}

impl Selection {
pub fn with_cmp(op: OpQuery, lhs: ColumnOp, rhs: ColumnOp) -> Self {
let cmp = ColumnOp::new(op, lhs, rhs);
pub fn with_cmp(op: OpQuery, lhs: FieldOp, rhs: FieldOp) -> Self {
let cmp = FieldOp::new(op, lhs, rhs);
Selection { clause: cmp }
}
}
Expand Down Expand Up @@ -257,25 +257,25 @@ pub fn find_field<'a>(
pub enum SqlAst {
Select {
from: From,
project: Vec<Column>,
project: Box<[Column]>,
selection: Option<Selection>,
},
Insert {
table: Arc<TableSchema>,
columns: Vec<FieldName>,
values: Vec<Vec<FieldExpr>>,
columns: Box<[ColId]>,
values: Box<[Box<[ColExpr]>]>,
},
Update {
table: Arc<TableSchema>,
assignments: HashMap<FieldName, FieldExpr>,
assignments: IntMap<ColId, ColExpr>,
selection: Option<Selection>,
},
Delete {
table: Arc<TableSchema>,
selection: Option<Selection>,
},
CreateTable {
table: TableDef,
table: Box<TableDef>,
},
Drop {
name: String,
Expand Down Expand Up @@ -350,8 +350,8 @@ fn compile_expr_value<'a>(
tables: impl Clone + Iterator<Item = &'a TableSchema>,
field: Option<&'a AlgebraicType>,
of: SqlExpr,
) -> Result<ColumnOp, PlanError> {
Ok(ColumnOp::Field(match of {
) -> Result<FieldOp, PlanError> {
Ok(FieldOp::Field(match of {
SqlExpr::Identifier(name) => FieldExpr::Name(find_field(tables, &name.value)?.0),
SqlExpr::CompoundIdentifier(ident) => {
let col_name = compound_ident(&ident);
Expand All @@ -373,7 +373,7 @@ fn compile_expr_value<'a>(
SqlExpr::BinaryOp { left, op, right } => {
let (op, lhs, rhs) = compile_bin_op(tables, op, left, right)?;

return Ok(ColumnOp::new(op, lhs, rhs));
return Ok(FieldOp::new(op, lhs, rhs));
}
SqlExpr::Nested(x) => {
return compile_expr_value(tables, field, *x);
Expand All @@ -388,7 +388,7 @@ fn compile_expr_value<'a>(

fn compile_expr_field(table: &From, field: Option<&AlgebraicType>, of: SqlExpr) -> Result<FieldExpr, PlanError> {
match compile_expr_value(table.iter_tables(), field, of)? {
ColumnOp::Field(field) => Ok(field),
FieldOp::Field(field) => Ok(field),
x => Err(PlanError::Unsupported {
feature: format!("Complex expression {x} on insert..."),
}),
Expand Down Expand Up @@ -422,7 +422,7 @@ fn compile_bin_op<'a>(
op: BinaryOperator,
lhs: Box<sqlparser::ast::Expr>,
rhs: Box<sqlparser::ast::Expr>,
) -> Result<(OpQuery, ColumnOp, ColumnOp), PlanError> {
) -> Result<(OpQuery, FieldOp, FieldOp), PlanError> {
let op: OpQuery = match op {
BinaryOperator::Gt => OpCmp::Gt.into(),
BinaryOperator::Lt => OpCmp::Lt.into(),
Expand Down Expand Up @@ -532,8 +532,8 @@ fn compile_from<T: TableSchemaView>(db: &RelationalDB, tx: &T, from: &[TableWith
let tables = base.iter_tables().chain([&*join]);
let expr = compile_expr_value(tables, None, x.clone())?;
match expr {
ColumnOp::Field(_) => {}
ColumnOp::Cmp { op, lhs, rhs } => {
FieldOp::Field(_) => {}
FieldOp::Cmp { op, lhs, rhs } => {
let op = match op {
OpQuery::Cmp(op) => op,
OpQuery::Logic(op) => {
Expand All @@ -543,7 +543,7 @@ fn compile_from<T: TableSchemaView>(db: &RelationalDB, tx: &T, from: &[TableWith
}
};
let (lhs, rhs) = match (*lhs, *rhs) {
(ColumnOp::Field(FieldExpr::Name(lhs)), ColumnOp::Field(FieldExpr::Name(rhs))) => {
(FieldOp::Field(FieldExpr::Name(lhs)), FieldOp::Field(FieldExpr::Name(rhs))) => {
(lhs, rhs)
}
(lhs, rhs) => {
Expand Down Expand Up @@ -597,7 +597,7 @@ fn compile_select_item(from: &From, select_item: SelectItem) -> Result<Column, P
sqlparser::ast::Expr::Value(_) => {
let value = compile_expr_value(from.iter_tables(), None, expr)?;
match value {
ColumnOp::Field(value) => match value {
FieldOp::Field(value) => match value {
FieldExpr::Name(_) => Err(PlanError::Unsupported {
feature: "Should not be an identifier in Expr::Value".to_string(),
}),
Expand Down Expand Up @@ -626,11 +626,13 @@ fn compile_select_item(from: &From, select_item: SelectItem) -> Result<Column, P
/// Compiles the `SELECT ...` clause
fn compile_select<T: TableSchemaView>(db: &RelationalDB, tx: &T, select: Select) -> Result<SqlAst, PlanError> {
let from = compile_from(db, tx, &select.from)?;

// SELECT ...
let mut project = Vec::with_capacity(select.projection.len());
for select_item in select.projection {
project.push(compile_select_item(&from, select_item)?);
}
let project = project.into();

let selection = compile_where(&from, select.selection)?;

Expand Down Expand Up @@ -711,21 +713,21 @@ fn compile_insert<T: TableSchemaView>(
.map(|x| {
table
.find_field(&format!("{}.{}", &table.root.table_name, x))
.map(|(f, _)| f)
.map(|(f, _)| f.col)
})
.collect::<Result<Vec<_>, _>>()?;
.collect::<Result<Box<[_]>, _>>()?;

let mut values = Vec::with_capacity(data.rows.len());

for x in &data.rows {
let mut row = Vec::with_capacity(x.len());
for (pos, v) in x.iter().enumerate() {
let field_ty = table.root.get_column(pos).map(|col| &col.col_type);
row.push(compile_expr_field(&table, field_ty, v.clone())?);
row.push(compile_expr_field(&table, field_ty, v.clone())?.strip_table());
}

values.push(row);
values.push(row.into());
}
let values = values.into();

Ok(SqlAst::Insert {
table: table.root,
columns,
Expand All @@ -744,19 +746,19 @@ fn compile_update<T: TableSchemaView>(
let table = From::new(tx.find_table(db, table)?);
let selection = compile_where(&table, selection)?;

let mut x = HashMap::with_capacity(assignments.len());

let mut assigns = IntMap::with_capacity(assignments.len());
for col in assignments {
let name: String = col.id.iter().map(|x| x.to_string()).collect();
let (field_name, field_ty) = table.find_field(&name)?;
let col_id = field_name.col;

let value = compile_expr_field(&table, Some(field_ty), col.value)?;
x.insert(field_name, value);
let value = compile_expr_field(&table, Some(field_ty), col.value)?.strip_table();
assigns.insert(col_id, value);
}

Ok(SqlAst::Update {
table: table.root,
assignments: x,
assignments: assigns,
selection,
})
}
Expand Down Expand Up @@ -916,7 +918,7 @@ fn compile_create_table(table: Table, cols: Vec<SqlColumnDef>) -> Result<SqlAst,
});
}

let table = TableDef::new(table.name, columns).with_constraints(constraints);
let table = Box::new(TableDef::new(table.name, columns).with_constraints(constraints));

Ok(SqlAst::CreateTable { table })
}
Expand Down
Loading

3 comments on commit 89aecd1

@github-actions
Copy link

@github-actions github-actions bot commented on 89aecd1 Jun 3, 2024

Choose a reason for hiding this comment

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

Bot test failed. Please check the workflow run for details.

@github-actions
Copy link

@github-actions github-actions bot commented on 89aecd1 Jun 3, 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 6079 6059 0.33% 6943 6947 -0.06%
sqlite 5676 5676 0.00% 6084 6080 0.07%

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 79527 79533 -0.01% 79919 80143 -0.28%
stdb_raw u32_u64_str no_index 64 128 2 string 121814 121820 -0.00% 122452 122626 -0.14%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 24312 24278 0.14% 24540 24572 -0.13%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 25351 25316 0.14% 25673 25786 -0.44%
sqlite u32_u64_str no_index 64 128 2 string 143669 143663 0.00% 145345 144997 0.24%
sqlite u32_u64_str no_index 64 128 1 u64 123010 123004 0.00% 124428 124098 0.27%
sqlite u32_u64_str btree_each_column 64 128 2 string 133532 133526 0.00% 135302 135154 0.11%
sqlite u32_u64_str btree_each_column 64 128 1 u64 130327 130321 0.00% 131927 131733 0.15%

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 879565 889423 -1.11% 925491 938535 -1.39%
stdb_raw u32_u64_str btree_each_column 64 128 1009455 1017057 -0.75% 1034995 1043621 -0.83%
sqlite u32_u64_str unique_0 64 128 398423 398417 0.00% 417667 415043 0.63%
sqlite u32_u64_str btree_each_column 64 128 971496 971490 0.00% 1017504 1012904 0.45%

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 152347 152226 0.08% 152423 152264 0.10%
stdb_raw u32_u64_str unique_0 64 16330 16209 0.75% 16402 16247 0.95%
sqlite u32_u64_str unique_0 1024 1046895 1046901 -0.00% 1050255 1050369 -0.01%
sqlite u32_u64_str unique_0 64 75041 75047 -0.01% 76169 76283 -0.15%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47438 47438 0.00% 50124 50022 0.20%
64 bsatn 25717 25717 0.00% 27961 27961 0.00%
16 bsatn 8118 8118 0.00% 9444 9444 0.00%
16 json 12142 12142 0.00% 14114 14012 0.73%

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 20276251 20428673 -0.75% 20810763 21006193 -0.93%
stdb_raw u32_u64_str unique_0 64 128 1304074 1313206 -0.70% 1374626 1386254 -0.84%
sqlite u32_u64_str unique_0 1024 1024 1802096 1802084 0.00% 1811300 1811124 0.01%
sqlite u32_u64_str unique_0 64 128 128626 128620 0.00% 131364 131420 -0.04%
On-disk benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 6444 6424 0.31% 7306 7332 -0.35%
sqlite 5718 5718 0.00% 6160 6152 0.13%

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 79892 79898 -0.01% 80396 80548 -0.19%
stdb_raw u32_u64_str no_index 64 128 2 string 122158 122164 -0.00% 123072 123170 -0.08%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 24677 24643 0.14% 25017 24989 0.11%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 25715 25682 0.13% 26053 26200 -0.56%
sqlite u32_u64_str no_index 64 128 1 u64 124931 124925 0.00% 126573 126363 0.17%
sqlite u32_u64_str no_index 64 128 2 string 145590 145584 0.00% 147462 147254 0.14%
sqlite u32_u64_str btree_each_column 64 128 1 u64 132423 132417 0.00% 134345 134171 0.13%
sqlite u32_u64_str btree_each_column 64 128 2 string 135582 135576 0.00% 137686 137438 0.18%

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 829878 840012 -1.21% 843874 857142 -1.55%
stdb_raw u32_u64_str btree_each_column 64 128 957266 966087 -0.91% 981466 991809 -1.04%
sqlite u32_u64_str unique_0 64 128 415971 415965 0.00% 434579 432011 0.59%
sqlite u32_u64_str btree_each_column 64 128 1022071 1022065 0.00% 1066851 1061881 0.47%

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 152712 152591 0.08% 152780 152693 0.06%
stdb_raw u32_u64_str unique_0 64 16695 16574 0.73% 16763 16676 0.52%
sqlite u32_u64_str unique_0 1024 1049963 1049963 0.00% 1053699 1053637 0.01%
sqlite u32_u64_str unique_0 64 76813 76813 0.00% 78081 78015 0.08%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47438 47438 0.00% 50124 50022 0.20%
64 bsatn 25717 25717 0.00% 27961 27961 0.00%
16 bsatn 8118 8118 0.00% 9444 9444 0.00%
16 json 12142 12142 0.00% 14114 14012 0.73%

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 19230430 19381296 -0.78% 19847632 20032318 -0.92%
stdb_raw u32_u64_str unique_0 64 128 1247812 1256526 -0.69% 1285868 1296950 -0.85%
sqlite u32_u64_str unique_0 1024 1024 1809886 1809880 0.00% 1818534 1818516 0.00%
sqlite u32_u64_str unique_0 64 128 132774 132768 0.00% 135768 135720 0.04%

@github-actions
Copy link

@github-actions github-actions bot commented on 89aecd1 Jun 3, 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 💿 432.3±1.99ns 426.4±2.69ns - -
sqlite 🧠 424.7±1.85ns 422.0±3.19ns - -
stdb_raw 💿 721.1±0.98ns 754.7±1.07ns - -
stdb_raw 🧠 690.9±1.25ns 723.8±1.18ns - -

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 521.1±0.63µs 518.4±1.08µs 1918 tx/sec 1929 tx/sec
sqlite 💿 u32_u64_str unique_0 2048 256 138.5±0.58µs 137.5±0.35µs 7.1 Ktx/sec 7.1 Ktx/sec
sqlite 💿 u32_u64_u64 btree_each_column 2048 256 430.5±25.06µs 422.3±0.46µs 2.3 Ktx/sec 2.3 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 2048 256 123.6±0.59µs 123.8±0.36µs 7.9 Ktx/sec 7.9 Ktx/sec
sqlite 🧠 u32_u64_str btree_each_column 2048 256 453.1±0.78µs 451.0±2.10µs 2.2 Ktx/sec 2.2 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 2048 256 124.2±0.51µs 123.8±0.34µs 7.9 Ktx/sec 7.9 Ktx/sec
sqlite 🧠 u32_u64_u64 btree_each_column 2048 256 368.8±0.62µs 364.7±0.85µs 2.6 Ktx/sec 2.7 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 2048 256 107.3±0.62µs 105.5±0.59µs 9.1 Ktx/sec 9.3 Ktx/sec
stdb_raw 💿 u32_u64_str btree_each_column 2048 256 486.7±11.65µs 564.5±55.68µs 2.0 Ktx/sec 1771 tx/sec
stdb_raw 💿 u32_u64_str unique_0 2048 256 409.1±13.60µs 437.6±50.96µs 2.4 Ktx/sec 2.2 Ktx/sec
stdb_raw 💿 u32_u64_u64 btree_each_column 2048 256 362.4±7.14µs 368.9±13.81µs 2.7 Ktx/sec 2.6 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 2048 256 340.0±10.51µs 347.2±7.84µs 2.9 Ktx/sec 2.8 Ktx/sec
stdb_raw 🧠 u32_u64_str btree_each_column 2048 256 312.4±0.56µs 312.9±0.18µs 3.1 Ktx/sec 3.1 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 2048 256 242.9±0.12µs 242.8±0.19µs 4.0 Ktx/sec 4.0 Ktx/sec
stdb_raw 🧠 u32_u64_u64 btree_each_column 2048 256 236.5±0.12µs 246.6±0.21µs 4.1 Ktx/sec 4.0 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 2048 256 213.0±0.13µs 218.0±0.27µs 4.6 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 21.4±0.13µs 20.4±0.10µs 45.6 Ktx/sec 47.8 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 19.3±0.07µs 19.4±0.29µs 50.5 Ktx/sec 50.3 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 20.3±0.16µs 19.3±0.33µs 48.0 Ktx/sec 50.7 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 18.3±0.04µs 18.0±0.20µs 53.3 Ktx/sec 54.1 Ktx/sec
stdb_raw 💿 u32_u64_str unique_0 4.8±0.00µs 4.8±0.00µs 202.7 Ktx/sec 201.7 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 4.7±0.00µs 4.7±0.00µs 206.9 Ktx/sec 206.1 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 4.8±0.00µs 4.8±0.00µs 203.6 Ktx/sec 202.6 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 4.7±0.00µs 4.7±0.00µs 208.3 Ktx/sec 207.3 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 66.7±0.18µs 69.1±0.95µs 14.6 Ktx/sec 14.1 Ktx/sec
sqlite 💿 u64 index 2048 256 63.3±0.30µs 64.4±0.56µs 15.4 Ktx/sec 15.2 Ktx/sec
sqlite 🧠 string index 2048 256 64.3±0.25µs 66.0±0.38µs 15.2 Ktx/sec 14.8 Ktx/sec
sqlite 🧠 u64 index 2048 256 59.0±0.15µs 60.0±0.16µs 16.5 Ktx/sec 16.3 Ktx/sec
stdb_raw 💿 string index 2048 256 5.1±0.00µs 5.2±0.00µs 189.8 Ktx/sec 188.3 Ktx/sec
stdb_raw 💿 u64 index 2048 256 5.1±0.00µs 5.1±0.00µs 192.3 Ktx/sec 191.0 Ktx/sec
stdb_raw 🧠 string index 2048 256 5.1±0.00µs 5.1±0.00µs 190.9 Ktx/sec 189.7 Ktx/sec
stdb_raw 🧠 u64 index 2048 256 5.1±0.00µs 5.1±0.00µs 193.4 Ktx/sec 192.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.7±0.00µs 3.7±0.01µs 25.8 Mtx/sec 25.6 Mtx/sec
u32_u64_str bflatn_to_bsatn_slow_path 100 3.5±0.00µs 3.5±0.01µs 27.4 Mtx/sec 27.2 Mtx/sec
u32_u64_str bsatn 100 2.4±0.01µs 2.4±0.07µs 40.4 Mtx/sec 39.6 Mtx/sec
u32_u64_str json 100 5.1±0.04µs 4.9±0.02µs 18.8 Mtx/sec 19.6 Mtx/sec
u32_u64_str product_value 100 1015.3±0.55ns 1015.3±0.80ns 93.9 Mtx/sec 93.9 Mtx/sec
u32_u64_u64 bflatn_to_bsatn_fast_path 100 1108.0±23.09ns 1225.8±10.24ns 86.1 Mtx/sec 77.8 Mtx/sec
u32_u64_u64 bflatn_to_bsatn_slow_path 100 2.9±0.00µs 2.9±0.01µs 33.0 Mtx/sec 33.2 Mtx/sec
u32_u64_u64 bsatn 100 1647.7±3.90ns 1741.4±30.31ns 57.9 Mtx/sec 54.8 Mtx/sec
u32_u64_u64 json 100 3.2±0.03µs 3.0±0.01µs 30.2 Mtx/sec 31.5 Mtx/sec
u32_u64_u64 product_value 100 1008.5±0.60ns 1008.2±0.57ns 94.6 Mtx/sec 94.6 Mtx/sec
u64_u64_u32 bflatn_to_bsatn_fast_path 100 889.4±3.92ns 946.8±1.09ns 107.2 Mtx/sec 100.7 Mtx/sec
u64_u64_u32 bflatn_to_bsatn_slow_path 100 2.9±0.00µs 2.9±0.01µs 33.0 Mtx/sec 33.1 Mtx/sec
u64_u64_u32 bsatn 100 1648.8±31.73ns 1702.3±27.11ns 57.8 Mtx/sec 56.0 Mtx/sec
u64_u64_u32 json 100 3.1±0.01µs 3.2±0.12µs 31.1 Mtx/sec 29.5 Mtx/sec
u64_u64_u32 product_value 100 1010.6±0.46ns 1010.7±0.68ns 94.4 Mtx/sec 94.4 Mtx/sec

stdb_module_large_arguments

arg size new latency old latency new throughput old throughput
64KiB 89.3±6.34µs 87.6±6.18µs - -

stdb_module_print_bulk

line count new latency old latency new throughput old throughput
1 40.5±5.91µs 39.4±6.60µs - -
100 349.0±7.83µs 353.2±11.49µs - -
1000 2.9±0.26ms 2.0±0.39ms - -

remaining

name new latency old latency new throughput old throughput
sqlite/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 48.2±0.41µs 46.3±0.24µs 20.3 Ktx/sec 21.1 Ktx/sec
sqlite/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 43.8±8.88µs 40.8±0.08µs 22.3 Ktx/sec 24.0 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 41.1±0.04µs 39.2±0.12µs 23.7 Ktx/sec 24.9 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 35.3±0.06µs 35.7±0.16µs 27.7 Ktx/sec 27.4 Ktx/sec
stdb_module/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 1272.7±18.63µs 1275.4±11.39µs 785 tx/sec 784 tx/sec
stdb_module/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 954.7±12.40µs 958.9±9.17µs 1047 tx/sec 1042 tx/sec
stdb_raw/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 552.7±23.11µs 630.5±25.38µs 1809 tx/sec 1586 tx/sec
stdb_raw/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 497.1±16.60µs 489.6±7.93µs 2011 tx/sec 2042 tx/sec
stdb_raw/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 387.5±0.37µs 381.9±0.29µs 2.5 Ktx/sec 2.6 Ktx/sec
stdb_raw/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 346.3±0.30µs 342.1±0.54µs 2.8 Ktx/sec 2.9 Ktx/sec

Please sign in to comment.