Wait! Do you really need to profile that PHP code? Are you sure you want to start down that time-consuming, tedious path? If you’re looking to squeeze some more performance out of your PHP web application, there are a few relatively quick and easy checks to perform that can give your performance a boost before you dive into refactoring the code. And even if you’re intent on profiling your PHP code, you should still look at these areas to make sure you’re getting maximum performance.

Cache In On OPCache

One of PHP’s strengths is that it’s interpreted on the fly into executable code called opcodes, so you can develop rapidly and test without pausing to compile your code with every change you make. However, it’s inefficient and slow to recompile the identical code each time that code runs on your website.

For many years, opcode cache has been a go-to solution for this particular slowdown. These caches are PHP extensions that create hooks into the compilation system and save the output of compiled code into memory. Then in future runs, PHP checks to make sure that the source file has not changed — via timestamps and file size checking — and if it hasn’t, it runs the cached copy of the code.

The most famous of these caches was APC or Alternative PHP Cache. APC not only provided an opcode cache, but also permitted making user data persistent in shared memory as well.

Given the importance of having an opcode cache configured to get optimal performance out of a PHP application, the PHP core team decided to include one by default with all versions of PHP since version 5.5. They chose OPCache, formerly Zend Optimizer+. Part of the commercial Zend Server offering, Zend Optimizer has now been open-sourced back to the community.

(To learn more about the importance of OpCache to PHP application performance, see this excellent article by my colleague Rob Bolton.)

Look Outside Your Application

Maybe it’s not your PHP at all that’s slowing your application down, especially if you’ve implemented opcode cache. It’s more than likely that at least several of your application bottlenecks happen when accessing external resources. Let’s look at a couple suspects.

Database Delays

It’s not untypical for the database layer to account for 90 percent of measured execution time for a PHP application. So it makes sense to spend the necessary time to review your codebase for all instances of database access.

First and most obvious, turn on the slow SQL logs and find and fix slow SQL queries. Then proceed to query the queries themselves. Are they efficient? Do you make the same queries multiple times in one execution of your code? (Even with a query cache, that’s still inefficient.) Are you making too many queries? Do you have queries hitting a table without an appropriate index?
Investing a little time to fix your queries can noticeably reduce your database access time and noticeably increase your application performance.

Filesystem Snafus

I/O, I/O, who knows where the time goes? Some of it is with all the in-and-out of your file system. So study your filesystem for the same kinds of inefficiencies you looked for in your database queries. Some likely time-consuming culprits: reading in local files, processing XML, image processing, or using the filesystem for session storage.

Specifically, look for code that would cause a file stat to happen — reading of a file’s statistics, such as the date it was last modified. Functions such as file_exists(), filesize(), or filetime() cause file stats to happen, and are easy to leave accidentally in a loop. Never do something twice that only needs to be done once. That’s the worst kind of wasted time.

Keep Your Eye On APIs

What other external resources do you rely on? It’s a rare application that doesn’t leverage APIs. Unfortunately, in many or most cases, you don’t have control over the remote APIs you’re using, so you can’t do anything directly about their performance. You can, however, mitigate the effect of API performance in your code through techniques such as caching API output or making API calls in the background.

Your main goal is to protect your user from a failing or misbehaving API. Make sure you have reasonable timeouts in place for any API requests and, to the best of your application’s ability, be ready to display your application’s output without the API’s response.

Now Profile Your PHP

If you’re lucky, just enabling an opcode cache and optimizing external resource usage is enough to get the performance gains you need at the moment. But eventually, as your application needs increase, you’ll need or want to go deeper to get better performance to maintain or boost user experience and conserve hosting costs.

A number of open source tools can help you profile your PHP code and discover where the most time is being spent. One of the most common is Xdebug. Xdebug does require compiling and running special extensions on your server, and was originally intended, as its name implies, to be a debugging tool; the profiling aspects were added later.

It’s not easy to keep application performance in step with ever-increasing user expectations. But looking into and optimizing the functionality described above can help make sure your PHP application is performing at its best.

PHP 5.5 introduced opcode caching into the core via OPCache. OPCache was previously known as Zend Optimizer+, and although free, was closed source. Zend decided to open source the implementation, and include it in the core PHP distribution. OPCache is also available as an extension through pecl, and is compatible all the way back to PHP 5.2. While other opcode caching solutions like APC exist, now that OPCache is bundled with PHP, it will likely become the standard going forward.

What is an opcode cache, and how does it work? Every time a PHP script is requested, the PHP script will be parsed and compiled into opcode which then is executed in the Zend Engine. This is what allows PHP developers to skip the compilation step required in other languages like Java or C# — you can make changes to your PHP code and see those changes immediately. However, the parsing and compiling steps increase your response time, and in a non-development environment are often unnecessary, since your application code changes infrequently.

When an opcode cache is introduced, after a PHP script is interpreted and turned into opcode, it’s saved in shared memory, and subsequent requests will skip the parsing and compilation phases and leverage the opcode stored in memory, reducing the execution time of PHP.

How much benefit can you expect from an opcode cache? Like many things in life, the answer is it depends. To test the benefit of OPCache, we have taken an existing PHP demo application used at AppDynamics, and installed OPCache. The OPCache settings were fairly straightforward, but we opted to use 0 for the refresh rate, which means a script will never be checked to see if it’s updated. While applicable for a production environment, it means you must delete the opcache cache when deploying new code.

The demo application is a simple e-commerce site built on top of Symfony 2 and PHP 5.4, leveraging a MySQL database, memcache and a backend Java service. For the test, the demo application is running on a medium ec2 instance (database, memcached, and Java services are on separate instances) with a small but steady amount of load on four different pages within the application.

In order to understand the performance benefit of enabling OPCache, the AppDynamics PHP agent was installed. The PHP agent auto-discovers application topology, and tracks metrics and flow maps for business transactions, app services, and backends in your web application by injecting instrumentation in the PHP-enabled web server instance at runtime. By leveraging the metrics collected by AppDynamics, we can see the decrease in response time OPCache provides.

Once OPCache was enabled on the application, there was a 14% reduction in response time for the application overall. AppDynamics has a feature called “compare releases” which allows you to select to separate time ranges and compare key metrics. In the screenshot below, we are comparing two small time ranges – March 14th from 9:00am to 12:00pm and March 14th from 1:00pm to 4:00pm, as OPCache was enabled at 12:10pm on March 14th.

While a 14% decrease in response time is good, especially considering the minimal amount of work required to install and enable OPCache, it may be less than you were expecting. The overall application decrease in response time obscures the variation seen across different pages within the application.

AppDynamics analyzes a concept called a business transaction, which represents an aggregation of similar user requests to accomplish a logical user activity. In this demo application, we were generating load on four specific business transactions: View Product, Search, Login, and Loop. Using the compare releases functionality from AppDynamics, instead of focusing on the individual business transactions, we see a lot of variation between the different business transactions in response time once OPCache was introduced.

Let’s look at each business transaction and determine why some transactions saw a large reduction in response time, while others experience a moderate or minimal decrease in response time.

The login business transaction saw a substantial decrease in response time, 74%.

The Login business transaction is relatively simple, as shown by the AppDynamics flow map below (a flow map graphically represents the tiers, nodes, and backends and the process flows between them in a managed application). The transaction goes through a standard Symfony controller and renders a basic html form to login — there are no databases or external services involved. On this particular business transaction, a majority of the response time was spent parsing and compiling the PHP. Once those steps are removed via an OPCache, the response time drops dramatically.

The Product View business transaction experience a similar decrease in response time at 74%.

The Product View business transaction relies on both memcache and MySQL database, although only 2% of the request time is spent outside of PHP (after OPCache was turned on, this increased to 8%), and hence we see a large benefit from opcode caching like we saw in the Login business transaction.

The Search business transaction response time dropped by only 8%.

Looking at the flow map, the majority of the response time is spent on the Java backend service, with a small amount of time spent in the network. Enabling OPCode resulted in a 70% reduction in response time in PHP, but since PHP was only 11% of the overall response time, the effect was muted.

Before:After:

The Loop business transaction is not part of the demo application, but was added specifically for this test. The Loop business transaction saw only a 6% decrease in time.

Loop is 3 lines of code – it loops 10 millions times and increments a counter on each loop. The amount of time it takes to parse and compile the code is small compared with the time it takes to actually execute the opcode, hence the small decrease in response time form enabling opcode caching.

To illustrate the difference we can review a call graph of each transaction. AppDynamics captures snapshots of certain requests, and a snapshot contains the call graph. Looking at the call graphs below for Loop and Login, we see Login has a lot more PHP code to parse and compile:

Loop

Login

In summary, opcode caches provide a quick way to decrease the latency of your PHP application and should always be enabled in production PHP environments. The decrease in response time will primarily depend on two things: 1. The amount of time the request spends in PHP. If your application spends a lot of time waiting for a database to return results or relies on slow third party web services, the decrease in response time from an opcode cache will be on the lower side. 2. If your PHP scripts are very basic, including only the minimal amount of code to process the request, as compared to using a framework, then the reduction in response time will also be limited. Get started by upgrading to PHP 5.5 or installing Zend OpCache today.

Take five minutes to get complete visibility and control into the performance of your production applications with AppDynamics Pro today.