Repository navigation
Replies: 1 comment
|
Update: The current version is 0.3.51. The next version is currently under review by Google and will include the update using the native implementation instead of XAML. Once it is available on the store, you can compare the two versions (0.3.51 vs the new release). |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
We ship an end-to-end encrypted messenger on .NET MAUI, live on the Play Store:
https://play.google.com/store/apps/details?id=com.dchat.dmessenger&hl=en-US
The build currently live there still runs stock CollectionView + XAML, and the real-world numbers
are rough: cold start is about 5 seconds before the app is usable, and opening a chat conversation
from the chat list, which is CollectionView realizing the message list, takes about 3 seconds on
its own.
That navigation cost is our core complaint. We originally measured it at over 1.5 seconds from tap
to a fully rendered conversation, and profiling traced it to MAUI re-rendering the whole bubble
tree on every open, not to our own data layer. Every number in the table below, on both sides of
the comparison, includes the same 200 ms slide-in transition we deliberately run on every open,
that part is equal either way, so the gap between the two columns is entirely the framework cost.
Our next release replaces CollectionView and XAML with that native canvas renderer (Android
RecyclerView + View.OnDraw, iOS CALayer/CoreText planned) on the highest-traffic screens, and the
improvement is not small, see the table below. We would rather this get fixed at the framework
level than maintain a second rendering engine per platform, so we are posting the numbers behind
that decision.
This is not a scroll-recycling complaint. We know CollectionView recycles views during an
active scroll, that's confirmed and working as intended (issue #12057). What we measured is a
different cost: what it takes to realize a screen the FIRST time it is attached, whether that is a
true first navigation or reopening a chat that was already open once before. Recycling only helps
once a list is already live on screen; it does nothing for the attach itself.
We also know we did not follow every recommended pattern. Our incoming-message bubble template was
a deep, 679-line tree realizing 75 views per item, most of them hidden behind IsVisible for variant
content like the edited marker, reactions, or a reply quote, not the flat, fixed-height shape the
performance guides recommend. Some of our cost is on us. Even accounting for that, the floor we hit
only went away once we stopped asking MAUI to create handlers for that screen at all, which is the
part we think is worth a fresh look.
What we measured, Release/AOT, non-debuggable builds, same device, same chats. Every number
below includes the same 200 ms slide-in transition on both sides, so that part is a constant, not
part of the gap:
"Same chat, reopened" is the one that stood out to us: nothing about that chat had changed since
it was last on screen, and it still cost 943 ms and dropped frames, close to the cold-open number,
not close to instant. We assumed, reasonably, that keeping a view instance alive would amortize
that cost after the first visit, the same LazyView / caching pattern suggested in discussion
#22793. We tried exactly that, an instance pool that reuses a chat's content view object instead of
letting it get collected, and it barely moved the needle: inflating the XAML tree went from 352 ms
to 278 ms with the pool, but attaching that view to the live tree still cost roughly 1367 ms, every
time, pooled object or not. In our case the dominant cost is handler creation at attach time, not
object construction or XAML parse, and reusing the C# instance does not touch that.
Startup shows a version of the same thing before any list or navigation is even involved.
Instrumenting the main thread from launch showed three contiguous 2 to 3.3 second blocks, all
after our own splash screen had already dismissed, 87 to 361 Choreographer frames dropped in the
same window, all handler creation and first measure/arrange for the page tree, including views not
yet visible. Deferring an eagerly-attached view and lazily creating tab content that used to build
eagerly cut this from 9.3 seconds to smooth in about 4.7 seconds, purely by delaying attach-time
handler creation for content the user had not asked to see yet, not by making that creation any
cheaper when it does happen.
This has been raised before in different shapes, discussion #22793 on first-navigation cost,
#21580 and #24224 on item template rendering cost, but those threads are quiet now and the
underlying cost is still there as of our testing. What we can add is a shipped, real-world app
with users, and a direct before/after of what happens to the same screen once handler creation is
removed from the equation entirely.
We do not know the framework internals well enough to propose how this should be solved, that is
for the team who owns the rendering pipeline. What we can say clearly: is there a supported way,
or could there be one, to re-attach a page or a content view without full handler creation when
nothing about it has actually changed since it was last on screen? Right now the only way we found
to get that behavior is to stop using MAUI's rendering for the screen and manage it ourselves.
Happy to share full measurement methodology, or a minimal repro, if that helps.
All reactions