Same principle as #523682: Small optimization to theme(), the registry pre-loads files required by module_implements() on a router path basis, meaning drupal_function_exists() should never need to autoload files for those functions except on cache misses, hence a straight function_exists() works in all cases.
According to webgrind, this makes module_invoke_all() about 30% faster internally, and 15% faster in total. I doubt it'd be measurable with ab, but 1/200th of request time for 30 characters of code ain't bad. See #513984: Find bottlenecks in HEAD - meta issue for where they all add up.
I'm starting to think drupal_function_exists() needs renaming to drupal_autoload_function().
| Comment | File | Size | Author |
|---|---|---|---|
| #2 | module_invoke_all.patch | 900 bytes | catch |
| module_invoke_all.patch | 763 bytes | catch |
Comments
Comment #1
Crell commentedAs with the linked issue, I'm OK with this but it should be documented as to why we're doing something so weird.
Comment #2
catchMakes sense.
Comment #3
Crell commentedAssuming the bot still likes it.
Comment #4
moshe weitzman commentedThe justification before was "theme() can be called hundreds or thousands of times on a page." module_invoke_all() cannot possibly achieve those numbers. and before you start, i have never seen a site which chooses 300 comments per page. or at least they are statistically negligible.
Comment #5
catch@moshe, the function I actually had in mind was field_access() - which calls module_invoke_all() for each field rendered on a page - that can easily run into the hundreds, and it's likely to as we convert more and more things to fields (comment body and subject, comment uploads, node username, taxonomy term fields for each vocabulary etc.)
Not to mention module_invoke_all() is an API function in the critical path which any module can use, so we have no idea what contrib modules will do with it. Just because core currently doesn't call it 3000 times per page, doesn't mean there aren't scenarios in contrib which could cause such a thing to happen.
While we're on 300 comments, I consider Drupal.org statistically significant for performance improvements, even small ones.
Comment #6
catchForgot to mention, the ideal thing here would be to remove the check entirely, since module_implements() already runs drupal_function_exists() (but doesn't check the return value - so we can't remove that check at the moment or we risk a lot of fragility in relation to undefined function errors). This information from Berdir in #drupal answering someone else's question, but relevant here.
Comment #7
catchOK so just to be clear on what the numbers are. In microbenchmarks (1 million iterations, no xdebug), drupal_function_exists() costs three times as much as function_exists().
nothing at all: 0.241947174072 seconds
function no_op() {}: 0.451041936874 seconds
function_exists() 0.640865802765 seconds
drupal_function_exists: 1.68899798393 seconds
We currently call it about 900 times on a front page with 30 comments, and 873 times on user/admin/permissions.
1 million iterations has it as 1.5 seconds more expensive than function_exists(), so 1,000 calls would be ~1.5ms. Even on pages like user/n where it's only called 150-odd times, drupal_function_exists() is still within the top 50 most expensive function calls for self time. Most pages on my laptop render in about 100-200ms (when measured via devel, not when profiling) - so, for every 1,000 pointless calls we remove, we get back ~1% of page execution time, ish. If you combine this with the patches to url(), drupal_lookup_path(), drupal_render(), theme(), check_plain(), some committed, some not, we're looking at in the region of 3-10,000 or so similar removals depending on which page you're looking at - which would tally with the 5% improvement I measured on #513984: Find bottlenecks in HEAD - meta issue.
For comparison, node_load() caching would have got us around a 5-10% improvement in core - see http://drupal.org/node/111127#comment-1147475 / http://drupal.org/node/111127#comment-1495318 - but which requires maintaining the cache properly, a lot of additional code weight to do that, whereas most of these 'micro' patches are less than 1-2k each.
Comment #8
pwolanin commentedIf we remove the registry it might be helpful to add back some caching of the modules that implement a hook inside module_invoke all (or module_invoke)
Comment #9
Crell commentedGiven how ingrained the registry is at this point, and how close we are to freeze, I don't think removing it is even a viable option anymore. It would totally break HEAD in a myriad of ways. We should focus on optimization at this point, IMO.
Comment #11
catchRegistry was ripped.