I’ve always heard the include_once()
and require_once()
functions were computationally expensive in PHP, but I never knew how much. I tested the following out on my i7 2010 MacBook Pro using PHP 5.3.4 as shipped by Apple.
This first test uses include_once()
to keep track of how often a file is included:
$includes = Array();
$file = ‘benchmarkinclude’;
for($i=0; $i < 1000000; $i++){
include_once($file.‘.php’);
}
Took: 10.020140171051 sec
This second example uses include()
and uses in_array()
to keep track of if I loaded the include:
$includes = Array();
$file = ‘benchmarkinclude’;
for($i=0; $i < 1000000; $i++){
if(!in_array($file, $includes)){
include($file . ‘.php’);
$includes[] = $file;
}
}
Took: 0.27652382850647 sec
For both, the include had the following computation:
$x = 1 + 1;
Lesson learned: Avoid using _once if you can avoid it.
Update: That means something like this will theoretically be faster:
$rja_includes = Array();
function rja_include_once($file){
global $rja_includes;
if(!in_array($file, $rja_includes)){
include($file);
$rja_includes[] = $file;
}
}
14 replies on “PHP’s include_once() Is Insanely Expensive”
Makes one wonder how *_once() is actually implemented, since you apparently implemented it in a way that was about 3600% faster…
@Preed: I’ve been meaning to look it up, just haven’t had the chance. I first suspected (I don’t actually know) it does
file_exists()
, and perhaps other verification. But that seems unlikely as the distribution of the several times I tested this was pretty tight, I would have expected file IO to create a much larger distribution, or tail off once it’s cached. I saw nothing to that effect.Note that your implementation is potentially quadratic in the number of
rja_include_once
calls.I agree with you, but your solution isn’t correct because include path may implement multiple path.
You should use :
$path = substr($file,0,strrpos(“/”));
$file_name = substr($file,strrpos(“/”));
$rja_includes[] = realpath($path).$file_name;
instead of
$rja_includes[] = $file;
And I think that’s why include_once is so slow 🙂
@Blusky: Not really.
include()
uses the first path that’s true. This is repeatable. So your key is the same regardless of the path chosen in the backend.No, imagine you have the following hierarchie :
a.php -> include_once(“b.php”);
b.php -> include_once(“a/a.php”);
a/a.php -> include_once(“b.php”);
a/b.php
Will you include the second b.php ?
Assuming duplicate file names isn’t a bad practice already… you should always use absolute paths to your includes. It’s much faster. Disk IO is the biggest bottleneck on a server. Anyone who would use this sort of optimization is already doing that (I can’t think of many PHP open source projects that don’t do that already). Otherwise you’re essentially going through your path dir’s and doing a
file_exists()
, that means disk latency.I don’t think that use-case really exists for those reasons. This is an optimization, not a catch all.
You always use file name such as “/var/www/website/dir/file.php” when you use include ?
the reason why include_once and require_once is slower because it normalizes the path first (so in “/web/dir/mi.php” calling include(“../includes/foo.php”) will process it to “/web/includes/foo.php”).
Correction… PHP include is insanely expensive 😀
I tried taking your original include_once and just replace include_once with include and then test speed difference.
$ time PHP incNormal.php
real 0m29.794s
user 0m13.477s
sys 0m15.541s
$ time PHP incOnce.php
real 0m2.569s
user 0m1.216s
sys 0m1.336s
Also, as others have pointed out, your code is not functionally the same as include_once.
This article is inaccurate and dangerous for developers to stumble upon. Please update it. As Terrasque pointed out, include_once is actually MUCH faster than include. For another reference, see http://blog.seeit.org/2010/06/.....ce-debate/. Unless you specifically want to include a file multiple times, you should never use include.
The author has claimed it totally wrong here.
This test has nothing to do with the time cost of
include
orinclude_once
.In the test code, you are checking if the files was included or not when testing for
include
but forinclude_once
, you are not.Hence, for the
include_once
, it is executing the statement 1000000 times, while for theinclude
, it is executing the statement only once.The test does not prove anything as they differ by the fact that it is executed.
your benchmark comparision is not practical, as the $includes array always only contains one item. It will make a difference if this array get’s larger and the strings in it possibly longer (with subpaths and such).
I looked at this code and first thing i noticed is that he does keep track of exactly 1 item. Which makes this benchmark the biggest joke in the world.