Make PHP apps fast, faster, fastest, Part 1: Boost performance, throughput with opcode cache software

PHP is a scripting language most often used to create Web applications. It's easy to learn and produces visible results quickly. However, because PHP is interpreted, PHP code is parsed and translated to opcodes every time it executes. An opcode cache eliminates that rework, making PHP applications faster.

Martin Streicher (martin.streicher@linux-mag.com), Editor in Chief, Linux Magazine

Martin Streicher is chief technology officer for McClatchy Interactive, editor-in-chief of Linux Magazine, a Web developer, and a regular contributor to developerWorks. He earned a master's degree in computer science from Purdue University and has been programming UNIX-like systems since 1986.



20 February 2007

Also available in Chinese Japanese

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 make and 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 kern.sysv.shmall=32768.

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://210.51.190.228/pub/XCache/Releases/xcache-1.2.0.tar.gz
$ tar xzf xcache-1.2.0.tar.gz
$ cd xcache

Run phpize to prepare XCache for compilation.

Listing 3. Run phpize
$ phpize
Configuring for:
PHP Api Version:         20020918
Zend Module Api No:      20020429
Zend Extension Api No:   20050606

Run 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

Here, the --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 --enable-xcache-optimizer.

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
$ 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"

where password is your password. Copy the output to xcache.admin.pass. For example, if you want your password to be op3nsesam3, run:

$ md5 -s "op3nsesam3"
MD5 ("op3nsesam3") = cd959ac3debe8f587546a3fa353b3268

and copy cd959ac3debe8f587546a3fa353b3268 to xcache.admin.pass.

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
The phpinfo() method summarizes the XCache settings

Monitor XCache

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
The XCache Administration dashboard

Test an application

Choose an application or two to test. You can use your own code or, if you want something more complex, try a large PHP application, such as phpMyAdmin or Serendipity.


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.

The 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
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:

--enable-inline-optimization --disable-debug

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 -march in 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.


Looking ahead

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.

Resources

Learn

  • 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

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=196453
ArticleTitle=Make PHP apps fast, faster, fastest, Part 1: Boost performance, throughput with opcode cache software
publish-date=02202007