When Drupal includes files, it almost always test the file existence on disk. While I understand why this could have been a good idea, it is in reality almost always useless.
drupal_get_filename() relies on the system_list() function cache, which is prepopulated the first system_list() hit. Meanwhile, drupal_load() function relies on drupal_get_filename().
Those functions are called only in the context where both the module exists and the system database table is complete (and cached). The file_exists() calls are redundant and useless.
This has serious drawbacks, when you use a slow filesystem with a good OPCode cache, all file system calls are supposedly routed to the OPCode cache instead when including PHP files, which actually lives the file system alone. This is specifically true when you disable the stat command to be run by the OPCode cache.
Doing excessive file_exists() calls, you force the file system to do it twice (if the OPCode cache does stat() calls) and you explicitely wake up the file system. This is a serious performance problem especially on slow file systems.
This is quite a stupid thing to do, considering that Drupal knows the module list at all time, has it into database, and moreover does cache it with the system_list() function.
What I propose here is a simple patch that keeps the backward compatibility for contrib module, by adding new functions:
module_load_system_include()as a replacement ofmodule_load_include(), fully relying onsystem_list()cache, with no hits into the database nor the filesystem. Keeping themodule_load_include()as-is for modules using it to attempt stupid file discovery using it.drupal_get_system_filename()as a replacement ofdrupal_get_filename(), fully relying onsystem_list()cache, with no hits into the database nor the filesystem. Keeping thedrupal_get_filename()as-is for modules using it to attempt stupid file discovery using it.- Some hacks inside the legacy functions to speed them up, testing first with the above functions if the file exists before trying to do any file system access.
And changing those details:
- At install time, prepopulates some essential modules into
system_list()cache while the database isn't up. - At install time, when database is up, fallback on normal behavior.
- Changed the
module_enable()function behavior, clears thesystem_list()cache before actually loading the module files, which will allow thedrupal_load()function to rely on a fresh rebuilt cache. This has no incidence on the core behavior. - Aggregated both bootstrap and others
system_list()cache entries into one, in order to ensure we'll have one and only one SQL or cache backend query in order to fetch all needed information.
Then fixes all core modules to use them in order to avoid excessive file_exists() calls (except in the update module which does some tricky install stuff without being properly enabled).
While doing this on a Vanilla core, this saves a lot of file system direct access. Even better, with a complex a big site, using Drupal Commerce, Rules, Views, CTools, etc... most of them have been ripped of, and everything is working fine, including install.
Notice that the core has a quite invasive patch, in order to have a positive impact all core modules must use the new functions and not the old ones.
Everything else works gracefully, legacy functions remain in order for contrib to have the right behavior.
| Comment | File | Size | Author |
|---|---|---|---|
| #1 | 1715222-1-d7-remove_useless_file_stat.patch | 30.8 KB | pounard |
Comments
Comment #1
pounardHere is the patch.
EDIT: Please note that I don't want to apply this to D8. While this is a great performance boost for D7 on slow filesystems, D8 is on the move with module handling, with tasks such as making the
.modulefile optional, which by design disrupt this patch methodology.Comment #3
pounardTests are failing, but normal runtime is not. My guess is that simpletest has some problems.
Comment #4
pounardJust some numbers: on a D7 vanilla install using the standard profile, hitting the home page, no content, as uid 1 will reduce the number of
file_exists()calls from 63 to 34. Considering thatfile_exists()calls number is proportional to the enabled modules count, this is significant as soon as you have a slow FS (especially network storage).Comment #5
martijn houtman commentedOn my setup with about 175 modules enabled, is_file() is being called 981 times, according to XHProf. It appears to be responsible for about 3.3% of the total Wall time. The above patch reduces the time spent on loading the page, but when applied, XHProf no longer works, and I have no idea why.
Anyhow, this patch is worth the research on large sites with lots of modules.
Comment #6
pounard3% of Wall time, you probably have a very efficient file system! 1000 FS access is way too many.
Comment #7
martijn houtman commentedNo, I think the rest of the execution is just dead slow ;-)
But yeah, it's way too many. Once the files are in place, Drupal should not have to check every time. That's what this patch is about, right?
Comment #8
pounardIt is!
Comment #9
Chris CharltonIt seems like this ticket got stalled?
Comment #10
pounardPretty much, but I think that recent commits already fixed a few of these.
Might worth the shot to see how the 7.x latest behave, but I don't have any time soon to do this.
Comment #11
joseph.olstadSubscribing, it'd be great if this patch was rerolled for the latest 7.x dev and some performance profiling was ran against it (xhprof).
Comment #12
joseph.olstadRemoving file_exists was already done in 8.x , is ready for backport, see related issues
Comment #13
stefan.r commented