Skip to content

Commit

Permalink
Restrict multi-col index scans to = (OpCmp::Eq) on all columns (#…
Browse files Browse the repository at this point in the history
…1316)

* add test test_multi_column_two_ranges, which should fail but doesn't

* restrict multi-col index scans to OpCmp::Eq
  • Loading branch information
Centril authored Jun 1, 2024
1 parent 6384452 commit 74bcecd
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 67 deletions.
18 changes: 18 additions & 0 deletions crates/core/src/sql/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,4 +721,22 @@ SELECT * FROM inventory",
assert!(result[0].data.is_empty());
Ok(())
}

#[test]
fn test_multi_column_two_ranges() -> ResultTest<()> {
let db = TestDB::durable()?;

// Create table [test] with index on [a, b]
let schema = &[("a", AlgebraicType::U8), ("b", AlgebraicType::U8)];
let table_id = db.create_table_for_test_multi_column("test", schema, col_list![0, 1])?;
let row = product![4u8, 8u8];
db.with_auto_commit(&ExecutionContext::default(), |tx| db.insert(tx, table_id, row.clone()))?;

let result = run_for_testing(&db, "select * from test where a >= 3 and a <= 5 and b >= 3 and b <= 5")?;

let result = result.first().unwrap().clone();
assert_eq!(result.data, []);

Ok(())
}
}
171 changes: 104 additions & 67 deletions crates/vm/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,10 +917,8 @@ type FieldsIndexed = HashSet<(FieldName, OpCmp)>;
/// i.e., both `WHERE a = 1 AND b = 2`
/// and `WHERE b = 2 AND a = 1` are valid.
///
/// - Queries against multi-col indices must use the same operator in their constraints.
/// - Queries against multi-col indices must use `=`, for now, in their constraints.
/// Otherwise, the index cannot be used.
/// That is, for `WHERE a < 1, b < 3`, we can use `ScanOrIndex::Index(Lt, [a, b], (1, 3))`,
/// whereas for `WHERE a < 1, b != 3`, we cannot.
///
/// - The use of multiple tables could generate redundant/duplicate operations like
/// `[ScanOrIndex::Index(a = 1), ScanOrIndex::Index(a = 1), ScanOrIndex::Scan(a = 1)]`.
Expand Down Expand Up @@ -953,11 +951,6 @@ type FieldsIndexed = HashSet<(FieldName, OpCmp)>;
/// but rather, `select_best_index`
/// would give us two separate `IndexScan`s.
/// However, the upper layers of `QueryExpr` building will convert both of those into `Select`s.
/// In the case of `SELECT * FROM students WHERE age > 18 AND height > 180`
/// we would generate a single `IndexScan((age, height) > (18, 180))`.
/// However, and depending on the table data, this might not be efficient,
/// whereas `age = 18 AND height > 180` might.
/// TODO: Revisit this to see if we want to restrict this or use statistics.
fn select_best_index<'a>(
fields_indexed: &mut FieldsIndexed,
header: &'a Header,
Expand All @@ -982,57 +975,61 @@ fn select_best_index<'a>(
let mut fields_map = BTreeMap::<_, SmallVec<[_; 1]>>::new();
extract_fields(ops, header, &mut fields_map, &mut found);

// Go through each operator and index,
// Go through each index,
// consuming all field constraints that can be served by an index.
//
// NOTE: We do not consider `OpCmp::NotEq` at the moment
// since those are typically not answered using an index.
for (col_list, cmp) in [OpCmp::Eq, OpCmp::Lt, OpCmp::LtEq, OpCmp::Gt, OpCmp::GtEq]
.into_iter()
.flat_map(|cmp| indices.iter().map(move |cl| (*cl, cmp)))
{
for col_list in indices {
// (1) No fields left? We're done.
if fields_map.is_empty() {
break;
}

if col_list.is_singleton() {
// For a single column index,
// we want to avoid the `ProductValue` indirection of below.
for FieldValue { cmp, value, field, .. } in fields_map.remove(&(col_list.head(), cmp)).into_iter().flatten()
{
found.push(make_index_arg(cmp, col_list, value.clone()));
fields_indexed.insert((field, cmp));
// Go through each operator.
// NOTE: We do not consider `OpCmp::NotEq` at the moment
// since those are typically not answered using an index.
for cmp in [OpCmp::Eq, OpCmp::Lt, OpCmp::LtEq, OpCmp::Gt, OpCmp::GtEq] {
// For a single column index,
// we want to avoid the `ProductValue` indirection of below.
for FieldValue { cmp, value, field, .. } in
fields_map.remove(&(col_list.head(), cmp)).into_iter().flatten()
{
found.push(make_index_arg(cmp, col_list, value.clone()));
fields_indexed.insert((field, cmp));
}
}
} else if col_list
.iter()
// (2) Ensure that every col has a field.
.all(|col| fields_map.get(&(col, cmp)).filter(|fs| !fs.is_empty()).is_some())
{
// We've ensured `col_list ⊆ columns_of(field_map(cmp))`.
// Construct the value to compare against.
let mut elems = Vec::with_capacity(col_list.len() as usize);
for col in col_list.iter() {
// Retrieve the field for this (col, cmp) key.
// Remove the map entry if the list is empty now.
let Entry::Occupied(mut entry) = fields_map.entry((col, cmp)) else {
// We ensured in (2) that the map is occupied for `(col, cmp)`.
unreachable!()
};
let fields = entry.get_mut();
// We ensured in (2) that `fields` is non-empty.
let field = fields.pop().unwrap();
if fields.is_empty() {
// Remove the entry so that (1) works.
entry.remove();
} else {
// We have a multi column index.
// Try to fit constraints `c_0 = v_0, ..., c_n = v_n` to this index.
//
// For the time being, we restrict multi-col index scans to `=` only.
// This is what our infrastructure is set-up to handle soundly.
// To extend this support to ranges requires deeper changes.
// TODO(Centril, 2024-05-30): extend this support to ranges.
let cmp = OpCmp::Eq;

// Compute the minimum number of `=` constraints that every column in the index has.
let mut min_all_cols_num_eq = col_list
.iter()
.map(|col| fields_map.get(&(col, cmp)).map_or(0, |fs| fs.len()))
.min()
.unwrap_or_default();

// For all of these sets of constraints,
// construct the value to compare against.
while min_all_cols_num_eq > 0 {
let mut elems = Vec::with_capacity(col_list.len() as usize);
for col in col_list.iter() {
// Cannot panic as `min_all_cols_num_eq > 0`.
let field = pop_multimap(&mut fields_map, (col, cmp)).unwrap();
fields_indexed.insert((field.field, cmp));
// Add the field value to the product value.
elems.push(field.value.clone());
}

// Add the field value to the product value.
elems.push(field.value.clone());
fields_indexed.insert((field.field, cmp));
// Construct the index scan.
let value = AlgebraicValue::product(elems);
found.push(make_index_arg(cmp, col_list, value));
min_all_cols_num_eq -= 1;
}
let value = AlgebraicValue::product(elems);
found.push(make_index_arg(cmp, col_list, value));
}
}

Expand All @@ -1047,6 +1044,20 @@ fn select_best_index<'a>(
found
}

/// Pop an element from `map[key]` in the multimap `map`,
/// removing the entry entirely if there are no more elements left after popping.
fn pop_multimap<K: Ord, V, const N: usize>(map: &mut BTreeMap<K, SmallVec<[V; N]>>, key: K) -> Option<V> {
let Entry::Occupied(mut entry) = map.entry(key) else {
return None;
};
let fields = entry.get_mut();
let val = fields.pop();
if fields.is_empty() {
entry.remove();
}
val
}

/// Extracts `name = val` when `lhs` is a field that exists and `rhs` is a value.
fn ext_field_val<'a>(
header: &'a Header,
Expand Down Expand Up @@ -2169,13 +2180,13 @@ mod tests {
"t1".into(),
cols.to_vec(),
vec![
//Index a
// Index a
(a.into(), Constraints::primary_key()),
//Index b
// Index b
(b.into(), Constraints::indexed()),
//Index b + c
// Index b + c
(col_list![b, c], Constraints::unique()),
//Index a + b + c + d
// Index a + b + c + d
(col_list![a, b, c, d], Constraints::indexed()),
],
);
Expand Down Expand Up @@ -2331,13 +2342,13 @@ mod tests {
make_index_arg(cmp, columns, val.clone())
};

// Same field indexed
// `a > va AND a < vb` => `[index(a), index(a)]`
assert_eq!(
select_best_index(&[(OpCmp::Gt, col_a, &val_a), (OpCmp::Lt, col_a, &val_b)]),
[idx(OpCmp::Lt, &[col_a], &val_b), idx(OpCmp::Gt, &[col_a], &val_a)].into()
);

// Same field scan
// `d > vd AND d < vb` => `[scan(d), scan(d)]`
assert_eq!(
select_best_index(&[(OpCmp::Gt, col_d, &val_d), (OpCmp::Lt, col_d, &val_b)]),
[
Expand All @@ -2346,31 +2357,30 @@ mod tests {
]
.into()
);
// One indexed other scan

// `b > vb AND c < vc` => `[index(b), scan(c)]`.
assert_eq!(
select_best_index(&[(OpCmp::Gt, col_b, &val_b), (OpCmp::Lt, col_c, &val_c)]),
[idx(OpCmp::Gt, &[col_b], &val_b), scan(&arena, OpCmp::Lt, col_c, &val_c)].into()
);

// 1 multi-indexed 1 index
// `b = vb AND a >= va AND c = vc` => `[index(b, c), index(a)]`
let idx_bc = idx(
OpCmp::Eq,
&[col_b, col_c],
&product![val_b.clone(), val_c.clone()].into(),
);
assert_eq!(
//
select_best_index(&[
(OpCmp::Eq, col_b, &val_b),
(OpCmp::GtEq, col_a, &val_a),
(OpCmp::Eq, col_c, &val_c),
]),
[
idx(
OpCmp::Eq,
&[col_b, col_c],
&product![val_b.clone(), val_c.clone()].into(),
),
idx(OpCmp::GtEq, &[col_a], &val_a),
]
.into()
[idx_bc.clone(), idx(OpCmp::GtEq, &[col_a], &val_a),].into()
);

// 1 indexed 2 scan
// `b > vb AND a = va AND c = vc` => `[index(a), index(b), scan(c)]`
assert_eq!(
select_best_index(&[
(OpCmp::Gt, col_b, &val_b),
Expand All @@ -2384,6 +2394,33 @@ mod tests {
]
.into()
);

// `a = va AND b = vb AND c = vc AND d > vd` => `[index(b, c), index(a), scan(d)]`
assert_eq!(
select_best_index(&[
(OpCmp::Eq, col_a, &val_a),
(OpCmp::Eq, col_b, &val_b),
(OpCmp::Eq, col_c, &val_c),
(OpCmp::Gt, col_d, &val_d),
]),
[
idx_bc.clone(),
idx(OpCmp::Eq, &[col_a], &val_a),
scan(&arena, OpCmp::Gt, col_d, &val_d),
]
.into()
);

// `b = vb AND c = vc AND b = vb AND c = vc` => `[index(b, c), index(b, c)]`
assert_eq!(
select_best_index(&[
(OpCmp::Eq, col_b, &val_b),
(OpCmp::Eq, col_c, &val_c),
(OpCmp::Eq, col_b, &val_b),
(OpCmp::Eq, col_c, &val_c),
]),
[idx_bc.clone(), idx_bc].into()
);
}

#[test]
Expand Down

3 comments on commit 74bcecd

@github-actions
Copy link

@github-actions github-actions bot commented on 74bcecd Jun 1, 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 74bcecd Jun 1, 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 💿 419.4±2.28ns 432.1±2.76ns - -
sqlite 🧠 409.7±1.96ns 421.6±2.59ns - -
stdb_raw 💿 715.7±1.07ns 720.9±1.00ns - -
stdb_raw 🧠 684.4±3.48ns 689.9±0.76ns - -

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.6±35.70µs 507.7±1.51µs 1917 tx/sec 1969 tx/sec
sqlite 💿 u32_u64_str unique_0 2048 256 136.6±0.54µs 134.2±0.54µs 7.1 Ktx/sec 7.3 Ktx/sec
sqlite 💿 u32_u64_u64 btree_each_column 2048 256 415.4±0.31µs 411.2±0.89µs 2.4 Ktx/sec 2.4 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 2048 256 123.8±0.54µs 121.8±0.48µs 7.9 Ktx/sec 8.0 Ktx/sec
sqlite 🧠 u32_u64_str btree_each_column 2048 256 447.5±0.98µs 443.5±0.40µs 2.2 Ktx/sec 2.2 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 2048 256 122.4±0.59µs 119.1±0.19µs 8.0 Ktx/sec 8.2 Ktx/sec
sqlite 🧠 u32_u64_u64 btree_each_column 2048 256 364.8±0.40µs 359.4±0.41µs 2.7 Ktx/sec 2.7 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 2048 256 104.9±0.28µs 105.1±0.62µs 9.3 Ktx/sec 9.3 Ktx/sec
stdb_raw 💿 u32_u64_str btree_each_column 2048 256 581.1±37.96µs 567.7±49.03µs 1721 tx/sec 1761 tx/sec
stdb_raw 💿 u32_u64_str unique_0 2048 256 470.4±22.35µs 491.3±20.75µs 2.1 Ktx/sec 2035 tx/sec
stdb_raw 💿 u32_u64_u64 btree_each_column 2048 256 333.9±5.57µs 372.3±6.64µs 2.9 Ktx/sec 2.6 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 2048 256 305.8±9.73µs 334.7±10.14µs 3.2 Ktx/sec 2.9 Ktx/sec
stdb_raw 🧠 u32_u64_str btree_each_column 2048 256 319.0±0.20µs 313.6±0.22µs 3.1 Ktx/sec 3.1 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 2048 256 241.5±0.13µs 241.0±0.21µs 4.0 Ktx/sec 4.1 Ktx/sec
stdb_raw 🧠 u32_u64_u64 btree_each_column 2048 256 241.1±0.26µs 243.7±0.08µs 4.1 Ktx/sec 4.0 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 2048 256 212.6±0.16µs 213.2±0.23µs 4.6 Ktx/sec 4.6 Ktx/sec

iterate

db on disk schema indices new latency old latency new throughput old throughput
sqlite 💿 u32_u64_str unique_0 21.3±0.23µs 19.3±0.09µs 45.8 Ktx/sec 50.5 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 20.1±0.12µs 17.4±0.06µs 48.7 Ktx/sec 56.2 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 19.9±0.26µs 18.4±0.18µs 49.0 Ktx/sec 53.1 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 18.1±0.21µs 16.2±0.16µs 53.9 Ktx/sec 60.2 Ktx/sec
stdb_raw 💿 u32_u64_str unique_0 3.8±0.00µs 4.8±0.00µs 255.6 Ktx/sec 203.8 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 3.7±0.00µs 4.7±0.00µs 263.3 Ktx/sec 207.9 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 3.8±0.00µs 4.8±0.00µs 258.8 Ktx/sec 204.5 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 3.7±0.00µs 4.7±0.00µs 265.5 Ktx/sec 209.2 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 67.5±0.18µs 64.5±0.16µs 14.5 Ktx/sec 15.1 Ktx/sec
sqlite 💿 u64 index 2048 256 64.3±0.22µs 62.1±0.19µs 15.2 Ktx/sec 15.7 Ktx/sec
sqlite 🧠 string index 2048 256 65.1±0.18µs 62.5±0.13µs 15.0 Ktx/sec 15.6 Ktx/sec
sqlite 🧠 u64 index 2048 256 59.8±0.45µs 57.9±0.08µs 16.3 Ktx/sec 16.9 Ktx/sec
stdb_raw 💿 string index 2048 256 5.1±0.00µs 5.1±0.00µs 192.4 Ktx/sec 192.5 Ktx/sec
stdb_raw 💿 u64 index 2048 256 5.0±0.00µs 5.0±0.00µs 195.2 Ktx/sec 195.3 Ktx/sec
stdb_raw 🧠 string index 2048 256 5.0±0.00µs 5.0±0.00µs 193.8 Ktx/sec 193.8 Ktx/sec
stdb_raw 🧠 u64 index 2048 256 5.0±0.00µs 5.0±0.00µs 196.5 Ktx/sec 196.5 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.00µs 25.9 Mtx/sec 25.9 Mtx/sec
u32_u64_str bflatn_to_bsatn_slow_path 100 3.5±0.11µs 3.5±0.01µs 26.9 Mtx/sec 27.0 Mtx/sec
u32_u64_str bsatn 100 2.5±0.00µs 2.4±0.00µs 38.0 Mtx/sec 39.4 Mtx/sec
u32_u64_str json 100 4.8±0.13µs 4.9±0.03µs 19.9 Mtx/sec 19.3 Mtx/sec
u32_u64_str product_value 100 1015.6±0.51ns 1014.3±0.37ns 93.9 Mtx/sec 94.0 Mtx/sec
u32_u64_u64 bflatn_to_bsatn_fast_path 100 1109.3±2.28ns 1137.3±33.99ns 86.0 Mtx/sec 83.9 Mtx/sec
u32_u64_u64 bflatn_to_bsatn_slow_path 100 2.9±0.00µs 2.9±0.00µs 33.0 Mtx/sec 32.9 Mtx/sec
u32_u64_u64 bsatn 100 1716.9±31.30ns 1678.8±40.74ns 55.5 Mtx/sec 56.8 Mtx/sec
u32_u64_u64 json 100 3.1±0.03µs 3.3±0.04µs 31.2 Mtx/sec 29.1 Mtx/sec
u32_u64_u64 product_value 100 1010.5±7.97ns 1009.4±0.73ns 94.4 Mtx/sec 94.5 Mtx/sec
u64_u64_u32 bflatn_to_bsatn_fast_path 100 916.1±31.63ns 887.6±3.28ns 104.1 Mtx/sec 107.4 Mtx/sec
u64_u64_u32 bflatn_to_bsatn_slow_path 100 2.9±0.00µs 2.9±0.00µs 32.9 Mtx/sec 32.6 Mtx/sec
u64_u64_u32 bsatn 100 1711.6±62.04ns 1674.4±31.63ns 55.7 Mtx/sec 57.0 Mtx/sec
u64_u64_u32 json 100 3.2±0.12µs 3.3±0.03µs 29.7 Mtx/sec 28.5 Mtx/sec
u64_u64_u32 product_value 100 1011.0±0.45ns 1010.4±0.47ns 94.3 Mtx/sec 94.4 Mtx/sec

stdb_module_large_arguments

arg size new latency old latency new throughput old throughput
64KiB 88.3±10.12µs 96.4±8.78µs - -

stdb_module_print_bulk

line count new latency old latency new throughput old throughput
1 45.7±3.59µs 39.2±4.60µs - -
100 358.7±43.07µs 362.2±82.01µs - -
1000 2.4±0.52ms 2.6±0.48ms - -

remaining

name new latency old latency new throughput old throughput
sqlite/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 45.9±0.06µs 44.2±0.03µs 21.3 Ktx/sec 22.1 Ktx/sec
sqlite/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 40.5±0.11µs 39.0±0.07µs 24.1 Ktx/sec 25.1 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 38.7±0.18µs 37.0±0.07µs 25.2 Ktx/sec 26.4 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 35.5±0.15µs 33.6±0.02µs 27.5 Ktx/sec 29.0 Ktx/sec
stdb_module/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 1268.3±17.39µs 1268.3±15.14µs 788 tx/sec 788 tx/sec
stdb_module/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 970.7±9.71µs 943.3±32.45µs 1030 tx/sec 1060 tx/sec
stdb_raw/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 611.1±47.70µs 635.9±24.40µs 1636 tx/sec 1572 tx/sec
stdb_raw/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 475.4±9.30µs 436.2±6.50µs 2.1 Ktx/sec 2.2 Ktx/sec
stdb_raw/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 379.8±0.95µs 382.2±0.28µs 2.6 Ktx/sec 2.6 Ktx/sec
stdb_raw/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 342.0±0.27µs 340.3±0.18µs 2.9 Ktx/sec 2.9 Ktx/sec

@github-actions
Copy link

@github-actions github-actions bot commented on 74bcecd Jun 1, 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 6025 6025 0.00% 6897 6897 0.00%
sqlite 5686 5686 0.00% 6174 6198 -0.39%

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 79499 79499 0.00% 79933 79909 0.03%
stdb_raw u32_u64_str no_index 64 128 2 string 121786 121786 0.00% 122354 122334 0.02%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 24244 24244 0.00% 24714 24718 -0.02%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 25285 25282 0.01% 25773 25762 0.04%
sqlite u32_u64_str no_index 64 128 2 string 143663 143663 0.00% 145201 145137 0.04%
sqlite u32_u64_str no_index 64 128 1 u64 123004 123004 0.00% 124268 124272 -0.00%
sqlite u32_u64_str btree_each_column 64 128 2 string 133526 133526 0.00% 135282 135246 0.03%
sqlite u32_u64_str btree_each_column 64 128 1 u64 130321 130321 0.00% 131891 131759 0.10%

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 889811 889537 0.03% 936439 936173 0.03%
stdb_raw u32_u64_str btree_each_column 64 128 1017434 1016934 0.05% 1042234 1041694 0.05%
sqlite u32_u64_str unique_0 64 128 398417 398417 0.00% 415277 418599 -0.79%
sqlite u32_u64_str btree_each_column 64 128 971490 971490 0.00% 1014904 1012712 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 152192 152192 0.00% 152230 152230 0.00%
stdb_raw u32_u64_str unique_0 64 16166 16175 -0.06% 16200 16209 -0.06%
sqlite u32_u64_str unique_0 1024 1046901 1046901 0.00% 1050341 1050281 0.01%
sqlite u32_u64_str unique_0 64 75047 75047 0.00% 76217 76181 0.05%

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% 50090 50090 0.00%
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% 14046 14046 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 20424814 20425481 -0.00% 20827380 20827691 -0.00%
stdb_raw u32_u64_str unique_0 64 128 1313255 1313251 0.00% 1374961 1374725 0.02%
sqlite u32_u64_str unique_0 1024 1024 1802084 1802084 0.00% 1811354 1811278 0.00%
sqlite u32_u64_str unique_0 64 128 128620 128620 0.00% 131488 131416 0.05%
On-disk benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 6390 6390 0.00% 7272 7276 -0.05%
sqlite 5718 5718 0.00% 6202 6222 -0.32%

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 79864 79864 0.00% 80362 80366 -0.00%
stdb_raw u32_u64_str no_index 64 128 2 string 122130 122130 0.00% 122822 122850 -0.02%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 24609 24609 0.00% 25155 25163 -0.03%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 25650 25649 0.00% 26230 26237 -0.03%
sqlite u32_u64_str no_index 64 128 1 u64 124925 124925 0.00% 126565 126489 0.06%
sqlite u32_u64_str no_index 64 128 2 string 145584 145584 0.00% 147502 147406 0.07%
sqlite u32_u64_str btree_each_column 64 128 1 u64 132417 132417 0.00% 134377 134285 0.07%
sqlite u32_u64_str btree_each_column 64 128 2 string 135576 135576 0.00% 137814 137574 0.17%

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 840170 840760 -0.07% 855024 855490 -0.05%
stdb_raw u32_u64_str btree_each_column 64 128 967541 966128 0.15% 990815 989710 0.11%
sqlite u32_u64_str unique_0 64 128 415965 415965 0.00% 432549 435803 -0.75%
sqlite u32_u64_str btree_each_column 64 128 1022065 1022065 0.00% 1063873 1062347 0.14%

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 152557 152557 0.00% 152631 152631 0.00%
stdb_raw u32_u64_str unique_0 64 16540 16540 0.00% 16610 16610 0.00%
sqlite u32_u64_str unique_0 1024 1049963 1049963 0.00% 1053673 1053581 0.01%
sqlite u32_u64_str unique_0 64 76813 76813 0.00% 78091 78055 0.05%

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% 50090 50090 0.00%
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% 14046 14046 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 19378247 19380496 -0.01% 19849125 19851114 -0.01%
stdb_raw u32_u64_str unique_0 64 128 1256804 1256948 -0.01% 1286158 1286250 -0.01%
sqlite u32_u64_str unique_0 1024 1024 1809880 1809880 0.00% 1818674 1818578 0.01%
sqlite u32_u64_str unique_0 64 128 132768 132768 0.00% 135888 135804 0.06%

Please sign in to comment.