Skip to content

Commit

Permalink
Merge branch 'master' into ingvar/fix-reducer-order
Browse files Browse the repository at this point in the history
  • Loading branch information
kazimuth authored Nov 15, 2024
2 parents 50a6456 + a7a1d36 commit 3309c54
Show file tree
Hide file tree
Showing 13 changed files with 212 additions and 128 deletions.
14 changes: 10 additions & 4 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
# summary PR" step). otherwise, we can use a fully shallow checkout
fetch-depth: ${{ env.PR_NUMBER && 1 || 2 }}

- name: Install stable toolchain
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
Expand All @@ -85,6 +85,13 @@ jobs:
target: wasm32-unknown-unknown
override: true

- name: Install .NET toolchain
uses: actions/setup-dotnet@v3
with:
dotnet-version: "8.x"
env:
DOTNET_INSTALL_DIR: ~/.dotnet

- name: Build
working-directory: crates/bench/
run: |
Expand Down Expand Up @@ -114,7 +121,7 @@ jobs:
rm -rf .spacetime
cargo bench --bench generic -- --save-baseline "$BASELINE_NAME" "$BENCH_FILTER"
# sticker price benchmark
cargo bench --bench generic -- --save-baseline "$BASELINE_NAME" 'stdb_module/disk/update_bulk'
cargo bench --bench generic -- --save-baseline "$BASELINE_NAME" 'stdb_module/.*/disk/update_bulk'
cargo bench --bench special -- --save-baseline "$BASELINE_NAME"
cargo run --bin summarize pack "$BASELINE_NAME"
popd
Expand Down Expand Up @@ -192,7 +199,7 @@ jobs:
- name: Install valgrind & iai-callgrind-runner
run: |
apt-get update
apt-get install -y valgrind protobuf-compiler bash sudo curl gh
apt-get install -y valgrind protobuf-compiler bash sudo curl gh
cargo install --git https://github.com/clockworklabs/iai-callgrind.git --branch main iai-callgrind-runner
git config --global --add safe.directory /__w/SpacetimeDB/SpacetimeDB
Expand Down Expand Up @@ -343,4 +350,3 @@ jobs:
echo "Letting anybody touch our git repo, in order to avoid breaking other jobs"
echo "This is necessary because we are running as root inside a docker image"
chmod -R a+rw .
1 change: 1 addition & 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 crates/bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ regex.workspace = true
rusqlite.workspace = true
serde.workspace = true
serde_json.workspace = true
serial_test.workspace = true
tempdir.workspace = true
tokio.workspace = true
tracing-subscriber.workspace = true
Expand Down
7 changes: 5 additions & 2 deletions crates/bench/benches/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use spacetimedb_bench::{
};
use spacetimedb_lib::sats::AlgebraicType;
use spacetimedb_primitives::ColId;
use spacetimedb_testing::modules::{Csharp, Rust};

#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
Expand All @@ -23,11 +24,13 @@ lazy_static! {
fn criterion_benchmark(c: &mut Criterion) {
bench_suite::<sqlite::SQLite>(c, true).unwrap();
bench_suite::<spacetime_raw::SpacetimeRaw>(c, true).unwrap();
bench_suite::<spacetime_module::SpacetimeModule>(c, true).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<Rust>>(c, true).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<Csharp>>(c, true).unwrap();

bench_suite::<sqlite::SQLite>(c, false).unwrap();
bench_suite::<spacetime_raw::SpacetimeRaw>(c, false).unwrap();
bench_suite::<spacetime_module::SpacetimeModule>(c, false).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<Rust>>(c, false).unwrap();
bench_suite::<spacetime_module::SpacetimeModule<Csharp>>(c, false).unwrap();
}

#[inline(never)]
Expand Down
23 changes: 15 additions & 8 deletions crates/bench/benches/special.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use criterion::async_executor::AsyncExecutor;
use criterion::{criterion_group, criterion_main, Criterion};
use criterion::{criterion_group, criterion_main, Criterion, SamplingMode};
use mimalloc::MiMalloc;
use spacetimedb_bench::{
database::BenchDatabase,
Expand All @@ -9,6 +9,7 @@ use spacetimedb_bench::{
use spacetimedb_lib::sats::{self, bsatn};
use spacetimedb_lib::{bsatn::ToBsatn as _, ProductValue};
use spacetimedb_schema::schema::TableSchema;
use spacetimedb_testing::modules::{Csharp, ModuleLanguage, Rust};
use std::sync::Arc;
use std::sync::OnceLock;

Expand All @@ -20,14 +21,19 @@ fn criterion_benchmark(c: &mut Criterion) {
serialize_benchmarks::<u32_u64_u64>(c);
serialize_benchmarks::<u64_u64_u32>(c);

let db = SpacetimeModule::build(true).unwrap();
custom_benchmarks::<Rust>(c);
custom_benchmarks::<Csharp>(c);
}

fn custom_benchmarks<L: ModuleLanguage>(c: &mut Criterion) {
let db = SpacetimeModule::<L>::build(true).unwrap();

custom_module_benchmarks(&db, c);
custom_db_benchmarks(&db, c);
}

fn custom_module_benchmarks(m: &SpacetimeModule, c: &mut Criterion) {
let mut group = c.benchmark_group("special/stdb_module");
fn custom_module_benchmarks<L: ModuleLanguage>(m: &SpacetimeModule<L>, c: &mut Criterion) {
let mut group = c.benchmark_group(format!("special/{}", SpacetimeModule::<L>::name()));

let args = sats::product!["0".repeat(65536).into_boxed_str()];
group.bench_function("large_arguments/64KiB", |b| {
Expand All @@ -44,10 +50,11 @@ fn custom_module_benchmarks(m: &SpacetimeModule, c: &mut Criterion) {
}
}

fn custom_db_benchmarks(m: &SpacetimeModule, c: &mut Criterion) {
let mut group = c.benchmark_group("special/db_game");
fn custom_db_benchmarks<L: ModuleLanguage>(m: &SpacetimeModule<L>, c: &mut Criterion) {
let mut group = c.benchmark_group(format!("special/db_game/{}", L::NAME));
// This bench take long, so adjust for it
group.sample_size(10);
group.sampling_mode(SamplingMode::Flat);

let init_db: OnceLock<()> = OnceLock::new();
for n in [10, 100] {
Expand All @@ -69,14 +76,14 @@ fn custom_db_benchmarks(m: &SpacetimeModule, c: &mut Criterion) {
}

let init_db: OnceLock<()> = OnceLock::new();
for n in [500, 5_000] {
for n in [10, 100] {
let args = sats::product![n];
group.bench_function(format!("ia_loop/load={n}"), |b| {
// Initialize outside the benchmark so the db is seed once, to avoid `unique` constraints violations
init_db.get_or_init(|| {
m.block_on(async {
m.module
.call_reducer_binary("init_game_ia_loop", &sats::product![5_000])
.call_reducer_binary("init_game_ia_loop", &sats::product![500])
.await
.unwrap();
})
Expand Down
2 changes: 1 addition & 1 deletion crates/bench/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::ResultBench;
///
/// Not all benchmarks have to go through this trait.
pub trait BenchDatabase: Sized {
fn name() -> &'static str;
fn name() -> String;

type TableId: Clone + 'static;

Expand Down
45 changes: 27 additions & 18 deletions crates/bench/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ mod tests {
sqlite::SQLite,
ResultBench,
};
use serial_test::serial;
use spacetimedb_testing::modules::{Csharp, Rust};
use std::{io, path::Path, sync::Once};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

Expand Down Expand Up @@ -101,30 +103,37 @@ mod tests {
Ok(())
}

fn test_basic_invariants<DB: BenchDatabase>() -> ResultBench<()> {
basic_invariants::<DB, u32_u64_str>(IndexStrategy::Unique0, true)?;
basic_invariants::<DB, u32_u64_u64>(IndexStrategy::Unique0, true)?;
basic_invariants::<DB, u32_u64_str>(IndexStrategy::BTreeEachColumn, true)?;
basic_invariants::<DB, u32_u64_u64>(IndexStrategy::BTreeEachColumn, true)?;
Ok(())
}

#[test]
fn test_basic_invariants_sqlite() {
basic_invariants::<SQLite, u32_u64_str>(IndexStrategy::Unique0, true).unwrap();
basic_invariants::<SQLite, u32_u64_u64>(IndexStrategy::Unique0, true).unwrap();
basic_invariants::<SQLite, u32_u64_str>(IndexStrategy::BTreeEachColumn, true).unwrap();
basic_invariants::<SQLite, u32_u64_u64>(IndexStrategy::BTreeEachColumn, true).unwrap();
fn test_basic_invariants_sqlite() -> ResultBench<()> {
test_basic_invariants::<SQLite>()
}

#[test]
fn test_basic_invariants_spacetime_raw() {
basic_invariants::<SpacetimeRaw, u32_u64_str>(IndexStrategy::Unique0, true).unwrap();
basic_invariants::<SpacetimeRaw, u32_u64_u64>(IndexStrategy::Unique0, true).unwrap();
basic_invariants::<SpacetimeRaw, u32_u64_str>(IndexStrategy::BTreeEachColumn, true).unwrap();
basic_invariants::<SpacetimeRaw, u32_u64_u64>(IndexStrategy::BTreeEachColumn, true).unwrap();
fn test_basic_invariants_spacetime_raw() -> ResultBench<()> {
test_basic_invariants::<SpacetimeRaw>()
}

// note: there can only be one #[test] invoking spacetime module stuff.
// #[test]s run concurrently and they fight over lockfiles.
// so, run the sub-tests here in sequence.

#[test]
#[serial]
fn test_basic_invariants_spacetime_module_rust() -> ResultBench<()> {
test_basic_invariants::<SpacetimeModule<Rust>>()
}

#[test]
fn test_basic_invariants_spacetime_module() {
// note: there can only be one #[test] invoking spacetime module stuff.
// #[test]s run concurrently and they fight over lockfiles.
// so, run the sub-tests here in sequence.
basic_invariants::<SpacetimeModule, u32_u64_str>(IndexStrategy::Unique0, true).unwrap();
basic_invariants::<SpacetimeModule, u32_u64_u64>(IndexStrategy::Unique0, true).unwrap();
basic_invariants::<SpacetimeModule, u32_u64_str>(IndexStrategy::BTreeEachColumn, true).unwrap();
basic_invariants::<SpacetimeModule, u32_u64_u64>(IndexStrategy::BTreeEachColumn, true).unwrap();
#[serial]
fn test_basic_invariants_spacetime_module_csharp() -> ResultBench<()> {
test_basic_invariants::<SpacetimeModule<Csharp>>()
}
}
54 changes: 21 additions & 33 deletions crates/bench/src/spacetime_module.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::Path;
use std::{marker::PhantomData, path::Path};

use spacetimedb::db::{Config, Storage};
use spacetimedb_lib::{
Expand All @@ -7,7 +7,7 @@ use spacetimedb_lib::{
};
use spacetimedb_paths::RootDir;
use spacetimedb_primitives::ColId;
use spacetimedb_testing::modules::{start_runtime, CompilationMode, CompiledModule, LoggerRecord, ModuleHandle};
use spacetimedb_testing::modules::{start_runtime, LoggerRecord, ModuleHandle, ModuleLanguage};
use tokio::runtime::Runtime;

use crate::{
Expand All @@ -17,23 +17,6 @@ use crate::{
};
use criterion::async_executor::AsyncExecutor;

lazy_static::lazy_static! {
pub static ref BENCHMARKS_MODULE: CompiledModule = {
if std::env::var_os("STDB_BENCH_CS").is_some() {
CompiledModule::compile("benchmarks-cs", CompilationMode::Release)
} else {
// Temporarily add CARGO_TARGET_DIR override to avoid conflicts with main target dir.
// Otherwise for some reason Cargo will mark all dependencies with build scripts as
// fresh - but only if running benchmarks (if modules are built in release mode).
// See https://github.com/clockworklabs/SpacetimeDB/issues/401.
std::env::set_var("CARGO_TARGET_DIR", concat!(env!("CARGO_MANIFEST_DIR"), "/target"));
let module = CompiledModule::compile("benchmarks", CompilationMode::Release);
std::env::remove_var("CARGO_TARGET_DIR");
module
}
};
}

/// A benchmark backend that invokes a spacetime module.
///
/// This is tightly tied to the file `modules/benchmarks/src/lib.rs`;
Expand All @@ -42,26 +25,27 @@ lazy_static::lazy_static! {
///
/// See the doc comment there for information on the formatting expected for
/// table and reducer names.
pub struct SpacetimeModule {
pub struct SpacetimeModule<L> {
// Module must be dropped BEFORE runtime otherwise there is a deadlock!
// In Rust, struct fields are guaranteed to drop in declaration order, so don't reorder this field.
pub module: ModuleHandle,
runtime: Runtime,
lang: PhantomData<L>,
}

// Note: we use block_on for the methods here. It adds about 70ns of overhead.
// This isn't currently a problem. Overhead to call an empty reducer is currently 20_000 ns.

impl AsyncExecutor for &SpacetimeModule {
impl<L> AsyncExecutor for &SpacetimeModule<L> {
fn block_on<T>(&self, future: impl std::future::Future<Output = T>) -> T {
self.runtime.block_on(future)
}
}

// It's easier to do it this way because async traits are a mess.
impl BenchDatabase for SpacetimeModule {
fn name() -> &'static str {
"stdb_module"
impl<L: ModuleLanguage> BenchDatabase for SpacetimeModule<L> {
fn name() -> String {
format!("stdb_module/{}", L::NAME)
}

type TableId = TableId;
Expand All @@ -81,7 +65,7 @@ impl BenchDatabase for SpacetimeModule {
// It's fine that we're constructing this path ad-hoc, as it's just
// a path location for tests, not part of our stable directory structure.
let path = RootDir(Path::new(env!("CARGO_MANIFEST_DIR")).join(".spacetime"));
BENCHMARKS_MODULE.load_module(config, Some(&path)).await
L::get_module().load_module(config, Some(&path)).await
});

for table in module.client.module.info.module_def.tables() {
Expand All @@ -90,7 +74,11 @@ impl BenchDatabase for SpacetimeModule {
for reducer in module.client.module.info.module_def.reducers() {
log::trace!("SPACETIME_MODULE: LOADED REDUCER: {:?}", reducer);
}
Ok(SpacetimeModule { runtime, module })
Ok(SpacetimeModule {
runtime,
module,
lang: PhantomData,
})
}

fn create_table<T: BenchTable>(
Expand All @@ -105,7 +93,7 @@ impl BenchDatabase for SpacetimeModule {
}

fn clear_table(&mut self, table_id: &Self::TableId) -> ResultBench<()> {
let SpacetimeModule { runtime, module } = self;
let SpacetimeModule { runtime, module, .. } = self;
runtime.block_on(async move {
// FIXME: this doesn't work. delete is unimplemented!!
/*
Expand All @@ -122,7 +110,7 @@ impl BenchDatabase for SpacetimeModule {
// message in the log.
// This implementation will not work if other people are concurrently interacting with our module.
fn count_table(&mut self, table_id: &Self::TableId) -> ResultBench<u32> {
let SpacetimeModule { runtime, module } = self;
let SpacetimeModule { runtime, module, .. } = self;

let count = runtime.block_on(async move {
let name = format!("count_{}", table_id.snake_case);
Expand All @@ -140,7 +128,7 @@ impl BenchDatabase for SpacetimeModule {
}

fn empty_transaction(&mut self) -> ResultBench<()> {
let SpacetimeModule { runtime, module } = self;
let SpacetimeModule { runtime, module, .. } = self;

runtime.block_on(async move {
module.call_reducer_binary("empty", &[].into()).await?;
Expand All @@ -151,7 +139,7 @@ impl BenchDatabase for SpacetimeModule {
fn insert_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, rows: Vec<T>) -> ResultBench<()> {
let rows = rows.into_iter().map(|row| row.into_product_value()).collect();
let args = product![ArrayValue::Product(rows)];
let SpacetimeModule { runtime, module } = self;
let SpacetimeModule { runtime, module, .. } = self;
let reducer_name = format!("insert_bulk_{}", table_id.snake_case);

runtime.block_on(async move {
Expand All @@ -162,7 +150,7 @@ impl BenchDatabase for SpacetimeModule {

fn update_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, row_count: u32) -> ResultBench<()> {
let args = product![row_count];
let SpacetimeModule { runtime, module } = self;
let SpacetimeModule { runtime, module, .. } = self;
let reducer_name = format!("update_bulk_{}", table_id.snake_case);

runtime.block_on(async move {
Expand All @@ -172,7 +160,7 @@ impl BenchDatabase for SpacetimeModule {
}

fn iterate(&mut self, table_id: &Self::TableId) -> ResultBench<()> {
let SpacetimeModule { runtime, module } = self;
let SpacetimeModule { runtime, module, .. } = self;
let reducer_name = format!("iterate_{}", table_id.snake_case);

runtime.block_on(async move {
Expand All @@ -187,7 +175,7 @@ impl BenchDatabase for SpacetimeModule {
col_id: impl Into<ColId>,
value: AlgebraicValue,
) -> ResultBench<()> {
let SpacetimeModule { runtime, module } = self;
let SpacetimeModule { runtime, module, .. } = self;

let product_type = T::product_type();
let column_name = product_type.elements[col_id.into().idx()].name.as_ref().unwrap();
Expand Down
4 changes: 2 additions & 2 deletions crates/bench/src/spacetime_raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ pub struct SpacetimeRaw {
}

impl BenchDatabase for SpacetimeRaw {
fn name() -> &'static str {
"stdb_raw"
fn name() -> String {
"stdb_raw".to_owned()
}
type TableId = TableId;

Expand Down
Loading

0 comments on commit 3309c54

Please sign in to comment.