Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced PHP Programming

.pdf
Скачиваний:
67
Добавлен:
14.04.2015
Размер:
7.82 Mб
Скачать

9

External Performance Tunings

IN ANY TUNING ENDEAVOR,YOU MUST NEVER lose sight of the big picture.While your day-to-day focus may be on making a given function or a given page execute faster, the larger goal is always to make the application run faster as a whole. Occasionally, you can make one-time changes that improve the overall performance of an application.

The most important factor in good performance is careful and solid design and good programming methodologies.There are no substitutes for these. Given that, there are a number of tunings you can make outside PHP to improve the performance of an application. Server-level or language-level tunings do not make up for sloppy or inefficient coding, but they ensure that an application performs at its best.

This chapter quickly surveys several techniques and products that can improve application performance. Because these all exist either deep inside PHP’s internals or as external technologies, there is very little actual PHP code in this chapter. Please don’t let that dissuade you from reading through the chapter, however; sometimes the greatest benefits can be gained through the symbiotic interaction of technologies.

Language-Level Tunings

Language-level tunings are changes that you can make to PHP itself to enhance performance. PHP has a nice engine-level API (which is examined in depth in Chapter 21, “PHP and Zend Engine Internals” and Chapter 23,“Writing SAPIs and Extending the Zend Engine”) that allows you to write extensions that directly affect how the engine processes and executes code.You can use this interface to speed the compilation and execution of PHP scripts.

Compiler Caches

If you could choose only one server modification to make to improve the performance of a PHP application, installing a compiler cache would be the one you should choose. Installing a compiler cache can yield a huge benefit, and unlike many technologies that

220Chapter 9 External Performance Tunings

yield diminishing returns as the size of the application increases, a compiler cache actually yields increasing returns as the size and complexity increase.

So what is a compiler cache? And how can it get such impressive performance gains? To answer these questions, we must take a quick peek into the way the Zend Engine executes PHP scripts.When PHP is called on to run a script, it executes a two-step process:

1.PHP reads the file, parses it, and generates intermediate code that is executable on the Zend Engine virtual machine. Intermediate code is a computer science term that describes the internal representation of a script’s source code after it has been compiled by the language.

2.PHP executes the intermediate code.

There are some important things to note about this process:

nFor many scripts—especially those with many included—it takes more time to parse the script and render it into an intermediate state than it does to execute the intermediate code.

nEven though the results of step 1 are not fundamentally changed from execution to execution, the entire sequence is played through on every invocation of the script.

n This sequence occurs not only when the main file is run, but also any time a script is run with require(), include(), or eval().

So you can see that you can reap great benefit from caching the generated intermediate code from step 1 for every script and include.This is what a compiler cache does.

Figure 9.1 shows the work that is involved in executing a script without a compiler cache. Figure 9.2 shows the work with a compiler cache. Note that only on the first access to any script or include is there a cache miss. After that, the compilation step is avoided completely.

These are the three major compiler caches for PHP:

nThe Zend Accelerator—A commercial, closed-source, for-cost compiler cache produced by Zend Industries

nThe ionCube Accelerator—A commercial, closed-source, but free compiler

cache written by Nick Lindridge and distributed by his company, ionCube

n APC—A free and open-source compiler cache written by Daniel Cowgill and me

Chapter 23, which looks at how to extend PHP and the Zend Engine, also looks in depth at the inner working of APC.

The APC compiler cache is available through the PEAR Extension Code Library (PECL).You can install it by running this:

#pear install apc

Language-Level Tunings

221

Figure 9.1 Executing a script in PHP.

To configure it for operation, you add the following line to your php.ini file:

extension = /path/to/apc.so

Besides doing that, you don’t need to perform any additional configuration.When you next start PHP, APC will be active and will cache your scripts in shared memory.

Remember that a compiler cache removes the parsing stage of script execution, so it is most effective when used on scripts that have a good amount of code. As a benchmark, I compared the example template page that comes with Smarty. On my desktop, I could get 26 requests per second out of a stock PHP configuration.With APC loaded, I could get 42 requests per second.This 61% improvement is significant, especially considering that it requires no application code changes.

Compiler caches can have especially beneficial effects in environments with a large number of includes.When I worked at Community Connect (where APC was written), it was not unusual to have a script include (through recursive action) 30 or 40 files. This proliferation of include files was due to the highly modular design of the code base, which broke out similar functions into separate libraries. In this environment, APC provided over 100% in application performance.

222 Chapter 9 External Performance Tunings

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 9.2 Script execution with a compiler cache.

Optimizers

Language optimizers work by taking the compiled intermediate code for a script and performing optimizations on it. Most languages have optimizing compilers that perform operations such as the following:

nDead code elimination—This involves completely removing unreachable code sections such as if(0) { }.

nConstant-folding—If a group of constants is being operated on, you can perform the operation once at compile time. For example, this:

$seconds_in_day = 24*60*60;

Language-Level Tunings

223

can be internally rendered equivalent to the following faster form:

$seconds_in_day = 86400;

without having the user change any code.

nPeephole optimizations—These are local optimizations that can be made to improve code efficiency (for example, converting $count++ to ++$count when the return value is used in a void context). $count++ performs the increment after any expression involving $count is evaluated. For example, $i = $count++; will set $i to the value of $count before it is incremented. Internally, this means that the

engine must store the value of $count to use in any expression involving it. In contrast, ++$count increments before any other evaluations so no temporary value needs to be stored (and thus it is cheaper). If $count++ is used in an expression where its value is not used (called a void context), it can be safely be converted to a pre-increment.

Optimizing compilers can perform many other operations as well.

PHP does not have an internal code optimizer, but several add-ons can optimize code:

nThe Zend Optimizer is a closed-source but freely available optimizer.

nThe ionCube accelerator contains an integrated optimizer.

nThere is a proof-of-concept optimizer in PEAR.

The main benefits of a code optimizer come when code is compiled and optimized once and then run many times.Thus, in PHP, the benefits of using an optimizer without using a compiler cache are very minimal.When used in conjunction with a compiler cache, an optimizer can deliver small but noticeable gains over the use of the compiler cache alone.

HTTP Accelerators

Application performance is a complex issue. At first glance, these are the most common ways in which an application is performance bound::

nDatabase performance bound

nCPU bound, for applications that perform intensive computations or manipulations

nDisk bound, due to intensive input/output (I/O) operations

nNetwork bound, for applications that must transfer large amounts of network data

The following chapters investigate how to tune applications to minimize the effects of these bottlenecks. Before we get to that, however, we need to examine another bottleneck that is often overlooked: the effects of network latency.When a client makes a request to your site, the data packets must physically cross the Internet from the client location to your server and back. Furthermore, there is an operating system–mandated

224Chapter 9 External Performance Tunings

limit to how much data can be sent over a TCP socket at a single time. If data exceeds this limit, the application blocks the data transfer or simply waits until the remote system confirms that the data has been received.Thus, in addition to the time that is spent actually processing a request, the Web server serving the request must also wait on the latency that is caused by slow network connections.

Figure 9.3 shows the network-level effort involved in serving a single request, combined with times.While the network packets are being sent and received, the PHP application is completely idle. Note that Figure 9.3 shows 200ms of dead time in which the PHP server is dedicated to serving data but is waiting for a network transmission to complete. In many applications, the network lag time is much longer than the time spent actually executing scripts.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 9.3 Network transmission times in a typical request.

This might not seem like a bottleneck at all, but it can be.The problem is that even an idle Web server process consumes resources: memory, persistent database connections, and a slot in the process table. If you can eliminate network latency, you can reduce the

Language-Level Tunings

225

amount of time PHP processes perform unimportant work and thus improve their efficiency.

Blocking Network Connections

Saying that an application has to block network connections is not entirely true. Network sockets can be created in such a way that instead of blocking, control is returned to the application. A number of highperformance Web servers such as thttpd and Tux utilize this methodology. That aside, I am aware of no PHP server APIs (SAPIs; applications that have PHP integrated into them), that allow for a single PHP instance to serve multiple requests simultaneously. Thus, even though the network connection may be nonblocking, these fast servers still require a dedicated PHP process to be dedicated for the entire life of every client request.

Reverse Proxies

Unfortunately, eliminating network latency across the Internet is not within our capabilities. (Oh, if only it were!) What we can do, however, is add an additional server that sits in between the end user and the PHP application.This server receives all the requests from the clients and then passes the complete request to the PHP application, waits for the entire response, and then sends the response back to the remote user.This intervening server is known as a reverse proxy or occasionally as an HTTP accelerator.

This strategy relies on the following facts to work:

nThe proxy server must be lightweight. On a per-client-request basis, the proxy consumes much fewer resources than a PHP application.

nThe proxy server and the PHP application must be on the same local network. Connections between the two thus have extremely low latency.

Figure 9.4 shows a typical reverse proxy setup. Note that the remote clients are on highlatency links, whereas the proxy server and Web server are on the same high-speed network. Also note that the proxy server is sustaining many more client connections than Web server connections.This is because the low-latency link between the Web server and the proxy server permits the Web server to “fire and forget” its content, not waste its time waiting on network lag.

If you are running Apache, there are a number of excellent choices for reverse proxies, including the following:

nmod_proxy—A “standard” module that ships with Apache

nmod_accel—A third-party module that is very similar to mod_proxy (large parts actually appear to be rewrites of mod_proxy) and adds features that are specific to reverse proxies

nmod_backhand—A third-party load-balancing module for Apache that implements reverse proxy functionality

nSquid—An external caching proxy daemon that performs high-performance forward and reverse proxying

226 Chapter 9 External Performance Tunings

Internet

client

client

client

High Latency

Internet Traffic

reverse proxy

low latency connection

PHP webserver

Figure 9.4 A typical reverse-proxy setup.

With all these solutions, the proxy instance can be on a dedicated machine or simply run as a second server instance on the same machine. Let’s look at setting up a reverse proxy server on the same machine by using mod_proxy. By far the easiest way to accomplish this is to build two copies of Apache, one with mod_proxy built in (installed in /opt/apache_proxy) and the other with PHP (installed in /opt/apache_php).

We’ll use a common trick to allow us to use the same Apache configuration across all machines:We will use the hostname externalether in our Apache configuration file. We will then map externalether to our public/external Ethernet interface in /etc/hosts. Similarly, we will use the hostname localhost in our Apache configuration file to correspond to the loopback address 127.0.0.1.

Reproducing an entire Apache configuration here would take significant space. Instead, I’ve chosen to use just a small fragment of an httpd.conf file to illustrate the critical settings in a bit of context.

A mod_proxy-based reverse proxy setup looks like the following:

DocumentRoot /dev/null

Listen externalether:80

MaxClients 256

KeepAlive Off

Language-Level Tunings

227

AddModule mod_proxy.c

ProxyRequests On

ProxyPass / http://localhost/

ProxyPassReverse / http://localhost/

ProxyIOBufferSize 131072

<Directory proxy:*> Order Deny,Allow Deny from all

</Directory>

You should note the following about this configuration:

nDocumentRoot is set to /dev/null because this server has no content of its own.

nYou specifically bind to the external Ethernet address of the server

(externalether).You need to bind to it explicitly because you will be running

a purely PHP instance on the same machine.Without a Listen statement, the first server to start would bind to all available addresses, prohibiting the second instance from working.

nKeepalives are off. High-traffic Web servers that use a pre-fork model (such as Apache), or to a lesser extent use threaded models (such as Zeus), generally see a performance degradation if keepalives are on.

nProxyRequests is on, which enables mod_proxy.

nProxyPass / http://localhost/ instructs mod_proxy to internally proxy any requests that start with / (that is, any request at all) to the server that is bound to

the localhost IP address (that is, the PHP instance).

nIf the PHP instance issues to foo.php a location redirect that includes its server name, the client will get a redirect that looks like this:

Location: http://localhost/foo.php

This won’t work for the end user, so ProxyPassReverse rewrites any Location redirects to point to itself.

n ProxyIOBufferSize 131072 sets the size of the buffer that the reverse proxy uses to collect information handed back by PHP to 131072 bytes.To prevent time spent by the proxy blocking while talking to the browser to be passed back to the PHP instance, you need to set this at least as large as the largest page size served to a user.This allows the entire page to be transferred from PHP to the proxy before any data is transferred back to the browser.Then while the proxy is handling data transfer to the client browser, the PHP instance can continue doing productive work.

nFinally, you disable all outbound proxy requests to the server.This prevents open proxy abuse.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]