Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui): Fix performance of ReadReceiptTimelineUpdate::apply #4612

Merged
merged 1 commit into from
Feb 4, 2025

Conversation

Hywan
Copy link
Member

@Hywan Hywan commented Feb 3, 2025

This patch improves the performance of
ReadReceiptTimelineUpdate::apply, which does 2 things: it calls remove_old_receipt and add_new_receipt. Both of them need a timeline item position. Until this patch, rfind_event_by_id was used and was the bottleneck. The improvement is twofold and is as follows.

First off, when collecting data to create ReadReceiptTimelineUpdate, the timeline item position can be known ahead of time by using EventMeta::timeline_item_index. This data is not always available though, for example if the timeline item isn't created yet. But let's try to collect these data if there are some.

Second, inside ReadReceiptTimelineUpdate::remove_old_receipt, we use the timeline item position collected from EventMeta if it exists. Otherwise, let's fallback to a similar rfind_event_by_id pattern, without using intermediate types. It's more straightforward here: we don't need an EventTimelineItemWithId, we only need the position. Once the position is known, it is stored in Self (!), this is the biggest improvement here. Le't see why.

Finally, inside ReadReceiptTimelineUpdate::add_new_receipt, we use the timeline item position collected from EventMeta if it exists, similarly to what remove_old_receipt does. Otherwise, let's fallback to an iterator to find the position. However, instead of iterating over all items, we can skip the first ones, up to the position of the timeline item holding the old receipt, so up to the position found by remove_old_receipt.

I'm testing this patch with the test_lazy_back_pagination test in #4594. With 10_000 events in the sync, the ReadReceipts::maybe_update_read_receipt method was taking 52% of the whole execution time. With this patch, it takes 8.1%.

The call tree of ReadReceipts::maybe_update_read_receipt and its flamegraph before this patch…

Screenshot 2025-02-03 at 14 51 21 Screenshot 2025-02-03 at 14 51 43

and after…
Screenshot 2025-02-03 at 14 52 32
Screenshot 2025-02-03 at 14 52 48


@Hywan Hywan requested a review from a team as a code owner February 3, 2025 14:15
@Hywan Hywan requested review from stefanceriu and bnjbvr and removed request for a team and stefanceriu February 3, 2025 14:15
Copy link

codecov bot commented Feb 3, 2025

Codecov Report

Attention: Patch coverage is 82.35294% with 6 lines in your changes missing coverage. Please review.

Project coverage is 85.73%. Comparing base (9ab5547) to head (b59b0ea).
Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
...ix-sdk-ui/src/timeline/controller/read_receipts.rs 82.35% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4612      +/-   ##
==========================================
- Coverage   85.74%   85.73%   -0.02%     
==========================================
  Files         291      291              
  Lines       33306    33329      +23     
==========================================
+ Hits        28558    28574      +16     
- Misses       4748     4755       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@bnjbvr bnjbvr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement!

} else {
warn!("received a read receipt for a local item, this should not be possible");
}
}

/// Apply this update to the timeline.
fn apply(
self,
mut self,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change spurious? Doesn't seem required according to the rest of the diff…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is necessary because remove_old_receipt takes a &mut self.

This patch improves the performance of
`ReadReceiptTimelineUpdate::apply`, which does 2 things: it calls
`remove_old_receipt` and `add_new_receipt`. Both of them need an
timeline item position. Until this patch, `rfind_event_by_id` was used
and was the bottleneck. The improvement is twofold as is as follows.

First off, when collecting data to create `ReadReceiptTimelineUpdate`,
the timeline item position can be known ahead of time by using
`EventMeta::timeline_item_index`. This data is not always available, for
example if the timeline item isn't created yet. But let's try to collect
these data if there are some.

Second, inside `ReadReceiptTimelineUpdate::remove_old_receipt`, we use
the timeline item position collected from `EventMeta` if it exists.
Otherwise, let's fallback to a similar `rfind_event_by_id` pattern,
without using intermediate types. It's more straightforward here: we
don't need an `EventTimelineItemWithId`, we only need the position.
Once the position is known, it is stored in `Self` (!), this is the
biggest improvement here. Le't see why.

Finally, inside `ReadReceiptTimelineUpdate::add_new_receipt`, we use
the timeline item position collected from `EventMeta` if it exists,
similarly to what `remove_old_receipt` does. Otherwise, let's fallback
to an iterator to find the position. However, instead of iterating over
**all** items, we can skip the first ones, up to the position of the
timeline item holding the old receipt, so up to the position found by
`remove_old_receipt`.

I'm testing this patch with the `test_lazy_back_pagination` test in
matrix-org#4594. With 10_000
events in the sync, the `ReadReceipts::maybe_update_read_receipt` method
was taking 52% of the whole execution time. With this patch, it takes
8.1%.
@Hywan Hywan force-pushed the fix-ui-performance-read-receipt branch from 84ade87 to b59b0ea Compare February 4, 2025 14:47
@Hywan Hywan merged commit 77a67de into matrix-org:main Feb 4, 2025
40 checks passed
Hywan added a commit to Hywan/matrix-rust-sdk that referenced this pull request Feb 4, 2025
Hywan added a commit to Hywan/matrix-rust-sdk that referenced this pull request Feb 4, 2025
Hywan added a commit that referenced this pull request Feb 4, 2025
This patch adds #4601, #4608, #4612 and #4616 in their respective
`CHANGELOG.md`s.
toger5 pushed a commit to toger5/matrix-rust-sdk that referenced this pull request Feb 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants