I’ve been working on an MVC framework for PHP for a while now. I started way long ago but never really had enough time to sit down and “really” work on it. Various ideas and chunks of code from this soon-to-exist framework have already been put to use over at Fuzz. Don’t get me wrong - we’re not using some crippled piece of shit at Fuzz, it works & is pretty rad - but I’ve got a bunch to do before I can put this bad boy out as a standalone framework that the public can use.

Anyways… One goal for the framework is to deal with the nuisance of require’s and include’s. Its always easy to fall back on include’ing/require’ing more shit than you actually need. Also, you have all the supposed require_once/include_once performance issues to deal with. With all that in mind I wrote a little, crappy script to do some timing tests on require_once, include_once, require & include. What’s the performance difference between all these?

This script performs a require, require_once, include or include_once on 1,000 DIFFERENT files (0.txt, 1.txt thru 999.txt) and times how long that “batch” takes. Due to the results I saw (which I’ll get to later) I actually didn’t trust it and wrote some other code to verify what I was seeing with this script.

Here’s the code:

the beefy script:

<?
    $numFiles 
= empty ($argv[1]) ? 1000 $argv[1];
    
ini_set (‘memory_limit’, -1);

    function doIt ($operation)
    {
        global 
$numFiles;
        
clearstatcache ();                       
        
        `
touch *.txt`;        
        for (
$i 0$i $numFiles$i++) include “{$i}.txt”;
        
$startMemoryUse memory_get_usage ();
        
        
$startTime microtime (true);        
        
        for (
$i 0$i $numFiles$i++) 
        {
            eval (
“$operation \”{$i}.txt\”;”);
        }
        
        
$endTime microtime (true);
        
$endMemoryUse memory_get_usage ();
        
        return array (
                (
$endTime $startTime), 
                (
$endMemoryUse $startMemoryUse));        
    }    
    
    echo 
“————————————\n”;    
    
    if (! 
file_exists (($numFiles -1) . “.txt”))
    {
        echo 
“Prep: touch $numFiles files\n”;        
        for (
$i 0$i $numFiles$i++) `touch {$i}.txt`;
    }
    
    
$operations = array (
                        
‘require_once’‘require’
                        
‘include_once’‘include’);
    
shuffle ($operations);

    $results = array ();           
 &n
bsp;  echo 
“Running for $numFiles files:\t”
            
implode (‘, ’$operations) , “\n”;
    foreach (
$operations as $operation)
    {
        list (
$time$memory) = doIt ($operation);
        
$results[$operation] = $time;
        
        
printf (“%12s: %10d\n”$operation$memory);
    }

    asort ($results);
    echo 
“————————————\n”;
    echo 
“Results:\n”;
    
    
$winner array_slice ($results01true);
    
$quickest $winner[key ($winner)];
    
    
$fh fopen (“results_{$numFiles}.csv”‘a’);
    
printf (“\t%12s    %10s %10s\n”
            
‘faster by’‘og. time’);    
    foreach (
$results as $k => $v)    
    {
        
fwrite ($fh“$k\t$v\n”);
        if (
$v $quickest)
        {
            
printf (“\t%12s: + %10f %10f\n”
                
$k, ($v $quickest), $v);
        }
        else
        {
            
printf (“\t%12s:   %10s %10f\n”$k‘n/a’$v);
        }
    }
?>

So running this on my MacBook I get the following results… oh ya, don’t forget that the timing reflects 1,000 operations.

on MacBook - 1.8Ghz Core Duo

------------------------------------
Running for 1000 files:	require, require_once, include_once, include
------------------------------------
Results:
	                 faster by   og. time
	include_once:          n/a   0.088193
	require_once: +   0.013137   0.101330
	     require: +   0.030759   0.118952
	     include: +   0.045299   0.133492

on Fuzz’s staging server - RHEL on i686

------------------------------------
Running for 1000 files:	require_once, include, include_once, require
------------------------------------
Results:
	                 faster by   og. time
	require_once:          n/a   0.026613
	include_once: +   0.000050   0.026663
	     require: +   0.026534   0.053147
	     include: +   0.026741   0.053354

On both systems x_once turned out the fastest times. I ran the script a bunch of times on both my MacBook and on the Linux box and x_once always registered the fasted times. It looks like my MacBook favors include_once and the Linux box favors require_once… it looks like a 60/40 sort of split.

This didn’t jive with me so I generated 4 new scripts. Each of these scripts ditched all the fluff from the original and just called require_once, require, include_once and include 1,000 times in a row. I ran each of these simplified scripts (honestly, I dunno why I made the first script all beefy anyways…) a bunch of times on both boxes with pretty much the same results as the beefy script.

script generator:

<?
    
if (empty ($argv[1]))
        die (
            
“usage:\n\tphp {$_SERVER['SCRIPT_NAME']} ” .
            
“[require|require_once|include|include|once]“
            
);
            
    
$type $argv[1];
    echo 
“<?\n”;
    
    echo 
“clearstatcache ();\n”;
    echo 
“ini_set (’memory_limit’, -1);\n”;
    
    echo 
“`touch *.txt`;\n”;
    echo 
“for (\$i = 0; \$i < 1000; \$i++) include \”{\$i}.txt\”;\n”;    
    
    echo 
“\$start = microtime(true);\n”;
    for (
$i 0$i 1000$i++)
    {
        echo 
“$type \”{$i}.txt\”;\n”;
    }
    echo 
“\$end = microtime(true);\n”;    
    echo 
“\$time = (\$end - \$start);\n”;
    
    echo 
“\$fh = fopen (\”manualResults_1000.csv\”, ’a');\n”;
    echo 
“fwrite (\$fh, \”$type\\t\$time\\n\”);\n”;
    echo 
“echo \”$type: \$time\\n\”;”;    
    echo 
“\n?>”;    
?>

Something worth noting is the fact that I do a touch and include of each file before I start the timer. The reason for this is cuz I wanted to make sure that each run through had its fair shot of having the txt file available in memory; my cheap attempt at trying to make sure the playing field was fair for each operation. Also, the $operations array is shuffled each time the script is run. I didn’t want to get into a situation where the order the operations ran played a role in their performance.

I added some logging to the end of the scripts so that the timing output was shoved in a cvs. A quick and dirty companion script I wrote would crunch through the csv files to report averages. These resuts were in line with the output of the beefy script.

I’m pretty shocked at the results. I’d always read about the x_once operations being slower. Honestly if it wasn’t for the fact that the simple scripts gave the same results I’d assume I had a bug. This is actually why I wrote the simple scripts… I just didn’t beleive my original numbers.

So, for now, it looks like using include_once or require_once are actually faster than using include or require. Now, the next thing to check out is going to be the difference in memory consumption. I’ll get to that later cuz I’m gonna go drink now :) .

Now, don’t get me wrong, I know there’s functional differences between include_once, require_once, include and require. However, for the problem I’m trying to solve any of them will do (it’ll make more sense when I write about my framework).

BTW: Happy Thanksgiving!

2 comments

Arin on Nov 23, 2007 at 1:58 pm

Check out http://www.phatduckk.com/blog/php/ for some more info

racers auction on Jun 04, 2008 at 5:13 pm

I have a small problem I can’t find the answer to. Say I have a large includes file but I only need ONE function from the file for a certain page the needs to be incredibly fast. Racing fast, should I include that function on the page itself, include the file with that function or write another file with only that function and include it on the page??

listening to Nothington and jonzing to play some music soon.

Search This Blog

Categories