Skip to content

Commit

Permalink
Make our sats<->serde translation compatible with RON (#1738)
Browse files Browse the repository at this point in the history
  • Loading branch information
coolreader18 authored Sep 27, 2024
1 parent 49712bf commit dd699c4
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 104 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ rayon = "1.8"
rayon-core = "1.11.0"
regex = "1"
reqwest = { version = "0.12", features = ["stream", "json"] }
ron = "0.8"
rusqlite = { version = "0.29.0", features = ["bundled", "column_decltype"] }
rust_decimal = { version = "1.29.1", features = ["db-tokio-postgres"] }
rustc-demangle = "0.1.21"
Expand Down
1 change: 1 addition & 0 deletions crates/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ rand.workspace = true
bytes.workspace = true
serde_json.workspace = true
insta.workspace = true
ron.workspace = true

# Also as dev-dependencies for use in _this_ crate's tests.
proptest.workspace = true
Expand Down
15 changes: 15 additions & 0 deletions crates/lib/tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ fn test_roundtrip() {
assert_eq!(&original, &result);
}

#[test]
fn test_roundtrip_ron() {
let original = Sample {
identity: Identity::__dummy(),
};

let s = value_serialize(&original);
let result: Sample = spacetimedb_sats::de::Deserialize::deserialize(ValueDeserializer::new(s)).unwrap();
assert_eq!(&original, &result);

let s = ron::to_string(&original).unwrap();
let result: Sample = ron::from_str(&s).unwrap();
assert_eq!(&original, &result);
}

#[test]
fn test_json_mappings() {
let schema = tuple([
Expand Down
176 changes: 77 additions & 99 deletions crates/sats/src/de/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ use serde::de as serde;

/// Converts any [`serde::Deserializer`] to a SATS [`Deserializer`]
/// so that Serde's data formats can be reused.
///
/// In order for successful round-trip deserialization, the `serde::Deserializer`
/// that this type wraps must support `deserialize_any()`.
pub struct SerdeDeserializer<D> {
/// A deserialization data format in Serde.
de: D,
Expand Down Expand Up @@ -46,19 +49,11 @@ impl<'de, D: serde::Deserializer<'de>> Deserializer<'de> for SerdeDeserializer<D
type Error = SerdeError<D::Error>;

fn deserialize_product<V: super::ProductVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {
self.de
.deserialize_struct("", &[], TupleVisitor { visitor })
.map_err(SerdeError)
self.de.deserialize_any(TupleVisitor { visitor }).map_err(SerdeError)
}

fn deserialize_sum<V: super::SumVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {
if visitor.is_option() && self.de.is_human_readable() {
self.de.deserialize_any(OptionVisitor { visitor }).map_err(SerdeError)
} else {
self.de
.deserialize_enum("", &[], EnumVisitor { visitor })
.map_err(SerdeError)
}
self.de.deserialize_any(EnumVisitor { visitor }).map_err(SerdeError)
}

fn deserialize_bool(self) -> Result<bool, Self::Error> {
Expand Down Expand Up @@ -267,71 +262,6 @@ impl<'de, A: serde::SeqAccess<'de>> super::SeqProductAccess<'de> for SeqTupleAcc
}
}

/// Converts a `SumVisitor` into a `serde::Visitor` for deserializing option.
struct OptionVisitor<V> {
/// The visitor to convert.
visitor: V,
}

impl<'de, V: super::SumVisitor<'de>> serde::Visitor<'de> for OptionVisitor<V> {
type Value = V::Output;

fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("option")
}

fn visit_map<A: serde::MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
self.visitor.visit_sum(SomeAccess(map)).map_err(unwrap_error)
}

fn visit_unit<E: serde::Error>(self) -> Result<Self::Value, E> {
self.visitor.visit_sum(NoneAccess(PhantomData)).map_err(unwrap_error)
}
}

/// Deserializes `some` variant of an optional value.
/// Converts Serde's map deserialization to SATS.
struct SomeAccess<A>(A);

impl<'de, A: serde::MapAccess<'de>> super::SumAccess<'de> for SomeAccess<A> {
type Error = SerdeError<A::Error>;
type Variant = Self;

fn variant<V: super::VariantVisitor>(mut self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {
self.0
.next_key_seed(VariantVisitor { visitor })
.and_then(|x| match x {
Some(x) => Ok((x, self)),
None => Err(serde::Error::custom("expected variant name")),
})
.map_err(SerdeError)
}
}
impl<'de, A: serde::MapAccess<'de>> super::VariantAccess<'de> for SomeAccess<A> {
type Error = SerdeError<A::Error>;

fn deserialize_seed<T: super::DeserializeSeed<'de>>(mut self, seed: T) -> Result<T::Output, Self::Error> {
let ret = self.0.next_value_seed(SeedWrapper(seed)).map_err(SerdeError)?;
self.0.next_key_seed(NothingVisitor).map_err(SerdeError)?;
Ok(ret)
}
}

/// Deserializes nothing, producing `!` effectively.
struct NothingVisitor;
impl<'de> serde::DeserializeSeed<'de> for NothingVisitor {
type Value = std::convert::Infallible;
fn deserialize<D: serde::Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_identifier(self)
}
}
impl serde::Visitor<'_> for NothingVisitor {
type Value = std::convert::Infallible;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("nothing")
}
}

/// Deserializes `none` variant of an optional value.
struct NoneAccess<E>(PhantomData<E>);
impl<E: super::Error> super::SumAccess<'_> for NoneAccess<E> {
Expand Down Expand Up @@ -364,29 +294,32 @@ impl<'de, V: super::SumVisitor<'de>> serde::Visitor<'de> for EnumVisitor<V> {
type Value = V::Output;

fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("enum")
match self.visitor.sum_name() {
Some(name) => write!(f, "sum type {name}"),
None => f.write_str("sum type"),
}
}

fn visit_enum<A: serde::EnumAccess<'de>>(self, access: A) -> Result<Self::Value, A::Error> {
fn visit_map<A>(self, access: A) -> Result<Self::Value, A::Error>
where
A: serde::MapAccess<'de>,
{
self.visitor.visit_sum(EnumAccess { access }).map_err(unwrap_error)
}
}

/// Converts Serde's `EnumAccess` to SATS `SumAccess`.
struct EnumAccess<A> {
/// The Serde `EnumAccess`.
access: A,
}

impl<'de, A: serde::EnumAccess<'de>> super::SumAccess<'de> for EnumAccess<A> {
type Error = SerdeError<A::Error>;
type Variant = VariantAccess<A::Variant>;
fn visit_seq<A>(self, access: A) -> Result<Self::Value, A::Error>
where
A: serde::SeqAccess<'de>,
{
self.visitor.visit_sum(SeqEnumAccess { access }).map_err(unwrap_error)
}

fn variant<V: super::VariantVisitor>(self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {
self.access
.variant_seed(VariantVisitor { visitor })
.map(|(variant, access)| (variant, VariantAccess { access }))
.map_err(SerdeError)
fn visit_unit<E: serde::Error>(self) -> Result<Self::Value, E> {
if self.visitor.is_option() {
self.visitor.visit_sum(NoneAccess(PhantomData)).map_err(unwrap_error)
} else {
Err(E::invalid_type(serde::Unexpected::Unit, &self))
}
}
}

Expand All @@ -400,7 +333,7 @@ impl<'de, V: super::VariantVisitor> serde::DeserializeSeed<'de> for VariantVisit
type Value = V::Output;

fn deserialize<D: serde::Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_identifier(self)
deserializer.deserialize_any(self)
}
}

Expand Down Expand Up @@ -430,17 +363,62 @@ impl<V: super::VariantVisitor> serde::Visitor<'_> for VariantVisitor<V> {
}
}

/// Deserializes the data of a variant using Serde's `serde::VariantAccess` translating this to SATS.
struct VariantAccess<A> {
// Implements `serde::VariantAccess`.
/// Converts Serde's `EnumAccess` to SATS `SumAccess`.
struct EnumAccess<A> {
/// The Serde `EnumAccess`.
access: A,
}

impl<'de, A: serde::VariantAccess<'de>> super::VariantAccess<'de> for VariantAccess<A> {
impl<'de, A: serde::MapAccess<'de>> super::SumAccess<'de> for EnumAccess<A> {
type Error = SerdeError<A::Error>;
type Variant = Self;

fn deserialize_seed<T: super::DeserializeSeed<'de>>(self, seed: T) -> Result<T::Output, Self::Error> {
self.access.newtype_variant_seed(SeedWrapper(seed)).map_err(SerdeError)
fn variant<V: super::VariantVisitor>(mut self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {
let errmsg = "expected map representing sum type to have exactly one field";
let key = self
.access
.next_key_seed(VariantVisitor { visitor })
.map_err(SerdeError)?
.ok_or_else(|| SerdeError(serde::Error::custom(errmsg)))?;
Ok((key, self))
}
}

impl<'de, A: serde::MapAccess<'de>> super::VariantAccess<'de> for EnumAccess<A> {
type Error = SerdeError<A::Error>;

fn deserialize_seed<T: super::DeserializeSeed<'de>>(mut self, seed: T) -> Result<T::Output, Self::Error> {
self.access.next_value_seed(SeedWrapper(seed)).map_err(SerdeError)
}
}

struct SeqEnumAccess<A> {
access: A,
}

const SEQ_ENUM_ERR: &str = "expected seq representing sum type to have exactly two fields";
impl<'de, A: serde::SeqAccess<'de>> super::SumAccess<'de> for SeqEnumAccess<A> {
type Error = SerdeError<A::Error>;
type Variant = Self;

fn variant<V: super::VariantVisitor>(mut self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {
let key = self
.access
.next_element_seed(VariantVisitor { visitor })
.map_err(SerdeError)?
.ok_or_else(|| SerdeError(serde::Error::custom(SEQ_ENUM_ERR)))?;
Ok((key, self))
}
}

impl<'de, A: serde::SeqAccess<'de>> super::VariantAccess<'de> for SeqEnumAccess<A> {
type Error = SerdeError<A::Error>;

fn deserialize_seed<T: super::DeserializeSeed<'de>>(mut self, seed: T) -> Result<T::Output, Self::Error> {
self.access
.next_element_seed(SeedWrapper(seed))
.map_err(SerdeError)?
.ok_or_else(|| SerdeError(serde::Error::custom(SEQ_ENUM_ERR)))
}
}

Expand Down
12 changes: 7 additions & 5 deletions crates/sats/src/ser/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,18 @@ impl<S: serde::Serializer> Serializer for SerdeSerializer<S> {
value: &T,
) -> Result<Self::Ok, Self::Error> {
// can't use serialize_variant cause we're too dynamic :(
use serde::SerializeMap;
let mut map = self.ser.serialize_map(Some(1)).map_err(SerdeError)?;
use serde::{SerializeMap, SerializeTuple};
let value = SerializeWrapper::from_ref(value);
if let Some(name) = name {
let mut map = self.ser.serialize_map(Some(1)).map_err(SerdeError)?;
map.serialize_entry(name, value).map_err(SerdeError)?;
map.end().map_err(SerdeError)
} else {
// FIXME: this probably wouldn't decode if you ran it back through
map.serialize_entry(&tag, value).map_err(SerdeError)?;
let mut seq = self.ser.serialize_tuple(2).map_err(SerdeError)?;
seq.serialize_element(&tag).map_err(SerdeError)?;
seq.serialize_element(value).map_err(SerdeError)?;
seq.end().map_err(SerdeError)
}
map.end().map_err(SerdeError)
}

unsafe fn serialize_bsatn(self, ty: &crate::AlgebraicType, bsatn: &[u8]) -> Result<Self::Ok, Self::Error> {
Expand Down

2 comments on commit dd699c4

@github-actions
Copy link

@github-actions github-actions bot commented on dd699c4 Sep 27, 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 5409 5411 -0.04% 5569 5453 2.13%
sqlite 5555 5555 0.00% 5971 5965 0.10%

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 117811 117813 -0.00% 118623 118289 0.28%
stdb_raw u32_u64_str no_index 64 128 1 u64 75399 75401 -0.00% 76061 75853 0.27%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 24068 24069 -0.00% 24644 24489 0.63%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 23035 23036 -0.00% 23577 23376 0.86%
sqlite u32_u64_str no_index 64 128 2 string 144677 144678 -0.00% 146091 146148 -0.04%
sqlite u32_u64_str no_index 64 128 1 u64 124026 124027 -0.00% 125234 125253 -0.02%
sqlite u32_u64_str btree_each_column 64 128 2 string 134476 134477 -0.00% 136066 136191 -0.09%
sqlite u32_u64_str btree_each_column 64 128 1 u64 131343 131344 -0.00% 132835 132922 -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 901857 905180 -0.37% 956991 958078 -0.11%
stdb_raw u32_u64_str btree_each_column 64 128 1053738 1060265 -0.62% 1095240 1131459 -3.20%
sqlite u32_u64_str unique_0 64 128 398292 398292 0.00% 415560 417288 -0.41%
sqlite u32_u64_str btree_each_column 64 128 983627 983609 0.00% 1023533 1021801 0.17%

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 152695 152697 -0.00% 152853 152735 0.08%
stdb_raw u32_u64_str unique_0 64 15720 15722 -0.01% 15870 15756 0.72%
sqlite u32_u64_str unique_0 1024 1068223 1068223 0.00% 1071623 1071495 0.01%
sqlite u32_u64_str unique_0 64 76209 76209 0.00% 77331 77263 0.09%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47118 47118 0.00% 49804 49770 0.07%
64 bsatn 25716 25716 0.00% 27994 27994 0.00%
16 json 12062 12062 0.00% 14000 13966 0.24%
16 bsatn 8117 8117 0.00% 9511 9477 0.36%

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 20673127 20715166 -0.20% 21427101 21316854 0.52%
stdb_raw u32_u64_str unique_0 64 128 1307218 1310644 -0.26% 1390338 1384128 0.45%
sqlite u32_u64_str unique_0 1024 1024 1802091 1801983 0.01% 1811253 1811077 0.01%
sqlite u32_u64_str unique_0 64 128 128437 128329 0.08% 131263 131247 0.01%
On-disk benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 5419 5421 -0.04% 5583 5467 2.12%
sqlite 5603 5597 0.11% 6193 6071 2.01%

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 117821 117823 -0.00% 118609 118263 0.29%
stdb_raw u32_u64_str no_index 64 128 1 u64 75409 75411 -0.00% 76091 75855 0.31%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 24078 24079 -0.00% 24674 24495 0.73%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 23045 23046 -0.00% 23579 23378 0.86%
sqlite u32_u64_str no_index 64 128 2 string 146598 146599 -0.00% 148332 148381 -0.03%
sqlite u32_u64_str no_index 64 128 1 u64 125947 125948 -0.00% 127539 127534 0.00%
sqlite u32_u64_str btree_each_column 64 128 2 string 136598 136599 -0.00% 138678 138835 -0.11%
sqlite u32_u64_str btree_each_column 64 128 1 u64 133445 133440 0.00% 135271 135488 -0.16%

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 852089 854770 -0.31% 906465 907006 -0.06%
stdb_raw u32_u64_str btree_each_column 64 128 1004152 1005976 -0.18% 1076568 1076970 -0.04%
sqlite u32_u64_str unique_0 64 128 415829 415829 0.00% 432343 434073 -0.40%
sqlite u32_u64_str btree_each_column 64 128 1021870 1021870 0.00% 1060848 1059660 0.11%

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 152705 152707 -0.00% 152855 152741 0.07%
stdb_raw u32_u64_str unique_0 64 15730 15732 -0.01% 15880 15766 0.72%
sqlite u32_u64_str unique_0 1024 1071291 1071291 0.00% 1075081 1074861 0.02%
sqlite u32_u64_str unique_0 64 77981 77987 -0.01% 79375 79253 0.15%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47118 47118 0.00% 49804 49770 0.07%
64 bsatn 25716 25716 0.00% 27994 27994 0.00%
16 json 12062 12062 0.00% 14000 13966 0.24%
16 bsatn 8117 8117 0.00% 9511 9477 0.36%

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 19414188 19459290 -0.23% 20230432 20130690 0.50%
stdb_raw u32_u64_str unique_0 64 128 1259747 1261345 -0.13% 1341069 1333325 0.58%
sqlite u32_u64_str unique_0 1024 1024 1809652 1809538 0.01% 1818254 1818262 -0.00%
sqlite u32_u64_str unique_0 64 128 132563 132449 0.09% 135525 135457 0.05%

@github-actions
Copy link

@github-actions github-actions bot commented on dd699c4 Sep 27, 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 💿 430.2±2.24ns 407.6±2.19ns - -
sqlite 🧠 421.1±5.65ns 400.0±1.82ns - -
stdb_raw 💿 618.7±0.58ns 623.5±1.26ns - -
stdb_raw 🧠 618.5±0.25ns 624.7±0.53ns - -

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 580.7±1.45µs 579.9±0.80µs 1721 tx/sec 1724 tx/sec
sqlite 💿 u32_u64_str unique_0 2048 256 146.4±0.46µs 145.3±0.54µs 6.7 Ktx/sec 6.7 Ktx/sec
sqlite 💿 u32_u64_u64 btree_each_column 2048 256 461.6±0.55µs 464.4±0.65µs 2.1 Ktx/sec 2.1 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 2048 256 132.3±0.74µs 134.5±0.39µs 7.4 Ktx/sec 7.3 Ktx/sec
sqlite 🧠 u32_u64_str btree_each_column 2048 256 441.2±0.48µs 441.5±0.70µs 2.2 Ktx/sec 2.2 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 2048 256 121.0±0.50µs 117.1±1.14µs 8.1 Ktx/sec 8.3 Ktx/sec
sqlite 🧠 u32_u64_u64 btree_each_column 2048 256 363.0±1.29µs 365.5±0.37µs 2.7 Ktx/sec 2.7 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 2048 256 103.9±0.26µs 106.2±0.37µs 9.4 Ktx/sec 9.2 Ktx/sec
stdb_raw 💿 u32_u64_str btree_each_column 2048 256 594.7±26.69µs 501.6±10.61µs 1681 tx/sec 1993 tx/sec
stdb_raw 💿 u32_u64_str unique_0 2048 256 504.4±26.33µs 458.3±45.59µs 1982 tx/sec 2.1 Ktx/sec
stdb_raw 💿 u32_u64_u64 btree_each_column 2048 256 336.7±4.60µs 380.5±8.33µs 2.9 Ktx/sec 2.6 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 2048 256 356.4±5.35µs 313.8±18.44µs 2.7 Ktx/sec 3.1 Ktx/sec
stdb_raw 🧠 u32_u64_str btree_each_column 2048 256 310.9±0.38µs 308.4±0.33µs 3.1 Ktx/sec 3.2 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 2048 256 245.3±0.21µs 240.5±0.20µs 4.0 Ktx/sec 4.1 Ktx/sec
stdb_raw 🧠 u32_u64_u64 btree_each_column 2048 256 246.5±0.17µs 249.3±0.13µs 4.0 Ktx/sec 3.9 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 2048 256 223.2±0.15µs 220.4±0.18µs 4.4 Ktx/sec 4.4 Ktx/sec

iterate

db on disk schema indices new latency old latency new throughput old throughput
sqlite 💿 u32_u64_str unique_0 21.9±0.28µs 21.4±0.14µs 44.5 Ktx/sec 45.6 Ktx/sec
sqlite 💿 u32_u64_u64 unique_0 20.4±0.14µs 19.8±0.16µs 48.0 Ktx/sec 49.3 Ktx/sec
sqlite 🧠 u32_u64_str unique_0 19.7±0.25µs 18.9±0.12µs 49.6 Ktx/sec 51.7 Ktx/sec
sqlite 🧠 u32_u64_u64 unique_0 17.9±0.14µs 17.2±0.05µs 54.7 Ktx/sec 56.8 Ktx/sec
stdb_raw 💿 u32_u64_str unique_0 3.8±0.00µs 4.7±0.00µs 257.2 Ktx/sec 206.3 Ktx/sec
stdb_raw 💿 u32_u64_u64 unique_0 3.7±0.00µs 4.6±0.00µs 264.4 Ktx/sec 211.2 Ktx/sec
stdb_raw 🧠 u32_u64_str unique_0 3.8±0.00µs 4.7±0.00µs 257.2 Ktx/sec 206.3 Ktx/sec
stdb_raw 🧠 u32_u64_u64 unique_0 3.7±0.00µs 4.6±0.00µs 264.4 Ktx/sec 211.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.3±0.23µs 68.6±0.27µs 14.5 Ktx/sec 14.2 Ktx/sec
sqlite 💿 u64 index 2048 256 64.8±0.26µs 64.9±0.22µs 15.1 Ktx/sec 15.0 Ktx/sec
sqlite 🧠 string index 2048 256 63.8±0.14µs 65.6±0.17µs 15.3 Ktx/sec 14.9 Ktx/sec
sqlite 🧠 u64 index 2048 256 59.6±0.10µs 59.7±0.03µs 16.4 Ktx/sec 16.4 Ktx/sec
stdb_raw 💿 string index 2048 256 4.9±0.00µs 4.8±0.00µs 199.2 Ktx/sec 201.9 Ktx/sec
stdb_raw 💿 u64 index 2048 256 4.8±0.00µs 4.8±0.00µs 202.3 Ktx/sec 201.7 Ktx/sec
stdb_raw 🧠 string index 2048 256 4.9±0.00µs 4.8±0.00µs 199.4 Ktx/sec 201.8 Ktx/sec
stdb_raw 🧠 u64 index 2048 256 4.8±0.00µs 4.8±0.00µs 202.1 Ktx/sec 201.7 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.05µs 28.6 Mtx/sec 29.0 Mtx/sec
u32_u64_str bflatn_to_bsatn_slow_path 100 3.8±0.13µs 3.4±0.01µs 25.0 Mtx/sec 28.3 Mtx/sec
u32_u64_str bsatn 100 2.3±0.01µs 2.4±0.01µs 42.0 Mtx/sec 39.8 Mtx/sec
u32_u64_str bsatn 100 40.5±0.10ns 40.4±0.15ns 2.3 Gtx/sec 2.3 Gtx/sec
u32_u64_str json 100 4.9±0.03µs 4.8±0.02µs 19.4 Mtx/sec 20.1 Mtx/sec
u32_u64_str json 100 6.8±0.07µs 7.5±0.02µs 14.0 Mtx/sec 12.7 Mtx/sec
u32_u64_str product_value 100 1014.3±0.68ns 1016.4±3.16ns 94.0 Mtx/sec 93.8 Mtx/sec
u32_u64_u64 bflatn_to_bsatn_fast_path 100 1145.1±13.57ns 1146.5±2.18ns 83.3 Mtx/sec 83.2 Mtx/sec
u32_u64_u64 bflatn_to_bsatn_slow_path 100 2.8±0.00µs 2.8±0.00µs 34.3 Mtx/sec 34.3 Mtx/sec
u32_u64_u64 bsatn 100 1542.4±11.47ns 1693.0±30.56ns 61.8 Mtx/sec 56.3 Mtx/sec
u32_u64_u64 bsatn 100 39.4±0.07ns 39.4±0.09ns 2.4 Gtx/sec 2.4 Gtx/sec
u32_u64_u64 json 100 3.5±0.05µs 3.3±0.11µs 27.6 Mtx/sec 28.8 Mtx/sec
u32_u64_u64 json 100 4.7±0.00µs 5.4±0.02µs 20.1 Mtx/sec 17.6 Mtx/sec
u32_u64_u64 product_value 100 1013.5±1.60ns 1014.3±1.58ns 94.1 Mtx/sec 94.0 Mtx/sec
u64_u64_u32 bflatn_to_bsatn_fast_path 100 900.1±2.29ns 893.4±0.83ns 106.0 Mtx/sec 106.7 Mtx/sec
u64_u64_u32 bflatn_to_bsatn_slow_path 100 2.8±0.01µs 2.8±0.01µs 34.3 Mtx/sec 34.4 Mtx/sec
u64_u64_u32 bsatn 100 1550.8±8.54ns 1695.0±31.66ns 61.5 Mtx/sec 56.3 Mtx/sec
u64_u64_u32 bsatn 100 752.3±0.50ns 756.1±0.42ns 126.8 Mtx/sec 126.1 Mtx/sec
u64_u64_u32 json 100 3.5±0.03µs 3.3±0.12µs 27.1 Mtx/sec 28.9 Mtx/sec
u64_u64_u32 json 100 4.7±0.00µs 5.1±0.07µs 20.4 Mtx/sec 18.6 Mtx/sec
u64_u64_u32 product_value 100 1025.9±0.48ns 1014.2±0.71ns 93.0 Mtx/sec 94.0 Mtx/sec

stdb_module_large_arguments

arg size new latency old latency new throughput old throughput
64KiB 113.4±6.89µs 105.6±8.51µs - -

stdb_module_print_bulk

line count new latency old latency new throughput old throughput
1 53.2±4.14µs 60.2±6.98µs - -
100 605.8±9.47µs 595.5±5.27µs - -
1000 5.0±0.82ms 3.5±0.08ms - -

remaining

name new latency old latency new throughput old throughput
special/db_game/circles/load=10 279.4±5.90µs 281.7±4.80µs - -
special/db_game/circles/load=100 281.8±3.98µs 283.1±2.57µ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 53.0±0.16µs 52.2±0.12µs 18.4 Ktx/sec 18.7 Ktx/sec
sqlite/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 49.7±13.98µs 44.5±0.08µs 19.6 Ktx/sec 22.0 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 38.5±0.29µs 38.0±0.43µs 25.3 Ktx/sec 25.7 Ktx/sec
sqlite/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 34.5±0.09µs 34.1±0.15µs 28.3 Ktx/sec 28.6 Ktx/sec
stdb_module/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 1229.8±15.52µs 1248.4±3.99µs 813 tx/sec 801 tx/sec
stdb_module/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 977.5±3.39µs 987.9±7.83µs 1023 tx/sec 1012 tx/sec
stdb_raw/💿/update_bulk/u32_u64_str/unique_0/load=2048/count=256 642.6±19.11µs 646.6±36.32µs 1556 tx/sec 1546 tx/sec
stdb_raw/💿/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 487.1±18.68µs 496.2±14.16µs 2.0 Ktx/sec 2015 tx/sec
stdb_raw/🧠/update_bulk/u32_u64_str/unique_0/load=2048/count=256 377.5±0.37µs 380.3±0.35µs 2.6 Ktx/sec 2.6 Ktx/sec
stdb_raw/🧠/update_bulk/u32_u64_u64/unique_0/load=2048/count=256 339.1±1.66µs 345.6±0.22µs 2.9 Ktx/sec 2.8 Ktx/sec

Please sign in to comment.