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

Startup time improvements #222

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft

Startup time improvements #222

wants to merge 7 commits into from

Conversation

darrenburns
Copy link
Owner

@darrenburns darrenburns commented Mar 11, 2025

Some improvements to startup time. I believe this PR will reduce startup times by around 100-170ms on average.

  • OpenAPI Pydantic stuff was adding ~60ms to startup time despite only being used in a subcommand. It's now loaded lazily when required.
  • Widgets inside TabPanes are now lazily loaded. It's hard to measure precisely (although I did try!), but it may have shaved 50-100ms off of time to first paint.
  • 10ms were shaved off by simply moving a commonly used class out of a rarely used module that is expensive to import. The rarely used module can then be imported lazily when required.
  • Switching to the C implementation of the PyYAML loader shaved off 5ms for a collection with 18 requests. This will likely not be a noticeable improvement unless you collection is huge, but every little helps.

Empty collection startup time

The improvements below apply to loading Posting such that it doesn't load any saved requests on startup.

Deferring open_api_pydantic import

Importing the open_api pydantic stuff is only required for a single CLI command, yet takes up 64ms in the importtime log.

By deferring this import to only happen when the posting import command runs, we can shave this time off of the startup time of the main Posting TUI. The difference here felt noticeable to me, before performing measurements.

  • Change: 145ms -> 83ms (before -> after time to app ready, empty collection)
  • Improvement: ~62ms (43%).

Moving HelpData class to its own module

This class was contained alongside the HelpScreen class, and is imported into many modules. However, the HelpScreen is never required on startup. HelpData (cheap import) is required on startup, HelpScreen (expensive import) is not.

Modules now import HelpData from a new help_data module, which is far quicker to import.

  • Change: 83ms -> 73ms (empty collection)
  • Improvement: ~10ms (12%)

Pydantic -> Dataclasses for themes

I wasn't making use of the features of Pydantic when it came to themes, so by switching to dataclasses, we can get rid of some minor overhead.

  • Change: 73ms -> 71ms (empty collection)

On testing this later on, I was consistently finding that switching Pydantic was adding 5ms overhead over dataclasses.

Deferred loading of watchfiles library

Posting watches for changes to external files using watchfiles. It does this watching asynchronously, and the watching logic is not required by the main Posting task, so by deferring loading, we can start up faster.

  • Change: 71ms -> 64ms (empty collection)

Using Textual's Lazy widget for tab panes

Without lazy:

  • 500ms TTFP (2.48s launch, 2.98s first paint)
  • 430ms TTFP (4.37s launch, 4.86s first paint)
  • 500ms TTFP (6.88s launch, 7.38s first paint)
  • 480ms TTFP (10.17s launch, 10.65s first paint)

With lazy (request area tabs only):

  • 400ms TTFP (17.58s launch, 17.98s first paint)
  • 300ms TTFP (19.62s launch, 20.00s first paint)
  • 370ms TTFP (21.50s launch, 21.87s first paint)
  • 370ms TTFP (23.55s launch, 23.92s first paint)

With lazy (all tabs):

  • 450ms TTFP (2.20s launch, 2.65s first paint)
  • 380ms TTFP (5.75s launch, 6.13s first paint)
  • 370ms TTFP (7.45s launch, 7.82s first paint)
  • 380ms TTFP (10.13s launch, 10.51s first paint)

I was suspicious of the Footer widget, so done some tests with the footer removed. However, it didn't have much impact.

No footer + all lazy:

  • 370ms (5.76s, 6.13s)
  • 330ms (8.23s, 8.56s)
  • 350ms (9.98s, 10.33s)
  • 360ms (11.86s, 12.22s)

The biggest win was using Lazy in the request area tabs. It seems to have shaved 50-100ms off of the time to first paint.

Other ideas

  • If the user is using one of the built-in themes (i.e. they haven't specified a different theme in their config, then we could lazily load their themes directory when the command palette is opened rather than at startup).
  • The collection could be loaded in the background, without blocking the UI from loading (although collections do load extremely quickly, and this would likely not be worth the UI flicker that may result)
  • httpx takes 47ms to import according to python -X importtime, which is a significant amount. This is a substantial portion of Posting's cumulative import time ~122ms. The majority of this time in httpx is spent importing things for the httpx CLI, which isn't used in Posting, and these imports are expensive.
    • httpx._main imports a bunch of stuff from e.g. Syntax Rich, which imports stuff from pygments, click etc, even though it's entirely stuff relating to the httpx CLI, which I don't need. This stuff is being imported despite it never being used in Posting.
    • The solution might be to fork httpx here, but I'll also raise an issue. Not sure there's a solution other than moving the CLI into a separate repo, which is its own headache.

Request loading improvements

Using PyYAML CLoader

PyYAML has a C implementation, which can be used if libyaml is available on the host system.

Switching to this load time when loading a collection of 18 requests from 72ms to 67ms. The time taken loading and parsing the YAML files reduced from 8ms to 3ms.

  • Change: 72ms -> 67ms (loading 18 requests).

@darrenburns darrenburns linked an issue Mar 11, 2025 that may be closed by this pull request
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.

[Minor] Feels little slow to launch
1 participant