Table of Contents
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 Monitis to 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.
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.
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:
- multithreading (within a single process)
- multiprocessing (e.g., pcntl_fork, cron jobs)
- serial processing (the usual “one line followed by another” approach)
- 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) 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.
- 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)
- 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)
- 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
- 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().
- 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.
- 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.
- Cast explicitly rather than implicitly.
- 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.
- 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
- 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(…).
- 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
- 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.
- Use an opcode optimizer.
- Minify source code at build time if it will be interpreted.
Using RAM Instead of DASD
- Move files to a ramdisk.
- Use a memory-based session handler instead of a file-based session handler.
Using Services (e.g., SQL)
- 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
- 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.
- 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.