본문 바로가기

Programming/PHP

PHP Performance (튜닝의 지름길)

 

18.     Performance

Performance, particularly on busy sites, can be critical - after all, if you can speed up your code by 10%, that decreases your hardware load by 10%, saving you the need to upgrade. There are a number of ways you can improve the performance of your scripts, and we will be covering as many as have space for. We will also be dispelling various myths about optimisation, and hopefully by the end of this chapter you will be confidently about to re-write fundamentally flawed algorithms, tune implementations of good algorithms, make your MySQL queries fly, and more.

Before we begin, I would like to make it quite clear that optimisation is the process of improving performance of your code, whether to use less space or run faster - it is usually a trade-off. Optimised code is not necessarily "perfect code", it is just better than unoptimised code.
 
Furthermore, there is rarely if ever such a thing as "too fast". In my spare time, I have been working on my own pet project: a gigantic online PHP strategy game. Actions occur at round end, which is triggered by a cron job every ten minutes. With a thousand dummy users in the system, round end takes over seven seconds, during which database writes are locked so that people cannot make changes. In the past I have spent hours and hours just to cut one part of that round end from 0.002 seconds to 0.001 seconds per player - it might not sound like a lot, but as far as I am concerned every single second counts. If you have tried out half of the recommendations here and find you have reduced the run-time for a script from four seconds down to one second, don't stop there - go for the fastest code you can get.
Topics covered in this chapter are:
  • Increasing performance by optimising your scripts
  • Increasing performance by optimising your SQL
  • Increasing performance by optimising your server
  • Caching PHP scripts
  • PHP the CGI vs. PHP the Apache module
 

18.1     Optimising your code

Optimising your code is about making your script work using fewer resources - this might be less CPU time, less RAM, less hard drive space, or preferably all three at once! When you successfully halve the time it takes to execute a script, you have two options: sit back and enjoy the fruits of your labour, or, perhaps better, make the script do twice as many things .
 
Either way, it is important to keep in mind that unless your script does some very heavy SQL queries, it is unlikely there will be much noticeable benefit for your end user even after you optimise your script. The reason for this is that most PHP scripts take very little time to execute on today's modern CPUs, which means it is likely each of your scripts take well under a tenth of a second to execute. Compare that to the Internet latency between two hosts, which is between 150-250ms, and the total data transfer time, which is probably about half a second, and you have a total time between request and return of about eight-tenths of a second. If you work hard on your script and increase its speed by a factor of 10, making it execute in just one-hundredth of a second, you will decrease the request-to-return time from eight-tenths of a second to 7.1-tenths of a second. That is, despite your hard work, there is only a 10% noticeable speed difference to the end user, and only then if they have very good time sense.
 
Is that to say that optimisation is pointless? Not at all - optimisation is always important, as it allows you to lower the resources being used on your server. This in turn allows your server to do more - if your server was running at full capacity, decreasing its load from 100% to 10% is a major achievement, despite the lack of noticeable different from the end user view.
 
Code optimisation is often a subjective topic. The items listed below are optimisations that I use personally and fully believe they work, however it is possible that you will find other people saying they tried some of these and found them not to work, or, worse, to make their pages run slower. Harrison's postulate covers this situation rather well: "For every action, there is an equal and opposite criticism". So, try these out, see what works for you, and put together your own list of optimisations. Also keep in mind that these optimisations were tested on PHP 5.0.2 - their applicability may be different on other versions.
 
Finally, a warning: only implement one optimisation at a time. If you try three at once, you may find your code ends up working slower and you will not be able to tell which "optimisation" is hindering your performance. Instead, implement one optimisation, test it out, make sure it is working, then implement another, and another, step-by-step
 

18.1.1     Write your code sensibly

"The fastest code is the code that is never executed."
If you are working on something you originally thought would be easy but has ended up being many more lines of code than you originally intended, it is quite possible that your code is getting a little bloated, and more than a little slow. Hoare's law tells us "inside every large program is a small program struggling to get out", so you should consider chopping out blocks of code that are outdated, outmoded, replaced, or irrelevant.
 
I've quoted from it already, but Eric Raymond's "The Art of Unix Programming" is just so damn right about this sort of thing that I can't help but quote from it again. Raymond says that, "the most powerful optimisation tool in existence may be the delete key", and even quotes Ken Thompson (one of the most highly respected Unix hackers in the world) as saying, "One of my most productive days was throwing away 1000 lines of code."
 
This is very much down to personal intuition, and is often very hard to do at first. However, try this kind of thing out to give you an idea of script performance:
 
    print "Start: ", microtime(true);
   
// ...[snip]...
   
print "End: ", microtime(true
);
This allows you to time the execution of your script, or at least certain parts of your script. If you see something running particularly slowly, it may be because your implementation is bad, or because your actual algorithm is faulty.
When dealing with performance benchmarking, however, you need to be wary - it has been said that "premature optimisation is the root of all evil". That is, there are many optimisations that can be implemented to make your code run faster/smoother, however most of them also make the code harder to read and/or edit.
 
Find a happy medium - test your scripts now and again, and note where things are running poorly. However, hold your optimisation off as long as possible.

18.1.2     Use your tools wisely

At various times through my PHP career, I have seen pages with sections of code like this:
 
$result = mysql_query("SELECT * FROM Users;");
while (
$row = mysql_fetch_assoc($result
)) {
    if (
$row['ID'] == '3'
) {
       
// do stuff here
   
}
}
 
As you can see, they are using PHP to filter their SQL results - looking through the entire result set for a particular row.
SQL is designed to extract specific data from a database, and you should exploit its power as much as possible. MySQL is blazingly fast at processing queries, and modifying the above code to the following would yield an exponential speed increase:
 
$result = mysql_query("SELECT * FROM Users WHERE ID = 3;");
while (
$row = mysql_fetch_assoc($result
)) {
   
// do stuff here
}
 
For very complicated SQL manipulation, you should use temporary tables - more information can be found on temporary tables in the Databases chapter.
 
A similar problem exists when people use functions that are more complex than is required for the task. For example, preg_replace() is a powerful way to search and replace text in a string, but if you are not doing regular expressions you should be using str_replace() as it's much faster. Similarly, explode() is a better choice than preg_split() when regular expressions are not required.

18.1.3     Avoid function for maximum performance

Calling a function in PHP is very expensive, so avoid it whenever you can. There are two methods for accomplishing this - one for user functions, and one for built-ins.
If you are calling a user function that is quite short, consider inlining it. This is a technique employed in C and C++ that essentially copies the contents of a function to wherever it was called, thus avoiding the function call. Consider the following code example:
 
    function some_func() {
       
$j = 1
;
    }

   
$START = time
();
    for (
$i = 0; $i < 4000000; ++$i
) {
       
some_func
();
    }
   
$END = time() - $START
;
    echo
"Calling the function took $END seconds\n"
;

   
$START = time
();
    for (
$i = 0; $i < 4000000; ++$i
) {
       
$j = 1
;
    }
   
$END = time() - $START
;
    echo
"Inlining took $END seconds\n"
;
As you can see, the function call is quite simple - only one line, with no parameters being passed in and no return value being use. On my test machine, the first loop took 15 seconds and the second loop to just 5 seconds. Having longer functions decreases the impact of function calls, but adding parameters and return values will increase it. For example, editing the above script so that some_func() takes just two parameters (although the function did not use them) increased the function loop execution time to 30 seconds - a huge difference from the 5 seconds with inlining.
 
For built-in functions, your only choice is to try to live without the function call. It is never better to try to rewrite a built-in function using PHP so that you can inline it, as the built-in function uses highly optimised C code that is likely to be as fast as it can get. However, there are some functions you can live without quite easily, such as floor(). This function rounds a floating-point number down to the nearest integer, which is also what typecasting a float to an integer does. In this situation, typecasting a floating-point number to an integer as opposed to using floor() is about a third faster, so it's best to avoid floor() in tight loops.
 
Author's Note: Potentially above and beyond all the other performance tweaks presented here, this one is definitely one best left until the very end - if you really need that extra bit of performance. Functions are there to centralise code and are incredibly helpful, and inlining them before you really need to definitely counts as premature optimisation.
 
Eric Raymond, in his marvellous book "The Art of Unix Programming" (available online at this link but really worth buying), has a wonderful quote from Steve Johnson, an early Unix hacker responsible (amongst other things) for the Portable C Compiler and Yacc. The quote is this: "Dennis Ritchie encouraged modularity by telling all and sundry that function calls were really, really cheap in C. Everybody started writing small functions and modularizing. Years later we found out that function calls were still expensive on the PDP-11, and VAX code was often spending 50% of its time in the CALLS instruction. Dennis had lied to us! But it was too late; we were all hooked..."
Expensive, yes, and expensive still, but there's a reason using functions is so addictive!
 

18.1.4     Use the Zend Optimizer

The Zend Optimizer is a free product that automagically helps your PHP code go faster.
It does so by performing actual code optimisation for you - changing things around (but leaving the "meaning" of the code the same) to increase execution speed. Even though it runs every time your page is executed, it has little to no noticeable overhead, and can drastically improve performance. Note that there are some cases where using the Optimizer will actually slow things down - usually when scripts are short, and/or exit early. These situations are rare, though, and you need not let this stop you from installing the Optimizer.
 
Zend Performance Suite comes with Zend Optimizer bundled as it caches the optimized PHP, eliminating the extra overhead of code optimisation.
If you don't plan to use scripts encoded with Zend Safeguard Suite, add the following line to your php.ini file to get a further small boost in performance:
 
   zend_optimizer.enable_loader = 0

 

18.1.5     Use a PHP code cache

There is one simple way you can double the speed of your server, and that is to install IonCube's PHP Accelerator - it is fast, free, and works perfectly on Linux. If you don't mind paying money and want another 20% extra speed boost, Zend's Performance Suite is the way to go - it is better supported than PHPA, works with newer versions better, and has the edge on speed to boot. If you have a small/non-existent budget, PHPA is your best choice. If you have a little money and really do want to push your PHP code to the max, Zend's Performance Suite will be a purchase you will not regret.
 
Recently the Alternative PHP Cache (APC), the cache that performed most poorly in our tests, has been transferred into PEAR, the PHP code repository - it is now being worked on by PHP developers. As such, it is now much better than it previously was, almost snatching the #1 spot. Well worth a try!
 

18.1.6     Read the manual carefully

Far too many people write code like this:
print "Foo" . somefunc() . " bar " . anotherfunc();
At first glance, that code might make sense. However, the . operator (string concatenation) has to be run three times before print is finally called. So, PHP would execute something like this:
  • Create a new, temporary string
  • Put "Foo" in there
  • Put the result of the somefunc() function in there
  • Create a new, temporary string
  • Put the first temporary string in there
  • Put " bar " in there
  • Create a new, temporary string
  • Put the second temporary string in there
  • Put the result of the anotherfunc() function in there
  • Send the final temporary string to print, for screen printing
Complicated? A little. Stupid? Certainly. The echo function can take more than one parameter simply by separating them with a comma while still having the same basic functionality. The extra parameters are not joined together in a new string like they are with the concatenation operator, they are just output directly.
So, your code would become:
 
     echo "Foo", dosomething(), " bar ", dosomethingelse();
 
Using a code cache does help eliminate or at least lessen these coding errors, but that does not mean you should be lazy!

18.1.7     Get your loops right first

A good rule of thumb to keep in mind is "90% of your scripts execution time is taken up in 10% of the code". Granted that is a vast over-generalisation and it is probably not very true, either, however the principle is correct: you can spend hours and hours optimising your program randomly and find it runs no faster because you have been optimising bits that are not run very often. Granted, from a purely programmatic point of view it is never a bad thing to optimise even rarely used code, however few people and even fewer companies have the time to spend optimising everything, so you should generally try to focus your efforts on what will give the greatest return.
 
One of the best places to look for optimisation opportunities is in loops, as they clearly embody code that needs to be executed multiple times. There are a number of ways you can speed up loop execution, of which two are the most common: calling a function as part of the loop condition, and loop-invariant code.
Consider this first example:
 
for ($i = 1; $i < count($myarr); ++$i) {
   
//
}
 
Here we have some code that does an action once for every element in an array - quite a common action, as you well know. However, due to the way the count() function is implemented, it will be executed in each loop iteration. That is, if we set $myarr to an array holding 100 items, PHP will count it 100 times - once for each time the loop goes around.
 
Sometimes this is the desired behaviour. For example, if you are changing the number of elements in the array inside the loop, you will of course need to recalculate its size with each iteration. However, more often that not the size is fixed, so why bother making PHP fetch its size every time?
 
Internally PHP actually caches the number of elements in the array, so technically calling count() does not make PHP count each individual element. However, it does still need to jump through extra hoops thanks to the function call, which is why there is such a noticeable speed difference here.


A much better solution is to calculate the array size just once, outside of the loop, like this:
 
$len = count($myarr);

for (
$i = 1; $i < $len; ++$i
) {
   
//
}
 
Naturally the benefits of changing to this faster method depend largely on how large the array is. No matter what, it is better to calculate the array size outside of the loop as it will always be about twice as fast, however twice as fast as "very fast" is not that noticeable! Try this script out to give yourself an idea:
 
    for ($i = 1; $i < 3000000; ++$i) {
       
$myarr[] = rand
();
    }

    echo
"Done setting up array.\n"
;

   
$START = time
();
    for (
$i = 1; $i < count($myarr); ++$i
) {
       
//
   
}
   
$END = time() - $START
;
    echo
"Using count() took $END seconds\n"
;

   
$START = time
();
   
$len = count($myarr
);
    for (
$i = 1; $i < $len; ++$i
) {
       
//
   
}
   
$END = time() - $START
;
    echo
"Not using count() took $END seconds\n"
;
On my test computer using count() in the loop took eight seconds and not using count in the loop took four seconds - quite a substantial difference, but it was on an array of 3,000,000 items!
The second "must avoid" loop problem is loop-invariant code, which is the practice of calculating things inside a loop that could be done, all or in part, outside the loop. For example, consider the following code:
 
    $n = 100;

    for (
$i = 1; $i < 100; ++$i
) {
        for (
$j = 1; $j < 100; ++$j
) {
           
$somearray[$i][$j] = ($n * 100) + ($i * $n) + ($j * $n
);
        }
    }
Inside our inner loop ($j), you will see values are put into a two-dimensional array using three added calculations: $n * 100, $i * $n, and $j * $n. Of these three, $n * 100 will always be the same value because $n does not change and 100 is always 100. However, that calculation will be done 10,000 times, each time coming up with the same answer, which makes it loop-invariant. Similarly, $i * $n does change, but not inside the inner loop, so this is loop-invariant for the inner loop only. The final calculation, $j * $n, does change, and so is not loop-invariant.
The loops should be re-written like this:
 
    $n = 100;
   
$n_mult_100 = $n * 100
;

    for (
$i = 0; $i < 100; ++$i
) {
       
$i_mult_n = $i * $n
;

        for (
$j = 0; $j < 100; ++$j
) {
           
$somearray[$i][$j] = $n_mult_100 + $i_mult_n + ($j * $n
);
        }
    }
This new code has removed the loop-invariance, thus making the script run faster. Loop-invariant code is usually easy to spot, and, unlike in the example above, usually yields hefty performance increases - particularly if a complex function is being called.
 

18.1.8     Pre-increment where possible

Take advantage of the fact that PHP allows you to post-increment ($i++) and pre-increment (++$i). The meaning is the same as long as you are not writing anything like $j = $i++, however pre-incrementing is almost 10% faster, which means that you should switch from post- to pre-incrementing when you have the opportunity, especially in tight loops. Note that both the Zend Optimizer and IonCube PHP Accelerator will implement this optimisation for you.
 

18.1.9     Don't think that using references will lower your RAM usage

Objects are always copied by reference in PHP 5, which means that copying even the most complex object is instantaneous and essentially free in terms of memory usage. Furthermore, PHP makes extensive use of "copy-on-write", which means taking ten copies of the same array is also free, because they all point to the same object. It's called "copy-on-write" because the minute one of the arrays change from the others, a full copy is made so the change is kept separate.
 

18.1.10     Be wary of garbage collection, part 1

PHP performs garbage collection at three primary junctures:
  1. When you tell it to
  2. When you leave a function
  3. When the script ends
Situation 1 occurs when you use unset(), mysql_free_result(), or other resource-destroying functions that explicitly clear up after your variables. Situation 2 clears up resources implicitly - any variable that leaves scope, i.e. is no longer applicable, gets cleared up for you. Finally, situation 3 frees up all script-related resources implicitly.
 
It is important to understand that situations 2 and 3 both use the same code path as situation 1 - that is, if you free up a resource using unset() or another resource-destroying function, it will take as much time to do as if PHP had to destroy the resource at the end of the script. This might sound like a rather mundane and, quite frankly, obvious thing to say, however it has profound implications because it means there is little reason to rely on PHP for garbage collection unless you are lazy!
 
Now, of course that is not quite true - unless you are really strapped for resources, there is little point calling unset() on each of your variables when you are done with them, and similarly there is little point in explicitly cleaning up after a resource just before the script is about to end, as it is going to happen anyway without the need to clog up your script. However, if there is any sizeable delay between you finishing using a big resource, and the juncture at which automatic garbage collection will take place (usually script end, but might also be function end), then you should clean up after yourself and claim the resource back.
 

18.1.11     Be wary of garbage collection, part 2

Getting a full understanding of how the garbage collection works in PHP, and also how copy-on-write works, is no easy task. However, if you ever plan to create interactive applications with PHP, such an understanding is essential because without it your program risks leaking memory.
 
I put together a simple test script that demonstrates how memory usage works in PHP. To run it you need to compile PHP by hand, with the configure switch "--enable-memory-limit". You'll also need to boost the memory limit up from 8MB in your php.ini. Set it to something high like 800MB temporarily, just to make sure. To get the memory usage, just call the memory_get_usage() function with no parameters - it returns the amount of memory being used by the current PHP process, in bytes. Here's the script:
 
echo "Stage 1: Mem usage is: ", memory_get_usage(), "\n";

  $arr = array();

  for ($i = 0; $i < 1000000; ++$i) {
    $arr[] = rand();
  }

  echo "Stage 2: Mem usage is: ", memory_get_usage(), "\n";

  $foo = 1;
  $bar = 2;

  echo "Stage 3: Mem usage is: ", memory_get_usage(), "\n";

  $foo = $arr;
  $bar = $arr;

  echo "Stage 4: Mem usage is: ", memory_get_usage(), "\n";

$arr = array();

  echo "Stage 5: Mem usage is: ", memory_get_usage(), "\n";

  $bar[] = "hello, world";

  echo "Stage 6: Mem usage is: ", memory_get_usage(), "\n";

  $foo = array();

  echo "Stage 7: Mem usage is: ", memory_get_usage(), "\n";
 
For those of you without the will or the way to do that, I ran the script for you. Here's what I got:
 
Stage 1: Mem usage is: 37712
Stage 2: Mem usage is: 60232136
Stage 3: Mem usage is: 60232248
Stage 4: Mem usage is: 60232248
Stage 5: Mem usage is: 60232288
Stage 6: Mem usage is: 104426704
Stage 7: Mem usage is: 60242672
 
OK, so what does that tell us? Before the script has done anything, PHP is already using 37KB of RAM. This is where the parsed script and other basic components live - there's nothing we can do about that. In Stage 2, we have allocated 1,000,000 numbers into the array $arr, using up 57.5MB of RAM. Yes, PHP is reporting 60232136 bytes, but there are 1024 bytes in a kilobyte, and 1024 kilobytes in a megabyte, hence 57.5MB. By stage 3 we've also got the $foo and $bar variables set to integers, so there's a nominal increase in memory usage. So far, so good.
 
Now, the interesting part is stage 4: $arr is "copied" into $foo and $bar, giving us three instances of the same array. However, look at the memory usage - it's exactly the same as in stage 3. Why is this? Copy-on-write, of course! That is, both $foo, $bar, and $arr are all pointing to the same internal array.
 
This is illustrated in the next two stages. In stage 5 $arr is set to be an empty array, and yet the memory usage barely moves. It goes up a little because a new array structure is allocated empty for $arr, but it's basically negligible. In stage 6 we've added an array element to the $bar array, so PHP performs the copy-on-write operation - $bar takes a full copy of the array it was previously pointing to, then adds the new element. At this point, $foo and $bar are point to two different arrays, and $arr is pointing to an empty array.
 
In stage 7, $foo is also set to be an empty array, and suddenly there's a huge drop in the amount of memory used as $foo's array gets cleaned up. Note, however, that even though the $bar array is no longer referenced in the rest of the script, it is not garbage collected: PHP holds it in memory all the way until the script finishes.
So, what lessons can we learn from that?
  • If you want a global scope variable to release its memory, use the unset() function or set it to a different value. Otherwise, PHP will keep it floating around just in case.
  • Copy-on-write is your friend, and means that for all intents and purposes arrays are copied by reference in the same way that objects are. The only difference is that if you change the array subsequently, a deep copy is performed.
  • The minute you unset() or re-assign a variable, PHP frees its memory. Freeing memory - particularly large amounts - isn't free in terms of processor time, which means that if you want your script to execute as fast as possible at the expense of RAM, you should avoid garbage collection on large variables while it's running, then let PHP do it en masse at the end of the script.
Author's Note: The garbage collection mechanism is due to change in PHP 5.1, which has the potential to change this performance tweak quite drastically. If you intend on using PHP 5.1, please do grab it from the PHP site and test this thoroughly before committing to a plan of action.
 

18.1.12     Listen to all errors, big and small

Always set your PHP error level to the most verbose level, E_ALL. All too often people don't realise that PHP is outputting various complaints about variables not being set, etc, which you can just do away with entirely by cleaning up your code. While you are editing your php.ini file, it would also help to disable all the extensions you don't use - they are just chewing up memory otherwise.
 

18.1.13     Keep up to date

If you are not using it already, consider upgrading to the latest version of PHP. Fixes and tweaks to slow code are being made to all the time, so it is worth taking advantage of all the hard work going on. Note that as the fix to many PHP bugs is "upgrade to the latest version", this is generally always the best option - if you are running anything older than 5.0, upgrade; if you are on anything before 4.3, upgrade right now; if you are on anything before 4.1, what is taking you so long? Use the phpinfo() function to get your version number and information about activated modules.
 
Do keep one thing in mind, however: the early bird may get the worm, but the second mouse gets the cheese. That is, don't feel obliged to update to the latest version of PHP just because it is new - let others try it out and find any obvious errors before you upgrade.
 

18.1.14     Cache array data

Do not ever repeatedly access the same element in an array unless you absolutely have to. Try running the following code on your own machine:
 
    $START = time();
   
$foo['bar'] = "test"
;

    for (
$i = 0; $i < 10000000; ++$i
) {
        if (
$foo['bar'] == "test"
) {
           
$j = 0
;
        }
    }
   
$END = time() - $START
;
    print
"Array took $END seconds\n"
;

   
$START = time
();
   
$testvar = $foo['bar'
];

    for (
$i = 0; $i < 10000000; ++$i
) {
        if (
$testvar == "test"
) {
           
$j = 0
;
        }
    }
   
$END = time() - $START
;
    print
"Var took $END seconds\n"
;
Running that took 38 seconds for the repeated array access, and 32 seconds for using a variable - the reason for this is that accessing array elements time and time again requires PHP to find the element inside the array, which is comparatively slow.
 

18.1.15     Compress your output

HTML is a very wordy format, which means there is a lot of duplication in the form of HTML tags, but also in the main body of text. Furthermore, by default PHP will send text to Apache as soon as it is ready, which results in less efficient transfer of data.
 
The solution is to enable output buffering, and to use gzip compression for the buffers. Output buffering, if you were unaware, makes PHP store up its data into one big chunk, then send it to Apache all at once. Because all the data is kept together and sent all at once, PHP is able to compress it using gzip compression, which will generally reduce the content to around 33% of its original size (that is, 1MB will become 300KB).
 
Not all clients support receiving compressed content (every browser made in the last five years will), and to handle that PHP will only compress data if the client can support it - this means you can enable compression, and not have to worry about old clients because PHP will not send them compressed data.
 
To enable output buffering for all your scripts, open up your php.ini file and set output_buffering to 1 and output_handler to "ob_gzhandler" (without the quotes). You'll find those values already set in your php.ini already, so just change the existing values. You should check your phpinfo() output to make sure output buffering is enabled correctly.
 

18.1.16     Don't use CGI

You have two options when installing PHP: use it as a CGI executable, or use it as an Apache/ISAPI module. Although there are advantages in favour of both options, running PHP as a module is overwhelmingly favourable when it comes to performance, as all of PHP and its extension modules reside in memory as opposed to being loaded with every request.
From a purely performance-motivated point of view you would be crazy to run PHP as a CGI executable.
 

18.1.17     Don't use dl()

The dl() function allows you to dynamically load a particular PHP extension for use in a single script then unload it when that script is done. While this is certainly very useful, consider what happens if you have that script being executed once every couple of seconds - PHP would need to continuously load the extension, execute the code, then unload it again, only to repeat the process a few seconds later.
 
In comparison, extensions that are enabled in the php.ini file are loaded once, when your web server is started, and unloaded only when the web server is stopped. This means that any scripts that take advantage of these modules execute a lot (lot) faster.
 
To give you an idea of the difference using dl() can make, I wrote a simple test script - in one, I used dl() to dynamically load the MySQL extension, connect to a database, then disconnect again. In the other, the MySQL extension was enabled in the php.ini file, there was resident in memory all the time, and it was also used to connect to a database then disconnect again.
Here is the two scripts:
 
    dl('php_mysql.dll');

   
mysql_connect("localhost", "phpuser", "alm65z"
);
   
mysql_close
();
 
    mysql_connect("localhost", "phpuser", "alm65z");
   
mysql_close
();
Simple enough, right? Well, I ran each script through the Apache benchmarking tool, which called each of them 10,000 times individually and returned the amount of time each took to execute - the former took 26.88 seconds to complete, and the latter took 4.98 seconds. From this test, dl() slowed the script down by a factor of five, which should make it crystal clear that you should enable your extensions in your php.ini file, and not by using dl() unless you really cannot help it.
 

18.1.18     Debug your code

One problem with PHP is that, by default, if it encounters non-fatal errors messages it will just output them along with the rest of its output, which means that very often you don't notice the errors. While this might not seem like such a bad thing - after all the errors are non-fatal, right?
 
In the world of programming, one rule is fairly constant: code will run quickly until it has to handle errors. That is, errors in your code are likely to chew up five, ten, or even twenty times the resources that they should - you will have noticed that Apache/PHP tends to shoot to the top of "top" and/or thrash your hard drive whenever it encounters a series of script problems.
 
You should thoroughly check the output your pages produce in order to make sure PHP is not emitting errors behind your back. Alternatively, make sure error logging is turned on in your php.ini file, and check it regularly.
 

18.1.19     Cache your pages

Although it is not always possible to cache the output of your PHP scripts, sometimes it becomes a necessity - particularly if your servers are being pushed to the limit despite you deploying a PHP accelerator long ago. In this case, you may well find that you can generate a page one every five minutes or so, then serve the static content up to visitors in-between regenerations.
 

18.1.20     Use persistent connections

If you connect to a database with each script, consider using a persistent connection rather than a normal connection. For MySQL users, that is the difference between using mysql_pconnect() rather than mysql_connect(). Persistent connections remain connected even after your script has ended, which means that the next time a script asks for a connection, it uses the one that is already open - this saves a lot time negotiating passwords and such that can otherwise be used to execute important code.
 
Switching to persistent connections does not require any other change than adding a "p" in the function name - the parameters are still the same. If your database server is not on the same machine as your web server, consider using CLIENT_COMPRESS as the fifth parameter to your mysql_connect() /mysql_pconnect() call - it allows MySQL to compress data to save space, can drastically lower network bandwidth and transfer speed, particularly when reading in lots of data.
 

18.1.21     Take advantage of new features

Lots of functionality gets added to PHP all the time, and yet few people seem to be taking advantage of it. How would you, for example, read the contents of a file into a string? Like this?
 
$contents = implode("\n", file("test.txt"))
 
If so, you are using code that was considered very fast before PHP 4.3 was released. However, in PHP 4.3 a new function was introduced called file_get_contents(), that takes a filename as its first parameter and returns as a string the entire contents of the file. It is very fast as it is very optimised, and neatly solves a problem that people have had to implement for themselves.
 
If you have not done so recently, take a good look through the PHP manual to see what is changed - lots of functions were added in 4.3, and many more in 5.0 so make sure you give your scripts a freshen up to take advantage of the latest features.
 

18.1.22     Compile right

One of the biggest advantages to using a Unix box is that you get to compile your software yourself, and it really does make a big difference to the speed of your software. If you are able to, I suggest you compile Apache, PHP, and MySQL yourself using GCC 3.4.x, and as many optimisations turned on as you have time to wait for.
Particularly important here is the PHP compilation, as you are not likely to get much improvement in your MySQL compilation over the stock binaries you can grab direct f
[인상적인 구절]
  "The fastest code is the code that is never executed."
  "The most powerful optimisation tool in existence may be the delete key."
  "One of my most productive days was throwing away 1000 lines of code."
 

'Programming > PHP' 카테고리의 다른 글

한자를 유니코드로 변환  (0) 2008.03.05
폴더안의 파일 가지고 놀기  (0) 2007.05.30
[PHP] 파일 읽고 쓰기 fOpen fWrite  (0) 2007.05.17
[PHP] 간단한 스위치(switch) 구문  (0) 2007.05.17
[PHP] 문자 장난  (0) 2007.05.17