atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

Blame: benchmarks/benches/room_bench.rs

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
use std::time::Duration;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
use matrix_sdk::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
    cross_process_lock::CrossProcessLockConfig, store::RoomLoadSettings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
    test_utils::mocks::MatrixMockServer,
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
use matrix_sdk_base::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
    BaseClient, DmRoomDefinition, RoomInfo, RoomState, SessionMeta, StateChanges, StateStore,
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
    ThreadingSupport, store::StoreConfig,
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
use matrix_sdk_sqlite::SqliteStateStore;
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
use matrix_sdk_test::{JoinedRoomBuilder, base64_sha256_hash, event_factory::EventFactory};
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
use matrix_sdk_ui::timeline::{TimelineBuilder, TimelineFocus};
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
use ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
    EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
    api::client::membership::get_member_events,
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
    events::room::member::{MembershipState, RoomMemberEvent},
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
    mxc_uri, owned_device_id, owned_room_id, owned_user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
    serde::Raw,
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
use tokio::runtime::Builder;
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
use wiremock::{Request, ResponseTemplate};
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
pub fn receive_all_members_benchmark(c: &mut Criterion) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
    const MEMBERS_IN_ROOM: usize = 100000;
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
    let runtime = Builder::new_multi_thread().build().expect("Can't create runtime");
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
    let room_id = owned_room_id!("!homohominilupusest:example.com");
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
    let f = EventFactory::new().room(&room_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
    let mut member_events: Vec<Raw<RoomMemberEvent>> = Vec::with_capacity(MEMBERS_IN_ROOM);
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
    for i in 0..MEMBERS_IN_ROOM {
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
        let user_id = OwnedUserId::try_from(format!("@user_{i}:matrix.org")).unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
        let event = f
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
            .member(&user_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
            .membership(MembershipState::Join)
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
            .avatar_url(mxc_uri!("mxc://example.org/SEsfnsuifSDFSSEF"))
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
            .display_name("Alice Margatroid")
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
            .reason("Looking for support")
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
            .into_raw();
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
        member_events.push(event);
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
    // Create a fake list of changes, and a session to recover from.
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
    let mut changes = StateChanges::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
    changes.add_room(RoomInfo::new(&room_id, RoomState::Joined));
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
    for member_event in member_events.iter() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
        let event = member_event.clone().cast();
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
        changes.add_state_event(&room_id, event.deserialize().unwrap(), event);
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
    // Sqlite
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
    let sqlite_dir = tempfile::tempdir().unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
    let sqlite_store = runtime.block_on(SqliteStateStore::open(sqlite_dir.path(), None)).unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
    runtime
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
        .block_on(sqlite_store.save_changes(&changes))
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
        .expect("initial filling of sqlite failed");
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
    let base_client = BaseClient::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
        StoreConfig::new(CrossProcessLockConfig::multi_process(
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
            "cross-process-store-locks-holder-name",
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
        ))
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
        .state_store(sqlite_store),
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
        ThreadingSupport::Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
        DmRoomDefinition::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
    );
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
    runtime
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
        .block_on(base_client.activate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
            SessionMeta {
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
                user_id: owned_user_id!("@somebody:example.com"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
                device_id: owned_device_id!("DEVICE_ID"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
            RoomLoadSettings::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
            None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
        ))
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
        .expect("Could not set session meta");
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
    base_client.get_or_create_room(&room_id, RoomState::Joined);
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
    let request = get_member_events::v3::Request::new(room_id.clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
    let response = get_member_events::v3::Response::new(member_events);
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
    let count = MEMBERS_IN_ROOM;
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
    let name = format!("{count} members");
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
    let mut group = c.benchmark_group("Test");
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
    group.throughput(Throughput::Elements(count as u64));
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
    group.sample_size(50);
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
    group.bench_function(BenchmarkId::new("Handle /members request [SQLite]", name), |b| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
        b.to_async(&runtime).iter(|| async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
            base_client.receive_all_members(&room_id, &request, &response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
    });
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
        let _guard = runtime.enter();
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
        drop(base_client);
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
    group.finish();
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
pub fn load_pinned_events_benchmark(c: &mut Criterion) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
    const PINNED_EVENTS_COUNT: usize = 100;
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
    let runtime = Builder::new_multi_thread().enable_all().build().expect("Can't create runtime");
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
    let room_id = owned_room_id!("!homohominilupusest:example.com");
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
    let sender_id = owned_user_id!("@sender:example.com");
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
    let f = EventFactory::new().room(&room_id).sender(&sender_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
    let mut joined_room_builder =
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
        JoinedRoomBuilder::new(&room_id).add_state_event(f.room_encryption());
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
    let pinned_event_ids: Vec<OwnedEventId> = (0..PINNED_EVENTS_COUNT)
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
        .map(|i| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
            EventId::new_v2_or_v3(&base64_sha256_hash(format!("${i}").as_bytes()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
                .expect("Invalid event id")
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
        .collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
    joined_room_builder =
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
        joined_room_builder.add_state_bulk(vec![f.room_pinned_events(pinned_event_ids).into()]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
    let (server, client, room) = runtime.block_on(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
        let server = MatrixMockServer::new().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
        let client = server.client_builder().build().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
        let room = server.sync_room(&client, joined_room_builder).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
        server
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
            .mock_room_event()
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
            .respond_with(move |r: &Request| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
                let segments: Vec<&str> = r.url.path_segments().expect("Invalid path").collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
                let event_id_str = segments[6];
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
                let event_id = EventId::parse(event_id_str).expect("Invalid event id in response");
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
                let event = f
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
                    .text_msg(format!("Message {event_id_str}"))
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
                    .event_id(&event_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
                    .server_ts(MilliSecondsSinceUnixEpoch::now())
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
                    .into_raw_sync();
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
                ResponseTemplate::new(200)
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
                    .set_delay(Duration::from_millis(50))
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
                    .set_body_json(event.json())
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
            .mount()
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
            .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
        client.event_cache().subscribe().unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
        (server, client, room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
    });
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
    let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
    assert!(!pinned_event_ids.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
    assert_eq!(pinned_event_ids.len(), PINNED_EVENTS_COUNT);
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
    let count = PINNED_EVENTS_COUNT;
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
    let name = format!("{count} pinned events");
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
    let mut group = c.benchmark_group("Load pinned events");
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
    group.throughput(Throughput::Elements(count as u64));
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
    group.sample_size(10);
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
    group.bench_function(BenchmarkId::new("Load pinned events [memory]", name), |b| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
        b.to_async(&runtime).iter(|| async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
            let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
            assert!(!pinned_event_ids.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
            assert_eq!(pinned_event_ids.len(), PINNED_EVENTS_COUNT);
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
            // Reset cache so it always loads the events from the mocked endpoint
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
            client
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
                .event_cache_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
                .lock()
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
                .unwrap()
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
                .as_clean()
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
                .unwrap()
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
                .clear_all_linked_chunks()
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
                .unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
            let timeline = TimelineBuilder::new(&room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
                .with_focus(TimelineFocus::PinnedEvents)
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
                .build()
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
                .expect("Could not create timeline");
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
            let (items, _) = timeline.subscribe().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
            assert_eq!(items.len(), PINNED_EVENTS_COUNT + 1);
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
            timeline.clear().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
    });
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
        let _guard = runtime.enter();
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
        drop(server);
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
    group.finish();
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
criterion_group! {
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
    name = room;
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
    config = Criterion::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
    targets = receive_all_members_benchmark, load_pinned_events_benchmark,
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
criterion_main!(room);