Speeding up the Unreal Editor launch (again) by not loading 1585 files

jay_kyburz1 pts0 comments

Speeding up the Unreal Editor launch (again) by not loading 1585 files – Larst Of Us

Skip to content

Bluesky

Mastodon

LinkedIn

RSS Feed

Speeding up the Unreal Editor launch (again) by not loading 1585 files

After writing one article on how to speed up Unreal’s editor launch by not spawning 38000 tooltips and one on how to speed it up further by not opening 5500 files and getting the described fixes merged in for 5.8, I sort of stopped looking into the topic for a while. Surely, the low-hanging fruits were all found by now, right? Finding another substantial optimization wouldn’t be easy.

Well, time passed, and my curiosity took the better of me. "Just one short profiling session." I told myself, while starting up the profiler once again. And after only a minute of looking through the Superluminal trace, I found something that looked like I found gold:

Spending up to 8 seconds in one function call? This seems worth investigating, especially since it already sounds like it’s doing a lot of unneeded work. Loading "all culture data"? I’d prefer "loading the minimum amount of culture data needed to get to the editor", thank you very much.

To be fair, after multiple rounds of profiling, it was clear that the function wouldn’t always take 8 seconds and would finish a lot faster in many cases. Normally, during profiling, I do 3-4 runs to reduce the impact of random outliers, but in this specific case, the values just continued to vary, even after a dozen different runs. From as low as 0.4 seconds, up to a baffling 10 seconds:

Thankfully, Superluminal not only shows the call stacks but also how busy the threads were at certain points. Looking at the graph, the variance started to make more sense. 99% of the time the thread was blocked by short IO operations like those:

Or, to put it simply: The main thread reads hundreds of tiny files, one after another. We will get to what those files are and what we need them for in a minute, but this bit of information already explains why the measured times vary as much as they do. All those file reads are done synchronously during the editor start, a time at which a bunch of other threads is already loading other stuff, from the same drive. Based on timing and the activity of the other threads, we run into dozens or hundreds of of I/O bottlenecks and the thread spends most time just waiting for a chance to load the next file. Not an ideal situation, surely we can speed things up?

Finding out what files are even loaded

For a start, let’s see if we can figure out what the function does, and why it is even called.

The content of the function is actually quite straightforward:

It loops over a list of known ‘cultures’ and loads them. "What’s a culture?" you ask? This question is easy to answer by looking into the class definition: It’s basically a list of strings holding all the different names of a language (English name, native name, display name) and some meta data (Iso-code, verse-Identifier):

Not very exciting stuff so far. Why does it take so long to retrieve a few strings? Well, the first issue is that Unreal knows a lot of different languages/cultures. 837, to be exact. Multiplied with the 13 strings that are the content of every culture, that is already some work. But copying 10000+ strings in itself still wouldn’t be a big problem. The real issue is that all of those strings are not baked into the code, or put into one single file that is read once and cached for later use. Instead, all of those different information points are scattered over multiple files. It’s not even one file per culture, in most cases, the data for a single culture is already distributed over multiple files. Just to retrieve all the needed names for the culture "German," Unreal needs to open and load four(!) different files! This results in 1585 files being opened and closed (yes I added a counter for this), along with all the overhead that comes with it.

I’m pretty sure that refactoring this code to use only one file would result in a significant performance boost. But the type of refactoring that touches >1500 files is not one commonly accepted as a pull request. Isn’t there something simpler we could try first?

To load or not to load

Why would the editor even need to load all languages on every single start? Isn’t one language enough? Let’s see if the call to the function gives us any hints.

Okay, this makes some sense. According to the comment, all languages are loaded to avoid hitches later at runtime. This is a common optimization, especially in games. You are trading very annoying hitches during gameplay for a bit of initial loading time at the start of the application. For this optimization to make sense, however, you should make sure that hitch itself occurs in scenarios where it is more annoying than the longer launch time. In games, this is almost always the case; in application software, like the Unreal Editor, not so much (except during Play-In-Editor). Where could...

files editor unreal loading culture time

Related Articles