From ec5c31a19da8a2d5d30537151e6f0d37377776a3 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 17 Sep 2025 16:25:28 +0200 Subject: [PATCH] chore(bench): Add the `room_list` benchmark. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the `room_list` benchmark. The goal is to measure the time it takes to create a room list, to sort it, to filter and to “display” it. --- Cargo.lock | 3 + benchmarks/Cargo.toml | 7 ++ benchmarks/benches/room_list.rs | 90 ++++++++++++++++++++ testing/matrix-sdk-test/src/event_factory.rs | 5 ++ 4 files changed, 105 insertions(+) create mode 100644 benchmarks/benches/room_list.rs diff --git a/Cargo.lock b/Cargo.lock index 32d039f0c..2b461b0e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,13 +490,16 @@ dependencies = [ name = "benchmarks" version = "1.0.0" dependencies = [ + "assert_matches", "codspeed-criterion-compat", + "futures-util", "matrix-sdk", "matrix-sdk-base", "matrix-sdk-crypto", "matrix-sdk-sqlite", "matrix-sdk-test", "matrix-sdk-ui", + "rand 0.8.5", "ruma", "serde", "serde_json", diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index 476508db4..79f24ce8c 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -14,13 +14,16 @@ release = false codspeed = [] [dependencies] +assert_matches.workspace = true criterion = { version = "3.0.5", features = ["async", "async_tokio", "html_reports"], package = "codspeed-criterion-compat" } +futures-util.workspace = true matrix-sdk = { workspace = true, features = ["native-tls", "e2e-encryption", "sqlite", "testing"] } matrix-sdk-base.workspace = true matrix-sdk-crypto.workspace = true matrix-sdk-sqlite = { workspace = true, features = ["crypto-store"] } matrix-sdk-test.workspace = true matrix-sdk-ui.workspace = true +rand.workspace = true ruma.workspace = true serde.workspace = true serde_json.workspace = true @@ -51,3 +54,7 @@ harness = false [[bench]] name = "event_cache" harness = false + +[[bench]] +name = "room_list" +harness = false diff --git a/benchmarks/benches/room_list.rs b/benchmarks/benches/room_list.rs new file mode 100644 index 000000000..ca6c4fb7d --- /dev/null +++ b/benchmarks/benches/room_list.rs @@ -0,0 +1,90 @@ +use assert_matches::assert_matches; +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; +use futures_util::pin_mut; +use matrix_sdk::{stream::StreamExt, test_utils::mocks::MatrixMockServer}; +use matrix_sdk_test::{JoinedRoomBuilder, event_factory::EventFactory}; +use matrix_sdk_ui::{ + RoomListService, eyeball_im::VectorDiff, room_list_service::filters::new_filter_non_left, +}; +use rand::{distributions::Uniform, prelude::Distribution}; +use ruma::{EventId, RoomId, owned_user_id}; +use tokio::runtime::Builder; + +/// Benchmark the time it takes to create a room list. +pub fn create(c: &mut Criterion) { + const NUMBER_OF_ROOMS: usize = 1000; + const NUMBER_OF_EVENTS_PER_ROOM: usize = 1000; + + let runtime = Builder::new_multi_thread().enable_all().build().expect("Can't create runtime"); + + let (server, client) = runtime.block_on(async { + let server = MatrixMockServer::new().await; + let client = server.client_builder().build().await; + client.event_cache().subscribe().unwrap(); + + (server, client) + }); + + let sender_id = owned_user_id!("@mnt_io:matrix.org"); + let mut rand = rand::thread_rng(); + let server_ts_range = Uniform::from(100..1000); + + for room_nth in 0..NUMBER_OF_ROOMS { + let room_id = RoomId::parse(format!("!r{room_nth}")).unwrap(); + let first_server_ts = server_ts_range.sample(&mut rand); + let event_factory = EventFactory::new().room(&room_id).server_ts(first_server_ts); + + let events = (0..NUMBER_OF_EVENTS_PER_ROOM) + .map(|event_nth| { + let event_id = EventId::parse(format!("$ev{room_nth}_{event_nth}")).unwrap(); + + event_factory.text_msg("a").sender(&sender_id).event_id(&event_id).into_raw_sync() + }) + .collect::>(); + + let _room = runtime.block_on(async { + server + .sync_room(&client, JoinedRoomBuilder::new(&room_id).add_timeline_bulk(events)) + .await + }); + } + + let mut group = c.benchmark_group("RoomList"); + group.throughput(Throughput::Elements(NUMBER_OF_ROOMS.try_into().unwrap())); + + group.bench_function( + BenchmarkId::new( + "Create", + format!("{NUMBER_OF_ROOMS} rooms × {NUMBER_OF_EVENTS_PER_ROOM} events"), + ), + |bencher| { + bencher.to_async(&runtime).iter(|| async { + let room_list_service = RoomListService::new(client.clone()) + .await + .expect("build the room list service"); + let room_list = room_list_service.all_rooms().await.expect("fetch `all_rooms`"); + let (entries_stream, entries_controller) = + room_list.entries_with_dynamic_adapters(20); + + // Setting the filter will trigger the entries stream computation. + entries_controller.set_filter(Box::new(new_filter_non_left())); + + pin_mut!(entries_stream); + let update = entries_stream.next().await.expect("receiving the reset update"); + assert_eq!(update.len(), 1); + assert_matches!(&update[0], VectorDiff::Reset { values } => { + assert_eq!(values.len(), 20); + }); + }); + }, + ); + + group.finish(); +} + +criterion_group! { + name = room_list; + config = Criterion::default(); + targets = create +} +criterion_main!(room_list); diff --git a/testing/matrix-sdk-test/src/event_factory.rs b/testing/matrix-sdk-test/src/event_factory.rs index ce1bdd744..f020292ff 100644 --- a/testing/matrix-sdk-test/src/event_factory.rs +++ b/testing/matrix-sdk-test/src/event_factory.rs @@ -562,6 +562,11 @@ impl EventFactory { self } + pub fn server_ts(self, ts: u64) -> Self { + self.next_ts.store(ts, SeqCst); + self + } + fn next_server_ts(&self) -> MilliSecondsSinceUnixEpoch { MilliSecondsSinceUnixEpoch( self.next_ts