Categories
Programming Web Development

PHP’s include_once() Is Insanely Expensive

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”

@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.

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.

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.

The author has claimed it totally wrong here.
This test has nothing to do with the time cost of include or include_once.

In the test code, you are checking if the files was included or not when testing for include but for include_once, you are not.

Hence, for the include_once, it is executing the statement 1000000 times, while for the include, 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.

Leave a Reply to Robert Cancel reply

Your email address will not be published. Required fields are marked *