Everything about Web and Network Monitoring

Website Performance: PHP

The World-Wide Web offers more PHP performance tips than can be comfortably discussed in one article, so the following is merely a list that can be used for reference purposes. The tips are divided into categories to group similar things together and make it easier to find what we need. The author’s search for tips was extensive, but completeness still cannot be guaranteed.These tips fall into category #3.1 (the server executes a script) in the taxonomy of tipsthat we have been working with.Because of the sheer volume, these tips have not been tested. The reader is expected to test them in his own production environment before relying on them. This is not unreasonable, though, because the value of most tips depends on the unique environment within which PHP operates.


Table of Contents

Finding the Bottleneck

Caching

Compiling vs. Interpreting

Content Reduction

Multithreading & Multiprocessing

Strings

Regular Expressions

Iteration Constructs (for, while)

Selection Constructs (if, switch)

Functions & Parameters

Object-Oriented Constructs

Session Handling

Type Casting

Compression

Error Handling

Declarations, Definitions, & Scope

Memory Leaks

Don’t Reinvent the Wheel

Code Optimization

Using RAM Instead of DASD

Using Services (e.g., SQL)

Installation & Configuration

Other


 

Finding the Bottleneck

Considering these tips to be “best practices” and using them on an ongoing basis is a good idea. However, when faced with a performance problem, the first step should always be to find the cause of the problem rather than to run through a list of tips. Understanding the cause, identifying options, selecting an option, implementing a fix, and measuring the result all come later. Finding the bottleneck is step one. Here are some tips that will help us with that all-important first step:

  • Use monitor.us or Monitisto measure, benchmark, and monitor. Networks, especially the Internet, continuously change. What performs well now may be a bottleneck in 5 minutes.
  • Profile the code. Know which parts of the code take the most time and focus attention on those sections.
  • To find bottlenecks, investigate every resource (e.g., network, CPU, memory, shared memory, file system, process management, network connections, etc.).
  • Benchmark iteration structures and complex code early.
  • Use realistic tests with realistic data under realistic loads. Use the production server if possible.

 

Caching

Some authors identify caching as one of the most effective ways to improve performance. Here are some tips to help us out:

  • Use opcode caching so the scripts are not recompiled every time they are accessed. Example: Enable the Windows Cache Extension on Windows platforms. It caches opcode, files, relative paths, session data, and user data.
  • Consider distributed caching in a multi-server environment.
  • Call imap_headers() before calling imap_header().

 

Compiling vs. Interpreting

Compiling translates PHP source code into machine code. Interpreting performs the same translation, but line-by-line as the code executes. Translating into opcode is somewhere in-between; it translates source code into opcode, then translates the opcode into machine code afterward. Here are some tips regarding compiling and interpreting:

  • Compile PHP into machine code before it goes live. Caching opcode is not as good, but is still better than interpreting. Compiling PHP code into a C extension is one option.
  • PHP’s opcode compiler (bcompiler) is not ready for production environments yet, but developers should keep an eye on it at php.net/manual/en/book.bcompiler.php.

 

Content Reduction

Smaller is often faster. Here are some tips to help us reduce the size of our code:

  • Provide less functionality per page.
  • Unclutter web pages.
  • Strip comments and extraneous white space at build time if the code is being interpreted.
  • Make fewer database queries.

 

Multithreading & Multiprocessing

In order from fastest to slowest:

  1. multithreading (within a single process)
  2. multiprocessing (e.g., pcntl_fork, cron jobs)
  3. serial processing (the usual “one line followed by another” approach)
Mutlithreading is not available in PHP, but a mutli-threaded C program can be used as a PHP extension. There are several other ways to use or simulate multiprocessing, but none is as well supported and some may be slower than serial processing.

Strings

String processing seems to be one of the most frequently executed operations in most programming languages. Here are some tips to help us make our string operations even faster:

  • PHP’s concatenation operator (the dot operator) is the fastest form of concatenation in most cases.
  • Instead of concatenating strings in a print, separate them with commas in an echo.
  • Use str* functions instead of functions that use regular expressions. pos() is faster than preg_match(), which is faster than ereg(). str_replace() is faster than preg_replace(), which is faster than ereg_replace().
  • Some people say that surrounding strings with single quotes performs better than with double quotes. Others say it makes no difference. Of course, if you want to include variables inside the string, single quotes won’t work.
  • If you need to know whether a string is shorter than some value (say 5), use isset($s[4]) instead of strlen($s) < 5. Flip the conditions if you want to know if it’s longer.
  • To concatenate many small strings into one large string, try creating an output buffer with ob_start, then use echo to append into the buffer. When done, use ob_get_contents to retrieve the string.

Regular Expressions

Regular expressions give us all kinds of flexibility when comparing or searching strings, but their performance cost is usually non-trivial.

  • Use str* functions instead of regular expressions whenever possible.
  • Use [aeiou]rather than (a|e|i|o|u).
  • The simplest regular expression is usually the fastest.
  • Unset PCRE_DOTALL if appropriate.
  • Use ^.* instead of .*.
  • Simplify. Example: Use a* instead of (a+)*.

Iteration Constructs (for, while)

Iteration (repetition, looping) is a fundamental structured programming construct. It’s hard to imagine a non-trivial application that doesn’t use it. Here are some tips to help us improve the performance of our iteration constructs:

  • Move as much as possible outside the loop (function calls, SQL queries, etc.).
  • Use i=maxval; while (i–) instead of for (i=0; i<maxval; i++)to eliminate one operation. It’s even more important if maxval is a function call.
  • Use foreach for iterating through collections and arrays.

Selection Constructs (if, switch)

Like iteration, selection is also a fundamental structured programming construct. Here are some tips to help us improve its performance:

  • Switches and else-ifs should list the most-often-true conditions ahead of the less-often-true conditions.
  • Some say if-else is faster than switch/case. Others say the opposite.
  • Use elseif instead of else if.

Functions & Parameters

Chopping code up into functions can eliminate redundancy and make our code more readable, but at what cost? Here are some tips to help us optimize our use of functions:

  • Pass objects and arrays in and out by reference, not by value.
  • Inline functions if they are used only in one place. Consider inlining them if they are invoked in multiple places, but beware the maintainability issue.
  • Know the complexity of the functions you use. Example: similar_text()is O(N**3), which means that doubling the length of the string multiplies the time requirement by 8.
  • Do not use return-by-reference to increase performance. The engine automatically optimizes this on its own.
  • Call functions “the normal way” instead of with call_user_func_array() or eval().

Object-Oriented Constructs

PHP’s object-oriented features can impact performance. Here are some tips to help us minimize that impact:

  • Not everything has to be object-oriented. The reduction in performance may outweigh the other benefits.
  • Creating objects is slow.
  • When possible, use arrays instead of classes.
  • If a method can be static, declare it static.
  • Invoking functions is faster than invoking methods of a derived class. Invoking methods of a derived class is faster than invoking methods of a base class.
  • Consider copying the base class’s most frequently used code into the derived classes, but beware the maintainability issue.
  • Avoid naive getters and setters. If they do nothing more than get/set the attribute, delete them and make the attribute public.
  • Consider using the singleton method when creating complex PHP classes.

Session Handling

Creating sessions has many benefits, but sometimes the performance cost is not worth it. Here are some tips to help us minimize the performance cost:

  • Don’t use auto_start.
  • Do not enable use_trans_sid.
  • Set cache_limited to private_no_expire.
  • Assign each user (vhost) its own directory.
  • Use a memory-based session handler rather than a file-based session handler.

Type Casting

Converting data from one type to another has a performance cost.

  • Cast explicitly rather than implicitly.

Compression

These tips let us compress text and data prior to transmission:

  • Use ob_start()at the beginning of the code.
  • Use ob_gzhandler()for faster downloads, but beware the CPU cost.
  • Apache’s mod_gzip module compresses your data on the fly.

Error Handling

Error handling impacts performance. Here’s what we can do about it:

  • Write error-free code instead of suppressing errors with @. Suppressed errors affect performance, too.
  • Check the error log for warnings, not just errors.

Declarations, Definitions, & Scope

Creating a variable, array, or object can also negatively affect performance.

  • Some say that declaring and manipulating global objects/variables is faster than using a local scope, but others disagree. Test this before deciding.
  • Declare all variables before using them.
  • Don’t declare variables you don’t use.
  • Use $a[] = … inside a loop instead of $a = array(…).

MemoryLeaks

If memory is allocated and never de-allocated, we have a problem. Here are some tips that deal with that problem:

  • Always free resources instead of expecting some automated process (e.g., the garbage collector) to do it for you.
  • Unset variables when you’re done with them, especially for resources and large arrays.
  • Close database connections when you’re done with them.
  • Use ob_end_flush() or ob_end_clean() once for every ob_start().

Don’t Reinvent the Wheel

Why spend time solving a problem that someone else has already solved?

  • Know PHP, its functions, and its extensions well. If you don’t know what’s available, you can’t make use of it.
  • Use the built-in array and string functions. They are well-tuned for the best performance.
  • Caveat Emptor: Just because someone invented the wheel before you doesn’t mean their invention performs well in your environment. Don’t forget to test for performance.

Code Optimization

Is generated opcode really as fast as it can be?

  • Use an opcode optimizer.
  • Minify source code at build time if it will be interpreted.

Using RAM Instead of DASD

RAM is much faster than disks and similar storage devices. Here are some tips that help us use RAM:

  • Move files to a ramdisk.
  • Use a memory-based session handler instead of a file-based session handler.

Using Services (e.g., SQL)

SQL is frequently used to access relational databases, but our PHP code may access many different kinds of services. Here are some things to keep in mind when accessing services:

  • Don’t ask a server for the same thing over and over. Use memoization to cache the result the first time, then use the cache for future accesses.
  • In SQL, use mysql_fetch_assoc($result) instead of mysql_fetch_array($result)to eliminate the integer indexing on the result set. Access the result set by column name instead of by index number.
  • For Oracle databases, if there is enough available memory, increase oci8.default_prefetch. Set oci8.statement_cache_sizeto the number of working statements the application uses.
  • Use mysqli_fetch_array() instead of mysqli_fetch_all() unless the result set will be sent to another layer for processing.

Installation & Configuration

PHP must be installed and configured with performance in mind:

  • Add more RAM.
  • Remove competing applications and services from the server machine.
  • Compile with only the extensions you need.
  • Compile PHP statically into Apache.
  • Use -O3in CFLAGS to enable all compiler optimizations.
  • Install only the modules you intend to use.
  • Upgrade to the latest version for minor releases. For major releases, wait for the first few bug fixes, but don’t wait too long.
  • Configure for multiple CPU’s.
  • Use -enable-inline-optimization.
  • Set session.save_handler=mm and compile with -with-mmto use shared memory.
  • Use a RAM disk.
  • Strip your binaries.
  • Disable register_globals and magic_quotes_*.
  • Turn off expose_php.
  • Disable always_populate_raw_post_dataunless you need it.
  • Turn off register_argc_argvfor non-cli SAPIs.
  • Use PHP only for files ending in .php rather than all static files.
  • Optimize max_execution_time, max_input_time, memory_limit, and output_buffering.
  • Set Apache allowoverride to noneto improve file/directory access.
  • Unless absolutely necessary do not enable always_populate_raw_post_data.
  • Use -march, -mcpu, -msse, -mmmx, and -mfpmath=sseto make the best use of the CPU.
  • Use the MySQL Native Driver (mysqlnd) instead of the MySQL Client Library (libmysql) for the mysql extension, the mysqliextension, and the PDO MySQL driver.
  • Adjust realpath_cache_size and realpath_cache_ttlto their optimal values.
  • Disable register_globals, register_long_arrays, and register_argc_argv. Enable auto_globals_jit.

Other

And then there are those tips that are hard to classify:

  • Instead of using include() or require(), embed the files at build time.
  • Avoid include_once() and require_once(). Use include or requireinstead. Better yet, see the preceding tip.
  • Use absolute paths rather than relative paths with include() and require().
  • Static HTML is faster than PHP-generated HTML.
  • Use ctype_alnum, ctype_alpha, and ctype_digitinstead of regular expressions.
  • Use simple servlets or CGI.
  • When your code gets to production, log as little as possible.
  • Use output buffering.
  • Use isset($a) rather than comparing $a to NULL. Use $var===null instead of is_null($var).
  • Use $_SERVER[’REQUEST_TIME’] rather than time()to find out when the script started executing.
  • Use echo rather than print.
  • Preincrement rather than postincrement. Most compilers will optimize this anyhow, but stay in practice for those occasions when they don’t.
  • For processing XML, use regular expressions instead of DOM or SAX.
  • Mount Unix filesystems with atimedisabled.
  • Hashing algorithms: md4, md5, crc32, crc32b, and sha1 are probably faster than other choices.
  • List file extensions in most-commonly-used to least-commonly-used order in spl_autoload_extensions(). Exclude file extensions you do not use.
  • Use IP addresses instead of domain names when calling fsockopen or fopen. If you have only a domain name, use gethostbyname() to get the IP address. Using cURL may be even faster.
  • Whenever possible, serve static content instead of dynamic content.

References

Listed here are a few of the web pages that were consuted in preparing the above list. Not all pages are listed here – some effort has been made to weed out the trivial and questionable.code.google.com/speed/articles/optimizing-php.htmldevelopers.facebook.com/blog/post/358php.netphp100.wordpress.com/2009/06/26/php-performance-googlephp100.wordpress.com/2009/07/13/php-performancephplens.com/lens/php-book/optimizing-debugging-php.phpchazzuka.com/63-best-practice-to-optimize-php-code-performances-58devshed.com/c/a/PHP/Optimizing-System-Performance/3dublish.com/articles/10.html

ibm.com/developerworks/linux/library/l-tune-lamp-2

scribd.com/doc/10633/Performance-Tuning-PHP

sitepoint.com/high-performance-string-concatenation-in-php

websiteoptimization.com/speed/tweak/time-to-first-byte

About Warren Gaebel

Warren wrote his first computer program in 1970 (yes, it was Fortran).  He earned his Bachelor of Arts degree from the University of Waterloo and his Bachelor of Computer Science degree at the University of Windsor.  After a few years at IBM, he worked on a Master of Mathematics (Computer Science) degree at the University of Waterloo.  He decided to stay home to take care of his newborn son rather than complete that degree.  That decision cost him his career, but he would gladly make the same decision again. Warren is now retired, but he finds it hard to do nothing, so he writes web performance articles for the Monitor.Us blog.  Life is good!