Bug #79572 [NEW]: The builtin autoloader function spl_autoload() does not use opcache
| From: | czirkos dot zoltan at gmail dot com | Date: | Thu, 07 May 2020 14:14:32 +0000 |
| Subject: | Bug #79572 [NEW]: The builtin autoloader function spl_autoload() does not use opcache | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-226948@lists.php.net to get a copy of this message | ||
From: czirkos dot zoltan at gmail dot com
Operating system: Linux 4.15.0-99-generic
PHP version: 7.2.30
Package: SPL related
Bug Type: Bug
Bug description:The builtin autoloader function spl_autoload() does not use opcache
Description:
------------
PHP 7.2.30-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: Apr 19 2020
07:50:50) ( NTS )
When the builtin autoloader loads a class, it will not use opcache.
It will always look for the file in the filesystem, regardless of
its status.
This causes performance degradation in a web server setting (eg. fpm).
I reproduced the problem in the cli sapi for easy testing, test
results and example files are below. Even though using opcache for
cli is not realistic, I simulated loading the class multiple times
by calling spl_autoload repeatedly. Please note that the problem
CAN BE REPRODUCED IN FPM by strace-ing the running fpm process as well.
Example files:
loadedclass.php:
<?php /* just an empty file */
test_spl.php:
<?php
set_include_path(__DIR__);
spl_autoload_extensions(".class.php");
foreach (range(1, 10000) as $i)
spl_autoload('loadedclass');
test_simple.php:
<?php
set_include_path(__DIR__);
function my_autoload($name) {
include $name . ".class.php";
}
foreach (range(1, 10000) as $i)
my_autoload('loadedclass');
The file test_simple.php contains an autoload function which is
almost equivalent to 'spl_autoload'. Note that it does NOT use
include_once,
but it uses the simple include command, this is important.
With opcache turned OFF (opcache.enable = 0, opcache.enable_cli = 0):
strace -c php test_spl.php
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
45.78 0.311463 8 40174 fstat
14.87 0.101157 10 10169 3 openat
13.46 0.091557 9 10313 mmap
12.96 0.088142 9 10151 munmap
12.15 0.082671 8 10170 close
Calling spl_autoload 10000 times shows tha the file is opened
10000 times (I don't know why it is fstat'ed 40000 times).
The other code produces the same results, as expected:
strace -c php test_simple.php
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
45.00 0.297444 7 40174 fstat
14.95 0.098808 10 10169 3 openat
14.04 0.092786 9 10151 munmap
13.47 0.089016 9 10313 mmap
11.93 0.078883 8 10170 close
However with opcache turned ON (opcache.enable = 1, opcache.enable_cli =
1):
strace -c php test_spl.php
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
45.58 0.317274 8 40175 fstat
14.91 0.103765 10 10170 3 openat
13.52 0.094119 9 10315 1 mmap
12.90 0.089784 9 10152 munmap
12.12 0.084343 8 10171 close
For the spl autoloader, the file is still opened 10000 times! As
these are openat() and mmap() kernel calls, this means that the file
is also loaded and compiled. There is no speedup in the application.
However, with the hand-made autoloader, the head of the strace
looks like this:
strace -c php test_simple.php
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
20.44 0.002377 8 316 1 mmap
16.11 0.001873 8 230 mprotect
12.28 0.001428 8 171 3 openat
9.08 0.001056 6 172 close
8.81 0.001025 6 179 fstat
Execution time is almost zero, there are no 10000 file opens and loads.
The same thing happens in the fpm setting. For my application with
around 500 classes loaded, Apache benchmark shows 12-13 ms / page
load when I'm using the spl_autoload for loading the classes, and
9-10 ms / page load when I'm using the simple autoload function above.
By stracing the php7.2-fpm worker process, I get the same results:
when using spl_autoload, there are many openat() kernel calls,
but when using the simple autoload function, there openat() and
the fstat() calls disappear.
Test script:
---------------
# loadedclass.class.php
# empty file
# test_simple.php
set_include_path(__DIR__);
function my_autoload($name) {
include $name . ".class.php";
}
foreach (range(1, 10000) as $i)
my_autoload('loadedclass');
# test_spl.php
set_include_path(__DIR__);
spl_autoload_extensions(".class.php");
foreach (range(1, 10000) as $i)
spl_autoload('loadedclass');
--
Edit bug report at https://bugs.php.net/bug.php?id=79572&edit=1
--
Fix committed: https://bugs.php.net/fix.php?id=79572&r=fixed
Fixed in release: https://bugs.php.net/fix.php?id=79572&r=alreadyfixed
Need backtrace: https://bugs.php.net/fix.php?id=79572&r=needtrace
Need Reproduce Script: https://bugs.php.net/fix.php?id=79572&r=needscript
Try newer version: https://bugs.php.net/fix.php?id=79572&r=oldversion
Not developer issue: https://bugs.php.net/fix.php?id=79572&r=support
Expected behavior: https://bugs.php.net/fix.php?id=79572&r=notwrong
Not enough info: https://bugs.php.net/fix.php?id=79572&r=notenoughinfo
Submitted twice: https://bugs.php.net/fix.php?id=79572&r=submittedtwice
register_globals: https://bugs.php.net/fix.php?id=79572&r=globals
PHP version support discontinued: https://bugs.php.net/fix.php?id=79572&r=phptooold
Daylight Savings: https://bugs.php.net/fix.php?id=79572&r=dst
IIS Stability: https://bugs.php.net/fix.php?id=79572&r=isapi
Install GNU Sed: https://bugs.php.net/fix.php?id=79572&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=79572&r=float
No Zend Extensions: https://bugs.php.net/fix.php?id=79572&r=nozend
MySQL Configuration Error: https://bugs.php.net/fix.php?id=79572&r=mysqlcfg