FasterCGI with HHVM

作者: 空气 分类: HHVM 发布时间: 2014-11-02 01:53 ė150 6没有评论

Posted on December 17, 2013 by

Today, we are happy to announce FastCGI support for HHVM. FastCGI is a popular protocol for communication between an application server (e.g. running your PHP code) and a webserver. With support for FastCGI, you will be able to run HHVM behind any popular web server (Apache, Nginx, Lighttpd, etc). The webserver is in charge of handling all the intricate details of the HTTP protocol. HHVM is in charge of what it does best, running PHP code blazingly fast.

If you can’t wait to get your hands on the new feature, just jump to the Installation section. If you are curious about how the new feature was baked into HHVM or just want to learn how well it performs, read on.

How it works

FastCGI was designed to solve one crucial problem that plagued its predecessor CGI, namely: performance. The CGI protocol required that a new instance of the application be spawned on every request. Such a trade-off could have been tolerable for small native programs where start-up overhead was negligible. It is however incredibly difficult for a JIT compiler such as HHVM. The HipHop virtual machine keeps track of the code that ran in the past and reuses bytecode output whenever it sees a known file. HHVM also performs just-in-time compilation, i.e. translates snippets of the bytecode into native machine code when they are run frequently. Both of these features require that an instance of HHVM runs in server mode and lives on from request to request. FastCGI protocol enables that. Moreover, FastCGI can be configured to reuse the same connection for serving multiple requests. This involves serving both requests coming one after another and serving multiple requests in parallel. In the latter case data from multiple requests is multiplexed on a single connection.

HHVM FastCGI server uses asynchronous I/O. This means that an I/O thread will never block waiting on a single connection. Instead, system routines such as select() are used to monitor activity on multiple connections at once. When activity is detected (e.g. file descriptor becomes ready for reading or writing), an I/O thread executes the appropriate non-blocking action. This completes a single cycle of the event loop. This approach maximizes CPU use when serving I/O. Consequently, the threads spend only as much time blocked as absolutely necessary. I/O is served using multiple threads. Once the server is under heavy load, there will roughly be one I/O thread per CPU core. The incoming connections are then distributed evenly between the I/O threads using a round robin. As a result, operations on a single connection are always single-threaded and no additional synchronisation is needed.

A separate set of worker threads is used to execute PHP code. All the (partially) decoded requests are put inside a common concurrent queue. In a loop, each worker thread then pops a new request to be served. At this point, all the request headers are ready. For small requests, the request body will also typically be available. When a request is large (e.g. is a file upload), the worker thread might block waiting on more I/O to become available. The payload can be stored to a file in order to keep memory usage low. Once all the request data becomes available, PHP execution may begin. The output produced by a PHP script is buffered and sent to an I/O thread for further handling.


We conducted benchmarks using Nginx. In the first test we ran a WordPress instance. The second test showcases HHVM performance for computation-intensive tasks. We used a simple php script computing Fibonacci numbers.


The first test was performed using the WordPress example page. No changes were made to the wordpress deployment besides filling in mandatory database connection fields. Nginx was used as a web server. Apache bench was used for load testing using this command:


Sadly the results were pretty bad for PHP. Only 23 requests per second seem to indicate that WordPress is computationally very expensive to run in an interpreter.


On a cold start, HHVM still performed much better than PHP-FPM; however, there was a clear penalty associated with the initial JITing of PHP files:

After the initial warm-up the results got noticeably better:

That’s a surprisingly good result of roughly 40x higher throughput than the default PHP-FPM setting.


Next we decided to compare the two runtimes using a computationally expensive example. We wrote a simple function computing Fibonacci numbers using an exponential algorithm. Here are the results for computing Fib(N) for value N = 5, 15, 25, 30:


*only 1000 requests were performed to save time

**only 100 requests were performed to save time


In case where N = 5, the page required almost no computation at all. In this case it was visible just how well optimized FPM really is. It was over 50% faster than HHVM FastCGI server which indicates that there is still a lot of room for improvement in our implementation. However at N = 15 the amount of computation already shifted the balance to HHVM’s advantage. Where for HHVM the bottleneck was still the network, FPM was clearly CPU bound at this point with a result almost 3x worse than HHVM. At N = 30, HHVM just in time compilation really shined, yielding results almost 80x faster than PHP.


Now to the fun part: how to get HHVM FastCGI working on your machine? For popular Debian-based distros we prepared a set of pre-built packages:

Ubuntu 12.04

Ubuntu 13.10

Debian 7


If your distro is not on the list, you can still run HHVM FastCGI, with slightly more work (or you can take the scripts from that package and repackage it for your distro). First, install the latest release of HHVM. To run the server in FastCGI mode, pass additional parameters to HHVM runtime:

The server will now accept connections on localhost:9000 (only TCP sockets are supported for now). To turn the server into a daemon, change the value of mode to:

All the usual options accepted by the HHVM runtime are also available in the FastCGI mode. Please make sure that HHVM runs from the directory where you wish to serve PHP files.

Once the HHVM FastCGI server is up and running, it’s time to appropriately configure your webserver of choice. As an example, we include instructions for Apache and Nginx.

Making it work with Apache

The recommended way of integrating with Apache is using the mod_proxy_fcgi module. First, you need to enable mod_proxy and mod_proxy_fcgi modules, if you haven’t done so already. To enable the modules make sure you have the following symlinks created:

Next up, you need to insert a directive instructing Apache to send traffic to the FastCGI server. You can do so by inserting the following line in your apache.conf / httpd.conf file.

Please note that this will route all the traffic to the FastCGI server. If you want to route only certain requests (e.g. only those from a subdirectory or ending *.php, you can use ProxyPassMatch, e.g.

Consult Apache docs for more details on how to use ProxyPass and ProxyPassMatch.

Making it work with Nginx

The default FastCGI configuration from Nginx should work just fine with HHVM FastCGI. You will need to add the following directives inside one of your location directives:

The traffic for the surrounding location directive will then be routed to HHVM.

What’s next?

There are a couple of important features still missing from the initial implementation. The most notable is support for local UNIX sockets. At present, only less performant (however more flexible) TCP sockets are available. The priorities for this release were:

  • Correctness,
  • Getting the architecture of the solution right, so that performance can be improved in the future.

We haven’t really done any profiling on the new server yet. It is therefore quite likely that there is some low hanging fruit waiting to be picked. We are definitely looking forward to improving HHVM FastCGI performance in the future releases.


本文出自 空气的时光记事本,非注明转载皆为原创,转载时请注明出处及相应链接。



电子邮件地址不会被公开。 必填项已用*标注