In a short time, PHP has become a popular programming language for Web applications. For the beginner, PHP is simple to install and easy to learn. For the more experienced developer, it (as of V5) offers robust object-oriented features. The community of PHP developers is enormous, and a significant number of open source and commercial libraries and tools extend the capabilities of the core language. PHP is favored by so many because it produces visible results rapidly.
Much like other scripting languages used for Web applications, including Perl, Python, and Ruby, PHP code is parsed, translated to opcodes (primitive instructions -- akin to assembly language -- that the PHP engine executes directly), and executed each and every time an HTTP request summons it. For negligible and low demand, your server executes this complex rendering process seemingly instantly. But as the number of pages served increases, interpretation -- in essence, rework -- can tax the system. In some cases, the "compilation" of your PHP code can far exceed the time required to execute the code. Hence, as demand grows, you can readily become a victim of your own success, as serving more dynamically interpreted, dynamically generated pages requires more and more resources.
If your site had an unlimited budget for processors and RAM, you'd likely never have to optimize your application stack (hardware, operating system, database, Web server, and PHP code) to keep your site responsive. However, because money is typically the scarcest of resources, tweaking for performance is inevitable. Tuning might mean adding memory to a starved system, modifying operating system parameters, hastening the Web or database server, rewriting code for efficiency, or some combination thereof. Each can contribute to gains.
Don't burn cycles -- Recycle
Yet another way to conserve CPU cycles is simply to reduce the amount of rework required to run your PHP application. Certainly, there is no need to translate the same PHP code each time. After your PHP code has been translated into opcodes, it can be saved and reused again and again -- until the original code is modified. Indeed, caching -- saving and reusing the intermediate PHP code, the opcodes -- is the idea behind several PHP accelerators, including the open source Alternative PHP Cache (APC), Turck MMCache for PHP, XCache, eAccelerator, and the commercial Zend Platform. The latter three cache and optimize the byte code, providing for even greater speedups.
This month, let's explore how to install, deploy, and configure XCache. XCache is a relative newcomer, but many sites are reporting good results with it. In addition, it is easy to build, install, and configure because it's implemented as a PHP extension. Recompiling Apache and PHP isn't required.
This article is based on XCache V1.2.0. It reliably supports PHP V4.3.11 to V4.4.4, PHP V5.1.x to V5.2.x, and early versions of PHP V6. (XCache doesn't support PHP V5.0.x.) XCache works with mod_php and FastCGI, but not with the Common Gateway Interface (CGI) or the command-line PHP interpreter. The XCache source code builds on a variety of systems, including FreeBSD, Sun Solaris, Linux®, and (as shown here) on Mac OS X. XCache can be built on Microsoft® Windows®, as well, using the Cygwin UNIX® emulation environment or Visual C. You can build XCache for Cygwin or for native Win32. The latter target is compatible with the official Win32 release of PHP.
The demonstrations shown here are based on Apache V2.2.3, PHP V5.2.0, XCache V1.2.0 (released 10 Dec 2006), and Xcode V2.4.1 on Mac OS X V10.4.8 Tiger. The hardware platform is an Apple MacBook with a 2-GHz Intel® Core Duo and 2 GB of RAM.
Step-by-step instructions for building XCache
Before you continue, ensure that your installation of PHP works as is and verify that
phpize is in your shell's
PATH. You should also have a C compiler, such as the GNU Compiler Collection (GCC), and a suite of development tools that includes
m4. On Mac OS X, the free Xcode software development environment provides the necessary build tools.
Complete the instructions below to build, deploy, and benchmark XCache on Mac OS X. Builds on other platforms are similar. If you use Linux, your distribution may already include XCache or may make it available to you in a prepackaged form.
Increase Mac OS X's shared memory
Begin by increasing the amount of shared memory set aside on Mac OS X. To do so, create (or edit) the file /etc/sysctl.conf and create the following entries:
Listing 1. Increase amount of shared memory set aside on Mac OS X
kern.sysv.shmmax=33554432 kern.sysv.shmmin=1 kern.sysv.shmmni=32 kern.sysv.shmseg=8 kern.sysv.shmall=8192
These settings increase the amount of shared memory to 32 MB. If you find that you need to expand shared memory further, set
kern.sysv.shmall to the value of
kern.sysv.shmmax divided by the hardware page size, which you can find using
sysctl hw.pagesize. For example, if you want 128 MB of shared memory, set
kern.sysv.shmmax=134217728 and set
To realize your modifications, restart Mac OS X. When rebooted, verify that the new settings took effect by typing:
sysctl -a | grep kern.sysv
Build XCache from source
Next, build XCache from source code. Download the source from http://xcache.lighttpd.net. When you have the code, unpack it, and change to the new directory that the .tar file creates.
Listing 2. Build XCache from source
$ cd /tmp $ wget http://18.104.22.168/pub/XCache/Releases/xcache-1.2.0.tar.gz $ tar xzf xcache-1.2.0.tar.gz $ cd xcache
phpize to prepare XCache for compilation.
Listing 3. Run
$ phpize Configuring for: PHP Api Version: 20020918 Zend Module Api No: 20020429 Zend Extension Api No: 20050606
configure to create a makefile appropriate for the native operating system.
Listing 4. Run
configure to create a makefile
$ ./configure --enable-xcache --enable-xcache-coverager checking build system type... i686-apple-darwin8.8.1 checking host system type... i686-apple-darwin8.8.1 ... creating libtool configure: creating ./config.status config.status: creating config.h
--enable-xcache option includes XCache support, and
--enable-xcache-coverager includes an additional feature to measure the efficacy of the accelerator. To enable the opcode optimizer, add
Of course, the next step is to build and install the code using the
make command. Run
make, then run
make install as root.
Listing 5. Build and install the code using
$ make ... cp ./xcache.so /Users/strike/tmp/xcache/modules/xcache.so Build complete. $ sudo make install Installing shared extensions: /usr/lib/php/extensions/no-debug-non-zts-20020429/
If the previous two actions go smoothly, you should now have XCache in /usr/lib/php/extensions/no-debug-non-zts-20020429/xcache.so. (The path /usr/lib/php/extensions/no-debug-non-zts-20020429 reflects the version of the API in use and the compilation options used to build PHP. If you enabled the experimental Zend Thread Safety feature, "no-debug" could be "debug," and "non-zts" could be "zts.")
Alter the php.ini file
Now that the extension is installed, you must alter your php.ini file to include and configure the XCache extension. Open /private/etc/php.ini and add the following lines to the file.
Listing 6. Alter php.ini for the XCache extension
[xcache-common] zend_extension = /usr/lib/php/extensions/no-debug-non-zts-20020429/xcache.so [xcache.admin] ; Change xcache.admin.user to your preferred login name xcache.admin.user = "admin" ; Change xcache.admin.pass to the MD5 fingerprint of your password ; Use md5 -s "your_secret_password" to find the fingerprint xcache.admin.pass = "0ad72f3f352fcd8acdf266bafd0ac48d" [xcache] ; Change xcache.size to tune the size of the opcode cache xcache.size = 24M xcache.shm_scheme = "mmap" xcache.count = 2 xcache.slots = 8K xcache.ttl = 0 xcache.gc_interval = 0 ; Change xcache.var_size to adjust the size of variable cache xcache.var_size = 8M xcache.var_count = 1 xcache.var_slots = 8K xcache.var_ttl = 0 xcache.var_maxttl = 0 xcache.var_gc_interval = 300 xcache.test = Off xcache.readonly_protection = On xcache.mmap_path = "/tmp/xcache" xcache.coredump_directory = "" xcache.cacher = On xcache.stat = On xcache.optimizer = Off [xcache.coverager] xcache.coverager = On xcache.coveragedump_directory = ""
Note: Some comments have been elided for brevity. To learn what each parameter controls, see the sample settings in the xcache.ini file included with the XCache source code.
The size of the opcode and variable cache is 32 MB, the maximum set aside in /etc/rc. For Mac OS X,
xcache.mmap_path must be a file name. And because the PHP code is going to run on a MacBook,
xcache.count is set to 2, the number of CPUs in the MacBook. To access the XCache statistics page, you must change the
xcache.admin.pass setting. Run:
$ md5 -s "password"
password is your password. Copy the output to
xcache.admin.pass. For example, if you want your password to be
$ md5 -s "op3nsesam3" MD5 ("op3nsesam3") = cd959ac3debe8f587546a3fa353b3268
Restart the Web server
With your XCache settings in place, restart your Apache Web server. On most systems, you can use
apachectl restart as root.
$ sudo apachectl restart /usr/sbin/apachectl restart: httpd restarted
Create a program to call phpinfo()
To verify that XCache is enabled, create a small PHP program to call
phpinfo() and open that file in your Web browser. You should see an XCache section like the one shown below.
Figure 1. phpinfo() method summarizes the XCache settings
To monitor XCache, install the administration pages found in the admin directory of the XCache source code. Copy the entire admin directory to your Apache document root. On Mac OS X, the document root is typically /Library/WebServer/Documents.
$ cp -pr admin /Library/WebServer/Documents
When the copy is complete, restart the Web server with
sudo apachectl restart. To verify that the administration panel works, point your browser to http://localhost/admin. You should see a dashboard similar to Figure 2.
Figure 2. The XCache Administration dashboard
Test an application
Running a benchmark
The Apache HTTP Web server ships with a utility named
ab, short for Apache HTTP server benchmarking tool. Use
ab to automate a large number of requests for PHP pages. The phpMyAdmin application is a good candidate because it's probably already installed on your system.
ab utility is simple to use: Provide a repeat count and a URL. The
ab utility requests that URL many times over and returns statistics. Because XCache is already enabled, the first benchmark will show performance with acceleration.
Before you run
ab, point your browser to http://localhost/phpmyadmin/. Visiting this page once loads all the PHP code used to render the page into the cache. Now, run the benchmark shown below, iterating 100,000 times.
Listing 7. Benchmark for phpMyAdmin
$ ab -n 100000 http://localhost/phpmyadmin ... Concurrency Level: 1 Time taken for tests: 14.597 seconds Complete requests: 100000 Failed requests: 98262 (Connect: 49131, Length: 49131, Exceptions: 0) Broken pipe errors: 0 Non-2xx responses: 50869 Total transferred: 25739714 bytes HTML transferred: 12005084 bytes Requests per second: 6850.72 [#/sec] (mean) Time per request: 0.15 [ms] (mean) Time per request: 0.15 [ms] (mean, across all concurrent requests) Transfer rate: 1763.36 [Kbytes/sec] received
The statistics of interest are requests per second and the total time to complete all the tests. For the former, a higher value is better; for the latter, lower is better.
Now, disable XCache in your php.ini file and run the benchmark again, as shown in Listing 8. You can comment out the reference to the XCache extension or turn all the XCache features off. Restart Apache before you run the benchmark again.
Listing 8. Benchmark for phpMyAdmin with XCache disabled
$ sudo apachectl restart $ ab -n 100000 http://localhost/phpmyadmin Concurrency Level: 1 Time taken for tests: 17.771 seconds Complete requests: 100000 Failed requests: 98256 (Connect: 49128, Length: 49128, Exceptions: 0) Broken pipe errors: 0 Non-2xx responses: 50872 Total transferred: 25741232 bytes HTML transferred: 12005792 bytes Requests per second: 5627.15 [#/sec] (mean) Time per request: 0.18 [ms] (mean) Time per request: 0.18 [ms] (mean, across all concurrent requests) Transfer rate: 1448.50 [Kbytes/sec] received
Here, with XCache disabled, the number of requests per second fell, reflecting that the Apache server took longer for each request. The time to run the entire suite of tests climbed, as well.
Although this is a simple benchmark -- the phpMyAdmin connection to the database could have been disabled to limit processing time to interpreting PHP alone -- and not highly scientific, it does demonstrate what you can achieve with XCache. For a tiny investment (and, thankfully, no need to recompile PHP or Apache), XCache can yield a relatively large return. The more complex your code, the greater the possible benefits.
If you're curious to see how well XCache is working, visit http://localhost/xadmin and click List PHP. You should see a list of PHP files in the cache, along with cache hits, the size of the code as measured in opcodes, the size of the source file measured in bytes, and more. Figure 3 shows some results taken when XCache was built specifically for the XAMPP stack package.
Figure 3. XCache administration page reflects state and contents of cache
As mentioned, XCache is one of a number of accelerator options. There are free open source alternatives and a commercial option in the powerful Zend software. Each PHP accelerator has its own set of system requirements, so choosing one depends largely on your existing or desired configuration and the characteristics of your application. It's difficult to recommend one over the other, but it is recommended that you install a compiler cache.
Tons of tuning options
In addition to caching, there are many other avenues to explore to speed your application. You can trim the fat from the PHP engine by omitting many of its more esoteric features. For example, if you don't use TCP/IP V6 (IPv6) networking, disable the feature when you build PHP. You can see the entire list of PHP configuration options by typing
./configure --help at the top of the PHP source code tree. No matter which configuration options you choose, add:
to your final configuration command. The former option produces the fastest PHP executable possible (without additional opcode optimizations by software like Zend Engine); the latter option takes PHP out of debug mode, which is needed only if you are trying to resolve issues in the PHP application server itself.
Of course, just like any C application, you can leverage the C compiler to build a better executable. If you run PHP as an Apache Dynamic Shared Object (DSO) on Linux or FreeBSD on x86 processors, consider adding the
-prefer-non-pic option to your
CFLAGS (the environment variable that stores C compiler options). Non-PIC builds PHP using position-independent code and provides something of a 10-percent boost. You can also use
CFLAGS to identify your processor type, as in
-march=opteron for the AMD Opteron processor.
Another speed enhancer is opcode optimization. Here, software such as the Zend Engine takes the time to optimize the opcodes produced during compilation, literally reducing the amount of work that the code performs.
Caching and optimization are transparent and require no additional programming muscle. To apply some elbow grease, pursue profiling, or examining where your code spends its time. A bottleneck is an obvious target for rework, as are wasteful or slow algorithms. Tweaking code to eke out cycles can be valuable -- but don't attempt any optimization without profiling first.
I'll revisit optimization in the coming months, along with treatises on debugging, fast text searches, alternate Web servers, and more. In the meantime, investigate one or more of the PHP accelerators and opcode optimizers. A few hours of tinkering is all that's needed to realize a 10- to 200-percent gain. Just imagine what your machines can do with all those spare cycles.
- Read Part 2 and Part 3 of this series.
- PHP.net is the resource for PHP developers.
- Check out the "Recommended PHP reading list."
- Browse all the PHP content on developerWorks.
- Expand your PHP skills by checking out IBM developerWorks' PHP project resources.
- To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
- Stay current with developerWorks' Technical events and webcasts.
- Using a database with PHP? Check out the Zend Core for IBM, a seamless, out-of-the-box, easy-to-install PHP development and production environment that supports IBM DB2 9.
- Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
- Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
Get products and technologies
- Download XCache.
- Download eAccelerator.
- Try the popular Alternative PHP Cache (APC).
- Download Turck MMCache for PHP.
- Innovate your next open source development project with IBM trial software, available for download or on DVD.
- The XCache IRC channel is temporarily hosted in the Lighttpd IRC channel's ZshWiki.
- Check out the XCache Forum.
- Participate in developerWorks blogs and get involved in the developerWorks community.
- Participate in the developerWorks PHP Developer Forum.