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

Optimize AssetRegistry class #5457

Merged
merged 8 commits into from Jul 4, 2023
Merged

Optimize AssetRegistry class #5457

merged 8 commits into from Jul 4, 2023

Conversation

willeastcott
Copy link
Contributor

The AssetRegistry class is incredibly inefficient. This PR massively optimizes it.

Before (~62ms to collapse a node in the 3D Tiles engine):

image

After (~3ms to collapse a node in the 3D Tiles engine):

image

Note, there is an API change here. AssetRegistry#find now returns undefined instead of null if no match is found. This matches how Array#find works.

I confirm I have read the contributing guidelines and signed the Contributor License Agreement.

@willeastcott willeastcott added performance Relating to load times or frame rate area: assets labels Jul 4, 2023
@willeastcott willeastcott self-assigned this Jul 4, 2023
Copy link
Member

@slimbuck slimbuck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

such a huge improvement!

src/framework/asset/asset-registry.js Outdated Show resolved Hide resolved
src/framework/asset/asset-registry.js Outdated Show resolved Hide resolved
@willeastcott willeastcott merged commit 7e68163 into main Jul 4, 2023
7 checks passed
@willeastcott willeastcott deleted the opt-assetregistry-remove branch July 4, 2023 11:19
@vogloblinsky
Copy link

👏🏻

@Maksims
Copy link
Contributor

Maksims commented Jul 4, 2023

Looks like it was inneficient build of names-asset index, which was using indices within list of assets instead of ID's of assets.
While findAll and find methoda used such indices and were really fast.
Now it has to iterate through all assets and does not use indices.
Also it allocates arrays from set on many of those operations.

How does this perform on projects with thousands of assets? I'm afraid it can be slower and lead to GC stalls.
More efficient way of updating cache and names index without iterator - will improve the bottleneck and will not reduce the performance with many assets in a project.

@willeastcott
Copy link
Contributor Author

@Maksims Array.from/find/filter are extremely fast+optimized. ArrayRegistry#find/findAll won't be as fast as they were, but they're still very fast. The important thing is that AssetRegistry#remove is now no longer rebuilding huge looks up indexes every time it's called. This PR is basically a balance between simplicity and performance (I could have introduced more Maps but chose not too until it can be demonstrated there's an actual problem).

@kungfooman
Copy link
Collaborator

kungfooman commented Jul 4, 2023

How does this perform on projects with thousands of assets? I'm afraid it can be slower and lead to GC stalls.

IMO it's a generic issue with Set's so far, they expose no filter/find/map methods. Hopefully in ES7, in the meantime we can just keep our eyes open for efficient/non-GC-causing polyfills.

Edit: Just made up a quick polyfill, without testing too much, but the principle should be clear:

Set.prototype.filter = function(test) {
    const ret = [];
    this.forEach((val, idx, set) => {
        if (test(val, idx, set)) {
            ret.push(val);
        }
    });
    return ret;
}
const nums = new Set([1, 2, 3, 4, 5, 6, 7, 8]);
nums.filter(_ => _ > 5);

Edit 2: found something official: https://github.com/tc39/proposal-collection-methods

IMO we should aim for a polyfill for this (less GC).

mvaligursky pushed a commit that referenced this pull request Jul 5, 2023
* Optimize AssetRegistry class

* Update src/framework/asset/asset-registry.js

Co-authored-by: Donovan Hutchence <slimbuck7@gmail.com>

* Update src/framework/asset/asset-registry.js

Co-authored-by: Donovan Hutchence <slimbuck7@gmail.com>

* if consistency

* null -> undefined

* Lint fixes

* Improve docs

* Restore return of null for find

---------

Co-authored-by: Donovan Hutchence <slimbuck7@gmail.com>
@yohami
Copy link

yohami commented Jul 13, 2023

_assets is not longer browsable, any way to retrieve the list manually?

@willeastcott
Copy link
Contributor Author

willeastcott commented Jul 13, 2023

const assets = this.app.assets.list();

@Maksims
Copy link
Contributor

Maksims commented Jul 14, 2023

Coming back to optimizations, unfortunately lack of indexing lead to major performance regression when using find and findAll.
In large projects with thousands of assets (tested on project with 2.5k of assets).
In engine 1.63.6 it used to take bellow 0.01ms for calling find (when hitting asset at the end of list) or findAll.
In engine 1.64.3+ it takes now 0.2ms for the same call. That is ~20x times regression in performance.

@willeastcott
Copy link
Contributor Author

You're calling AssetRegistry#find/findAll every frame?

@kungfooman
Copy link
Collaborator

I guess the Set polyfill would solve that? Is it the Array.from overhead + GC?

@Maksims
Copy link
Contributor

Maksims commented Jul 14, 2023

The particular project has a loader for custom level format and uses names of assets for referencing. And during loading it takes significantly longer to find all assets due to this performance regression.
Additional index by name as it was before (but without awful part of re-indexing), just like for url - will fix this problem.

@willeastcott
Copy link
Contributor Author

Wanna do a PR? 😉

@Maksims
Copy link
Contributor

Maksims commented Jul 14, 2023

Wanna do a PR? 😉

#5480 (review)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: assets performance Relating to load times or frame rate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants