arcade
arcade copied to clipboard
Look into improving Arcade's startup time
Arcade can be pretty slow at startup. Especially the first time you launch it. The OS will do some caching causing future startups faster. Investigate what is taking up all this time. We know font loading is one of the reasons.
Do we need to preload these default fonts? The examples seem to be the main place they're used.
https://github.com/pythonarcade/arcade/blob/c5ac3504204098dc85ee93782cbe40ace4723ae2/arcade/init.py#L571-L583
Defaults in the API seem to use Arial or calibri: https://github.com/pythonarcade/arcade/blob/bf28ded0f25bcefad70e85ae1bd6a02900404748/arcade/gui/widgets/text.py#L64 https://github.com/pythonarcade/arcade/blob/2a7c1f1513e3439a05adca806f761a7cc0a50a3f/arcade/text.py#L176 https://github.com/pythonarcade/arcade/blob/2a7c1f1513e3439a05adca806f761a7cc0a50a3f/arcade/text.py#L638 https://github.com/pythonarcade/arcade/blob/2a7c1f1513e3439a05adca806f761a7cc0a50a3f/arcade/text.py#L553
Once I'm done with some current projects, I can look at replacing this preload list with a libre font that's metrically compatible with Arial as part of #1135.
I would love a patch to remove this default font loading. It is both slow, surprising (I'm not using any text in my application, but pay the font loading cost), and incidentally causes issues with Bazel build integration because Bazel does not support any files with whitespace (so it ignore them from the wheel): https://github.com/bazelbuild/rules_python/issues/617
So that means there are two issues:
- Resource files containing spaces
- Avoid triggering pyglet's system font loader
@ClintLiddick Quick fix for whitespace in files names now in development at least : https://github.com/pythonarcade/arcade/commit/f012e7b220b66aec8a74ea99b1a904119b97cd5e
We can probably find some way to disable the font loading. The issue is not the font loading itself. That's pretty fast. The real issue is that pyglet will scan all system fonts when you access the pyglet.text
module proxy. This is then cached in the OS so it will be more or less instant next time your run your game (within the cache time)
Still. It's definitely annoying when you don't use text.
Recap of what I was told in the pyglet Discord today:
- Win32 GDI (Win 7-ish and fallback?) font loading currently scans the system font directory
- I suggested a pyglet config flag
- Ben suggested deferring font loading by default to allow people who ship their own fonts to use them
That's an upstream change as I understand it, but one that seems like it should be simple if I can figure out where the font loading actually gets invoked.
We did some testing the other day. The font scanning is gone on windows since we no longer use GDI.
- Media/sound initialization is the most time consuming
- The input/jotstick module can add another 0.2 - 0.4s for directinput
Startup times will rely on OS. I haven't tested for linux and mac.
This is the worst case situation for me after profiling over a few days
355/1 0.002 0.000 4.193 4.193 <frozen importlib._bootstrap>:1349(_find_and_load)
258 3.987 0.015 4.000 0.016 {built-in method time.sleep}
355/1 0.001 0.000 3.883 3.883 <frozen importlib._bootstrap>:1304(_find_and_load_unlocked)
346/1 0.002 0.000 3.883 3.883 <frozen importlib._bootstrap>:911(_load_unlocked)
314/1 0.001 0.000 3.883 3.883 <frozen importlib._bootstrap_external>:988(exec_module)
786/2 0.001 0.000 3.883 1.942 <frozen importlib._bootstrap>:480(_call_with_frames_removed)
314/1 0.002 0.000 3.883 3.883 {built-in method builtins.exec}
2/1 0.000 0.000 3.883 3.883 arcade\__init__.py:1(<module>)
1 0.000 0.000 3.823 3.823 arcade\sound.py:1(<module>)
1 0.000 0.000 3.822 3.822 pyglet\media\__init__.py:1(<module>)
84/42 0.000 0.000 3.236 0.077 {built-in method builtins.__import__}
3958/3714 0.002 0.000 3.185 0.001 <frozen importlib._bootstrap>:1390(_handle_fromlist)
35 0.002 0.000 3.049 0.087 ctypes\__init__.py:343(__init__)
1 0.000 0.000 3.048 3.048 pyglet\media\codecs\__init__.py:40(add_default_codecs)
1 0.000 0.000 3.039 3.039 pyglet\media\codecs\__init__.py:88(have_ffmpeg)
1 0.000 0.000 3.039 3.039 pyglet\media\codecs\ffmpeg_lib\__init__.py:1(<module>)
7 0.000 0.000 3.032 0.433 pyglet\lib.py:81(load_library)
21 0.000 0.000 3.027 0.144 ctypes\__init__.py:459(LoadLibrary)
1 0.000 0.000 1.964 1.964 pyglet\media\codecs\ffmpeg_lib\libavcodec.py:1(<module>)
1 0.000 0.000 0.652 0.652 pyglet\media\codecs\ffmpeg_lib\libavformat.py:1(<module>)
1 0.000 0.000 0.531 0.531 pyglet\media\codecs\ffmpeg_lib\libavutil.py:1(<module>)
1 0.000 0.000 0.447 0.447 arcade\application.py:1(<module>)
1 0.000 0.000 0.418 0.418 pyglet\media\codecs\ffmpeg_lib\libswscale.py:1(<module>
Recap of what I was told in the pyglet Discord today:
- Win32 GDI (Win 7-ish and fallback?) font loading currently scans the system font directory
- I suggested a pyglet config flag
- Ben suggested deferring font loading by default to allow people who ship their own fonts to use them
That's an upstream change as I understand it, but one that seems like it should be simple if I can figure out where the font loading actually gets invoked.
Clarification, Windows 7 does use DirectWrite as well in Pyglet, it just doesn't have all the features that 8.1+ does (colored font characters/emojis for example).
Right now when you use a tuple of font names, a call of have_font
is called for each font name on creation. This triggers pyglet to check the loaded custom fonts if a font exists, if it doesn't, then it will enumerate all system fonts to check for the name. While the initial call can take a while, a result against the database is cached (changed since arcade 2.6 was released) at least. On my system it took 0.00449824333190918
for 1031 entries, which isn't too bad in my opinion. Even if I doubled my fonts, it would only be 0.008 seconds.
That being said I did investigate a bit, and there is a way to check if a font exists for GDI+ without enumerating all of the fonts. With that fix in, it took 0.00024770002346485853
to check if a font exists. I can push this out to pyglet so it can be in the next version. As long as the latest pyglet is still compatible with Arcade 2.6, or whoever is using GDI+ for legacy/performance reasons, it should be significantly faster. I would be curious to see the load time difference between the old way and new way.
@caffeinepills We don't really worry about 2.6 at this points, but I'm sure that's a great addition for other pyglet users if people are using GID. I'm not sure how widespread that is.
Looking into media and input initialization will probably benefit more people? I don't know if there's any improvements that can be done here.
In arcade we should start by not importing sound/media/input on arcade import by default.