Skip to content

Recover merged thread startup latency regression #163064

Closed

Description

@zanderso

On app startup, when the work to start the engine, launch the app, and set the Dart code running is serialized on the platform thread, the time to first interactable frame takes longer. This issue tracks investigating and mitigating the regression.

b/373761573

Activity

  1. zanderso commented on Feb 11, 2025

    @zanderso
    MemberAuthor
  2. added
    P2Important issues not at the top of the work list
    on Feb 18, 2025
  3. jtmcdole commented on Mar 12, 2025

    @jtmcdole
    Member

    @jason-simmons - can you add some before and after timing captures for the default flutter counter app?

  4. jason-simmons commented on Mar 12, 2025

    @jason-simmons
    Member

    Uploaded traces of the default counter app:

    These were taken on a Pixel 6a. The Flutter app process in the traces is called com.example.bug_163064.

    I added events to show two phases of app startup:

    • engineStartup
      • covers the interval from FlutterLoader.startInitialization through the engine's invocation of the Dart main function
    • firstFrameRender
      • the interval in the app's Dart code from the start of main through the completion of WidgetsBinding.waitUntilFirstFrameRasterized

    WidgetsBinding.waitUntilFirstFrameRasterized completes when the rasterizer finishes rendering the first frame on screen. Some apps that are reporting latency regressions are using that event to compute their startup metrics.

    In these traces, there is a small difference in the overall time from the beginning of engineStartup to the end of firstFrameRender. (176ms for merged threads versus 161ms for separate UI thread).

    For this minimal app the Dart ReadProgramSnapshot work does not consume much of this time (about 4ms).

    However, note the interleaving of Android host platform tasks and Dart execution on the merged UI/platform thread.

    In the separate UI thread trace, the firstFrameRender phase begins immediately after the end of engineStartup.

    But in the merged thread trace, there is a gap of 30ms before firstFrameRender begins. The platform thread is doing various Android-related work (some of which does not seem to be specific to Flutter) before it can execute the Dart message handling tasks.

    There is more latency while the platform thread continues to be split between Android work (such as layout of the Android activity) and Dart execution work until the first frame is rendered. With a separate UI thread the Android and Dart tasks can run concurrently.

  5. johnmccutchan commented on Mar 31, 2025

    @johnmccutchan
    Contributor

    Have we considered keeping the separate threads during startup and then merging them?

  6. jason-simmons commented on Apr 1, 2025

    @jason-simmons
    Member

    Initializing the engine on a separate thread and then shifting it to the platform thread will require changing many threading design assumptions.

    I tried a quick experiment of running an engine with --no-enable-merged-platform-ui-thread and calling MessageLoopTaskQueues::Merge for the platform and UI task runners at the end of Engine::Run.

    The first issue I saw was an assertion failure in an fml::WeakPtr captured in a lambda that was posted to the UI task runner before the merge and then used on the platform thread after the merge. (fml::WeakPtr can only be used on the thread where it was created)

    After that Dart_EnterIsolate failed a similar assertion which verifies that the isolate is running on its owner thread.

  7. johnmccutchan commented on Apr 1, 2025

    @johnmccutchan
    Contributor

    Given that we can't move the Android work to another thread, can you try and figure out how complicated it would be to actually move the engine from one thread to the other after initialization? Is it a week, month, or quarter of work?

  8. zanderso commented on Aug 18, 2025

    @zanderso
    MemberAuthor

    The internal customer is ready to proceed with performance with the --merged-platform-ui-thread=mergeAfterLaunch flag, and the ability to opt-out can be removed. I'll close this issue as fixed.

  9. github-actions commented on Sep 1, 2025

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  10. locked as resolved and limited conversation to collaborators on Sep 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listteam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions