Chapter 7
APC

In this section the Advanced PHP Cache introduced in section 4.6 will be used.

7.1 Considerations

The idea behind APC has been discussed in detail already (see sections 4.6 and 5.3.2). Nevertheless here is a short overview of what APC does:

7.1.1 Compiler Cache

For PHP being a scripting language, the script code has to be compiled to a runnable intermediate format each time the script is executed. This matches the idea of quick prototyping as a change to the script file is immediately applied when the file is saved.

The ratio between necessary compilation and useless recompilation is very bad, though. Especially when the application is finished the recompilations without a code change exceed necessary ones by far.

That is where compiler caches hook in. They store the result and the compilation and reuse this intermediate code for the next request. Before that, a quick check is made whether the script has been modified or not, of course. In that case the cached code is invalid and a recompilation is initiated.

The magnitude of this tuning even increases when you consider that the compilation process has to be initiated for every file that is included by the first script.

7.1.2 Code Optimization

A topic which has not yet been discussed but adds to a speed increase of the compiled PHP scripts is the optimization of code. It is worth spending some effort (and therefore time) on optimizing the PHP code before storing it in the cache. The cost for doing this is minimal considering that the optimized code will be reused a few thousand times at least.

Although there is no documentation for APC on the topic of code optimization there are still quite a few resources. On the one hand the author of another PHP cache, Nick Lindridge, has written an article covering code optimization [Lin02]. According to [Sch04] the optimizations done are quite similar. On the other hand the source code (long live open source!) is available and documented well enough to give a brief overview on what it does.

These code optimizations should not be compared to what “real compilers” like GCC1 do [Jon05]. They concentrate instead on common cases that gain much from small adjustments. Longer and very comprehensive analysis would eventually not be worth the effort.

Here is just a short overview of these so-called “Peephole optimizations:”

7.1.3 Outputting Data

Especially the second point in the listing above shows the need for some more testing. Outputting is an important point for a script that is used to return data to the browser. Therefore the quickest method for transmitting data has to be determined.

In PHP there is a concept called “Output Buffering.”

Initially, output buffering was integrated with PHP because of the necessity to send HTTP headers before writing any output (compare to [Sur00]). This is because PHP instantly sends the output of the script to the browser (it “flushes” its buffer), but this can only be done after all headers have been sent to the client. If you wanted to set a cookie (which is done in the HTTP header with the Set-Cookie field) after you printed some text already, PHP returns an error message or the cookie is simply dismissed.

Now output buffering not only enables the programmer to set headers at any stage, but also performance benefits from the concept.

Instead of sending all data instantly to the browser, the data is stored in an internal buffer. Therefore, all headers can be modified until output buffering is terminated or the script is finished. At this point the headers and all data stored in the buffer are sent to the browser.

A fine thing is the optional callback function. It allows the programmer to modify the contents of the buffer before it is sent. This can be used to compress the output, e.g. with gzip (see below) or to modify it for compatibility with character encodings [Kir05].

7.1.4 Programmer’s View

The boring thing about this section is that there is nothing to do for the programmer.

7.2 Preparation

APC is activated easily. In the php.ini file just a line

extension = /usr/lib/php4/apc.so

has to be added (see the patch file in the appendix, Listing A.3).

7.2.1 Output Buffering

For testing the output behaviour, three additional scripts have been created. Common to all of them is the generation of random output of a double quoted string (which is examined for contained variables by PHP) as shown in listing 7.1.


Listing 7.1: Generate lengthy random output
 
1<?php 
2for ($i = 0; $i < 5000; $i++) { 
3  echo str_repeat("This is a test string", rand(1, 4)); 
4} 
5?>

These lines were not integrated (although it would be obvious) with another file to be included in order to have these lines included with the compiler cache separately for each file.

no.ob-start(.php) uses no output buffering, i.e. the PHP function ob_
start() is never called.

ob-start.nogz(.php) just calls ob_start() at the beginning of the script. This enables output buffering, but does not set a post-processing callback function.

ob-start.gz(.php) adds an internal callback function to output buffering: ob_gzhandler. Before sending the data, it is compressed, commonly by using gzip. If the browser reports (using the header field Accept-Encoding) that it has no support for gzip, another supported compression method is chosen. For this test only gzip compression is used.

7.3 Results

The effects of switching on APC are not as extraordinary as those for Squid. They apply to all of the tested scripts.



Table 7.1: APC Benchmarking results (Requests/s)
APC off
APC on
Concurrent requests
File 10 100 10 100





skeleton-t.php 112.17103.75409.11506.44
pres-skel-t.php7.27 7.26 9.01 8.91
index.php 2.83 2.47 3.25 2.76
links.php 4.31 3.69 4.90 4.19

skeleton-t.php (Figure 7.1) receives the highest benefit from APC. This is due to two points: There is no need to connect to the database and only little output is made. The speed gained from dismissing the database is quite obvious, but an interesting point is the cost of outputting data. We will take a closer look at this point in Section 7.3.1.



Figure 7.1: APC benchmark: skeleton-t.php
PIC



Figure 7.2: APC benchmark: pres-skel-t.php
PIC



Figure 7.3: APC benchmark: index.php
PIC



Figure 7.4: APC benchmark: links.php
PIC

Also scripts that need to build a connection to the database receive a gain in speed. The difference in speed lies between 10 and 25 percent, which is quite a value for literally uncommenting a line. In the rather uncommon case of no database connection (we will see later how to make scripts independent of a database) the gain hits a 500%.

The results for index.php (Figure 7.3) and links.php (Figure 7.4) show that with APC the same performance (when speaking of requests per second) for 100 concurrent requests2 can be achieved as for 10 CCR without APC.

When comparing requests per second for different concurrency rates it still has to be considered that the speed from the view of a client still varies. If benchmark A and B have the same rate of requests/s, the total time is also the same regardless of the concurrency rate.

The response time “felt” by the client is therefore longer for more concurrent requests.

Considering two benchmarks with the same speed of 25 requests per second, 25 users requesting the page at the same time will have to wait one second each. If the request rate stays the same and 250 users want to access the page at the same time, every user has to wait 10 seconds. The request rate is still 25 requests per second (and therefore a “good” result).

7.3.1 Results for output testing

In the previous section a cause for the achieved results has been claimed to be the output performance of PHP. Therefore, some extra testing was included to check how different settings for outputting data perform.

As stated before and can be seen in Figure 7.5, output buffering increases speed. When looking at the dark grey bars representing the results without APC we see that for 10 concurrent requests output buffering increases speed by only 2%. With APC the gap increases to about 5% (see Table 7.2).

For 100 CCR there is not a gain but a loss. This can be explained by the higher memory usage that output buffering takes. With many concurrent requests, much data has to be stored in a buffer: a mean size of 262,500 bytes sums up to 25 mega bytes only used for buffered data (there is an additional overhead, of course). With the additional overhead of Apache binaries this can quickly fill memory.



Table 7.2: Output benchmarking results (Requests/s)
Files (.php)
CCRAPCno.ob-startob-start.nogzob-start.gzskel-t






10 off 38.77 40.29 28.72 112.17
10 on 48.68 51.45 35.69 409.11
100 off 37.59 35.32 26.86 103.75
100 on 46.02 43.01 33.29 506.44



Figure 7.5: APC benchmark: Skeletons with(out) ob_start() (10 CCR)
PIC



Figure 7.6: APC benchmark: Skeletons with(out) ob_start() (100 CCR)
PIC

The slowest output comes from gzip compressed. This is due to the expensive algorithm for compressing data. Still, using this mode can be recommended as due to less data (text data is very suitable for compression [BK93]) that needs to be transferred, the bandwidth becomes less a factor for performance. It is also a cost factor: usually you have an account-based traffic limit; using compression you can serve more visitors at the same price.

The results for 100 CCR do not show a better performance for output buffering than with 10 CCR. Instead, there is only a (very small) gain with APC on and gzip compression turned off.

For documents that do not return any contents (skeleton-t.php) the rate is evidently even higher since no output has to be stored or sent. Only the headers are sent in this case. This reduces the used network bandwidth even more. The headers are not compressed under any circumstances.

Although the gain in speed is not enormous, the use of output buffering (with enabled compression) is heavily recommended. For large projects also bandwith plays an important role and even a small loss of speed in combination with a reduction of traffic can save a lot of money.

7.4 Conclusions for APC

APC cache proves to speed up each PHP script requested more than once. This shows how much time is consumed for compilation when executing a PHP script.

Turning off APC or not installing a compiler cache is simply a waste of CPU time and it should always be considered to install and activate APC.