-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[perf] disabling _populateCache() in loader in favor of an on-demand process to create internal module info based on the raw meta when the module is needed #1581
Conversation
…to addModules to the metas when they are really needed
This is proven to be worth it, bumping the priority of this one. A clean up is needed! /cc @ekashida |
* @param {string} name of the module | ||
* @private | ||
*/ | ||
_getModuleInfo: function(name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the accessor to the public moduleInfo
property be private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good question. It seems that moduleInfo
was public, but we were not expecting people to use it. That sounds off! http://yuilibrary.com/yui/docs/api/classes/Loader.html#property_moduleInfo
We could make getModuleInfo
public, and make moduleInfo
private (at least in the docs).
var rawMetaModules = META.modules, | ||
globalConditions = GLOBAL_ENV._conditions, | ||
globalRenderedMods = GLOBAL_ENV._renderedMods, | ||
internal = this._internal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to change this semicolon to a comma.
… is analyzed before addModule is called.
…hat those are taken in consideration even when those modules are not added thru addModule() call
… to support when: after
This PR is ready for a formal review. |
for (i in cache) { | ||
if (cache.hasOwnProperty(i)) { | ||
self.moduleInfo[i] = Y.merge(cache[i]); | ||
_getModuleInfo: function(name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the new accessor for module info, although, in some places we are intentionally using the internal hash this.moduleInfo
to avoid the penalty of the function call, even though there is a short circuit in this method for that case. Ideally, we can test this and simply drop the property in favor of the accessor, or we can replace the property with a define property (when that feature is available), and that will help us to control this better.
if (cache.hasOwnProperty(i)) { | ||
self.moduleInfo[i] = Y.merge(cache[i]); | ||
if (this.moduleInfo[name]) { | ||
return this.moduleInfo[name]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could optimize this lookup by not doing it twice, but once and bind it to a local var.
This should be in a 3.x release because it changes the public |
defaults = META.modules, | ||
cache = GLOBAL_ENV._renderedMods, | ||
i; | ||
getModuleInfo: function(name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this staying private even though the method name does not start with an underscore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is ok to make it public, as the accessor for module info.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API doc comment should be updated then.
The updates look good. I added a comment about whether |
delete mod.langCache; | ||
delete mod.skinCache; | ||
mod.langCache = undefined; | ||
mod.skinCache = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Setting a property to undefined
is different than delete
ing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
most js engines will go crazy about deleting a member of an object, part of the micro optimizations they do thru the synthetic classes. Deleting a member of a module meta object will simply throw the class away and will require extra work to access any member.
This is obviously not relevant for dictionaries/hashes, which is not the case here.
👍 |
What do the coverage numbers look like for the code that was changed in this diff? Since this is loader I want us to be extra cautious. We might want to add some tests, especially for the new Also we need may need User Guide updates. |
👍 |
Initial analysis:
For a very basic page with all necessary modules in the page upfront, and a YUI().use(‘json-stringify’) call, will run under 20ms in chrome/desktop, but it takes more than 100ms in safari iPad3.
For a more complex example, adding more modules (app level modules), we can clearly see more degradation of the initialization, even though none of those modules are "used". Here is an example using modules from screen.yahoo.com (almost 400 new modules + the 407 modules from yui core):
http://jsbin.com/iPOriSAS/1/edit
For what we can see in the profiling, _populateCache(), which is executed once per Y instance, it will do a one time operation to digest all the raw metadata from YUI.Env.modules, passing them thru addModule() method that will do a basic expansion of the meta without doing the recursive computation to expand the dependencies, that happens on demand.
By using the loader built of this branch, we seed a good deal of improvement, and overall a constant time to use despited the number of extra modules added thru the metas, which is exactly what we are looking for.
Changes:
Loader keeps a member called
moduleInfo
, which is a hash with the internal meta for every module. This structure is used within the loader and from yui.js as well. This PR is proposing a shim on top of that structure to se can populate the cache for each individual module from the raw meta structureYUI.Env.modules
when we needed. This will help to not have to walk thousand of modules as part of the initialization of the first YUI instance.A quirk on this new way of initialization is that conditional modules are not requested by any other module in the require chain, which means they will have to be analyzed before applying any operation to compute dependencies, for that, we are just warming up the conditional cache (which is normally 16 entries for yui core), instead of just initializing all core modules (400+).
Performance tests
command:
yb src/loader/tests/performance/loader-tests.js --phantom --ref v3.14.1 --ref v3.15.0
loader resolve core modules
loader resolve application modules
caculate dependecies
loader constructor with global cache
loader constructor without cache