A couple of weeks ago I shared some performance tests for server response time as well as Speed Index and time to first byte that I ran on this site before and after moving to a new web host. The posts were part of an on-again off-again series about website performance that I’ve been running the last year or so. In every post up to this point I’ve talked as though all sites were built in fundamentally the same way. As you know, not all sites are built in the same way.
Two major classes of sites, as far as performance is concerned, are static and dynamic. The big difference is that dynamic sites are built on the fly. The page that displays in the browser doesn’t exist as a single file. Instead, it’s programmatically generated when requested. Dynamic pages run server side code that calls other files, and queries databases to gather all the parts necessary for assembly.
Dynamic pages are converted into static HTML pages that are ultimately delivered across the network, but they naturally have more work to do during the conversion and consequently take more time to finally reach their destination after being requested.
I want to pick up the series again spend some time talking about dynamic web pages and some of the specific performance issues they face and how you can improve that performance. Because I’m most familiar with PHP and MySQL and because the odds are it’s the same for you, I’ll focus the series on both, but know that much of what’s true for each is also true for other languages and databases. The specifics might change, but the general ideas are the same.
The Critical Path
I’ve started a number of these mini-series with a quick mention of the critical path, which are the things that need to happen before a web page can load in your browser after you request it.
- DNS Lookup
- Browser sends an HTTP request
- Server responds and sends back the requested HTML file
- Browser begins to render HTML
The first three steps in the path lead to the time to first byte (TTFB).
There’s no difference in the first two steps of the path for static and dynamic sites. It’s in the third step where you see a difference. Again it’s ultimately an HTML page that’s delivered over the web, but the server needs to do a little more work and take a little more time to generate that HTML page.
Somewhere in the server responds part of step three, the server converts dynamic code into static HTML. How does it do that?
Requests for Dynamic Pages
What happens when a dynamically built page is requested? Again the first two steps in the critical path are the same. There’s a DNS lookup and then an HTTP request is sent for the page.
If the URL in question is for a dynamic page, the server needs to execute the server side code to build the HTML page that will be delivered. The requested file is run through an interpreter or parser. The code executes and possibly queries the database to find the specific content to be displayed.
Consider a very simple example:
It’s a simple page with a single line of PHP to echo some text on the page. I even included the the text to echo to make it as simple as possible and yet, as simple as it is, the one line of PHP code means the file needs to run through an interpreter, which takes some amount of time no matter how much.
It all happens faster than it took me to type our what happens, but it’s still slower than if everything were already included directly in a single file without any need to run code through a runtime engine.
As you would expect, static pages load faster than dynamic ones since they don’t need to be sent elsewhere to execute code and since they don’t need to connect to a database, query it, and wait for the database to return results.
However, the odds are your site is dynamic, possibly on WordPress like mine. What can we do to speed up the execution time of the code? What can we do to speed up how quickly the code can get results from a database? Let’s focus on the code, specifically PHP for now and in a few weeks I’ll get to the database.
How the Zend Engine Works
The default PHP interpreter is the Zend Engine. It reads your PHP code and converts it into an abstract syntax tree (AST), which is translated into something called opcodes. Opcodes are execution units for the Zend Virtual Machine.
Opcodes are low level in that they’re closer to the machine code the server hardware understands as opposed to the higher level abstraction of PHP that’s easier for you and me and every other person to read and work with.
The first time the code is run it will need to go through the conversion to opcodes, but when the same code is run again a cached version of the opcodes can be used instead of having to convert from high level to low level code.
Because PHP code is interpreted each time the code needs to execute, it means a lot of identical code is recompiled again and again, at least it would be without the ability to cache it.
OPcache is an extension for PHP that saves compiled code into memory so the next time the same code needs to be compiled, PHP can check the cache to see if it’s already there. It checks file sizes and timestamps to determine if the source code has changed and if not, the cache will be used.
You can check to see if OPcache is enabled using phpinfo() or through the command line with php -v. It’s possible your hosting company enables it by default as mine did, but if you have to do it yourself, you can enable OPcache by editing your php.ini file and adding a few lines depending on your specific needs.
I’ll point you to some resources with more information for enabling the cache and for tweaking other settings you might want to change from their default.
- OPcache Documentation
- Installing OPcache
- OPcache Runtime Configuration
- PHP OPcache
- How To: Enable PHP 7 OPcache on Ubuntu 16.04
- PHP Performance I: Everything You Need to Know About OpCode Caches
The main thing I wanted to get across in this post is that there are additional steps to serve dynamic web pages as opposed to static ones. The dynamic pages first have to be assembled and then converted into the static HTML that gets delivered over the network.
The Zend Engine is likely the interpreter that reads your PHP code, converts it into opcodes that are low level as opposed to the high level abstraction that is PHP.
Instead of converting the code each time it’s called, you can take advantage of OPcache which will store a compiled version of the opcodes that can be used if the original source code hasn’t changed.
Next week I’ll continue talking about PHP and I’ll share some tips for writing more efficient PHP so you can avoid some common bottlenecks that might potentially end up in your code.
Download a free sample from my book, Design Fundamentals.