Joining, Retirement, Testing, and Bankruptcy

First of all, hi. I recently joined the team as you could probably see from my plethora of commits to github. I first fixed up closures a bit and moved our “PHP tests in C++” to have one fewer language. I’ll be doing many exciting things for hiphop but I’ll be mostly caring about making it incredibly easy for anyone to use for any PHP code. So please bug me if it doesn’t run your favorite piece of code.

As you can see from our graphs, lots of code has been flowing lately. The first major thing, is we deleted almost all of HPHPc. It has been fully replaced by HHVM. We no longer do source level transforms to C++, instead the backend is now a just-in-time compiled virtual machine. It is faster, allows for iterative development, and opens us up for many optimizations in the future. If there is leftover dead code, please send in pull requests.

The was also a major addition in the graphs. We imported almost all of php.net’s tests. 8446 in total. We want to keep an eye on how we compare, and we’ll work hard over the next few months to move more from bad to good. We aren’t trying for parity on error messages but we are striving to be able to run all major PHP software. If you fix a compatibility issue, please move the test from bad to good.

As you can see, we have over 300 issues on github. We’ve been keeping abreast of the current issues and pull requests, but we just haven’t gotten into our backlog. Many of them are for HPHPc, and many others don’t have good repro cases. To help us not end up in broken window syndrome, we’re going to be closing all bugs older than 2 months. If yours is still an issue, please re-open it, and in order of goodness:

  1. Give detailed repro steps
  2. Write a test case in hphp/tests/quick (run it with hphp/tests/run) and send the pull request
  3. Fix it in a pull request
On that note, I’d like to chat briefly about pull requests. For hiphop to accept your code, you must fill out the CLA. It is super quick and you only have to fill it out once for all our open source projects. Also, we don’t directly merge in your code. We copy it into our internal code review tool, do a ton of perf and unit testing, and then it will appear as a commit authored by you after that. It is just much easier and safer for us to run all our internal testing on it before merging it in.

Thanks for listening. I hope this helps keep issues that are current at the front of our minds as we improve hiphop. Please keep sending issues and pull requests as much as possible.

The AdminServer

HipHop, if properly configured*, will actually startup not one, but two http servers.  The first, on port 80 by default, you’re already familiar with.  Requests are translated to filesystem paths, and PHP files are executed to generate content.  The other one, however, you might not have come across yet.

This second http server allows you, as the administrator, to take action through a web browser similar to the apachectl command.  This interface can enable/disable/display various statistics, dump jit translations and apc values, and even instruct the server to shut itself down.

* Note: HipHop builds prior to Dec 10, 2012 had the AdminServer
  enabled by default.  If you do not wish to have this running,
  you should add AdminServer.Port = 0 to your config file.

Enabling the AdminServer

Turning it on is actually just a one-line change to your config file, but since it provides administrator type functionality, it’s best to set a password as well, and probably a firewall to block unwanted traffic.

AdminServer {
  Port = 8080
  Password = superSecret
}

For firewalls, I find linux’s iptables do the trick nicely.  These two lines allow access from localhost, but drop any other packets bound for the admin server from elsewhere.

iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --destination-port 8080 -j DENY

Getting a list of commands

Just browse to the AdminServer’s port and you’ll get a text/plain dump of the available commands and their usage.  For the purpose of these examples, I’ll be using cURL from the command line.

$ curl 'http://localhost:8080'

Some commands, like /stats-web are binary switches, calling once will turn them ‘On’ (indicating you’ve enabled it), calling again will return ‘Off’.  Others, like /check-health will give you info on HipHop’s internal workings in JSON (or HTML or XML) format.

$ curl 'http://localhost:8080/check-health'
{
  "load":1.52
,  "queued":0
,  "tc-size":544
,  "tc-stubsize":358
,  "targetcache":20480
,  "units":1
}

Password authorization

If you set a password in your configuration, then all queries to the AdminServer (apart from /help or no command at all) will require that password be passed with the request, e.g.:

curl 'http://localhost:8080/check-health?auth=superSecret'

Generally speaking, however, you should probably firewall your AdminServer or disable it entirely (by setting Port=0).

On Garbage Collection

In this post, I’ll expand on a topic that I briefly mentioned in the footnote to my previous post: memory management.

Most modern programming languages, including PHP, have automatic memory management. Programmers don’t explicitly free the memory they allocate, and the language runtime does that for them. There are a variety of approaches that language runtimes can take to implement this, and they lie on a spectrum.

At one end of the spectrum is eager reference counting: every time a reference to a chunk of allocated memory is created or destroyed, the allocation’s reference count is immediately updated and it is freed if its refcount drops to zero. At the other end is tracing: periodically during the program’s execution, the runtime computes, by following objects’ references to each other, which allocations are no longer reachable and frees them. Nothing special happens immediately when references are created and destroyed. In the middle of the spectrum are a variety of hybrid approaches, such as deferred refcounting, generational collectors that trace young values and refcount old values, etc. We won’t go into those here; the academic literature on this topic is extensive.

For those unfamiliar with tracing GC, it works by starting from a root set and then tracing references from it recursively. A root set is a set of values that are assumed to be visible and reachable from PHP code: the eval stack, local variables, and global variables. Tracing references means to enumerate other values that are reachable through a given value: all keys and values of an array, and all properties of an object. We build a reachable set that starts as just the root set, and then iterate through the current reachable set, tracing each value’s references and adding those to the reachable set. When this is done, any values on the heap that aren’t in the reachable set are assumed to be garbage and are freed.

PHP’s language semantics require eager refcounting of arrays and objects — I explain why below. But the idea of moving HHVM towards the use of tracing in some contexts has been around since the project’s early days. The increased flexibility of implementation offered by tracing can translate into increased performance, so it’s an attractive idea. We’ve done some experiments to see if it’s worthwhile; read on for details.

Why does PHP require eager refcounting?

There are two ways by which PHP code can observe the runtime’s memory management scheme.

  • Object destructors; that is, __destruct() methods. These methods have to be called immediately when the object’s refcount goes to zero. A tracing GC wouldn’t detect that an object was garbage until some time after the last reference to it had disappeared, so it wouldn’t be able to call the destructor at the right time.
  • Copy-on-write arrays. PHP’s arrays have value semantics. Here’s an example:
    $a = array(20);
    $b = $a;
    $b[0] = 4500;
    echo $a[0];  // prints "20"

    In principle, the assignment $b = $a makes a copy of the array. PHP runtimes don’t actually copy the array at that point. Instead, they wait until the program tries to modify one of the “copies”. To be able to do this, the array needs an accurate refcount.

    You may think that copy-on-write arrays are just an optimization — that a PHP runtime could eagerly copy arrays and still have correct behavior. In fact, PHP code can tell that copy-on-write is happening under the hood, by taking a reference to an array element:

    $arr = array(10);
    $ref =& $arr[0];
    $copy = $arr;   // the array is "copied" at this point
    $arr[0] = 'whoops';
    echo $copy[0];  // prints "whoops"

This means that we have to keep eagerly refcounting arrays and objects to preserve correct behavior. However, there are a couple of options for limited usage of tracing GC: strings, and cycle collection. We’ve made attempts at both of these. Here are the details.

Collecting strings

It’s impossible for PHP code to observe how the runtime is implementing memory management of strings. They’re immutable, so no copy-on-write logic is observable, and there’s no PHP code that gets run when they’re destroyed. This means we have some room to experiment.

The first problem we run into is that HHVM does copy-on-write of strings. As I said before, PHP strings are immutable as far as the language is concerned, but HHVM mutates strings in-place as long as there’s no way for PHP code to tell. We determine that there’s no way for PHP code to tell by checking the refcount: if it’s exactly 1, we can mutate. To start using tracing GC on strings, we had to stop keeping accurate refcounts for them; that would defeat the purpose. So we had to start eagerly copying strings. This is obviously a performance hit, but we were hoping that not having to refcount would make up the difference.

Unfortunately, we were never really able to isolate the effect of eager-copying strings from the rest of the effects that this change caused. Memory allocation and deallocation patterns were changed, affecting data locality and potentially allocator performance. Less refcounting code was emitted, affecting code locality and memory access patterns. We couldn’t easily measure these effects separately. Worse, the overall effect on performance was negative.

The second, more serious problem that we run into here is root-finding. In my description above, I made HHVM’s root set sound simple (stack, locals, globals) but the reality is quite a bit more complicated than that.

  • Enregistered roots in JIT-compiled code. The JIT compiler loads eval stack slots and local variables — which contain references to heap-allocated data — into hardware registers. This means that, if a tracing cycle is invoked from C++ code that’s been called from JIT-compiled code, the in-memory eval stack and local variables may be stale. To get the real root set, we would have to (a) remember/compute which machine registers each stack slot/local variable was mapped to in each JIT-code frame above us; and (b) walk the C++ stack to find the values of the JIT’s registers, which were saved according to the usual x86-64 ABI as control passed from JIT-compiled code into C++. None of this is impossible; it would just be very messy in the current system.
  • Untracked C++ references. We hold pointers to heap-allocated data in member variables of C++ objects in many places. These are all roots or references that should be traced, even though they may not correspond directly to anything in PHP source.Most of these references are held within our smart-pointer classes Variant, Array, Object and String, so we tried a technique where the smart-pointer classes’ constructors added their inner raw pointers to a temporary root set for use by the GC, and their destructors removed them. In addition to causing an unacceptable performance hit, this still wasn’t enough. There are C++ roots in the form of raw pointers too, and there is no elegant solution for tracking them; we’d be slowly working our way down a long tail.

Having an approximate root set isn’t good enough. The failure mode of having a non-comprehensive root set is that we mistakenly decide some objects are unreachable, so we mistakenly free a reachable object and leave a dangling reference somewhere. That would be unacceptable.

Cycle collection

As I mentioned before, naïve eager refcounting can’t detect and free cyclical garbage (unreachable objects that hold references to each other). Here’s an example:

$a = new stdClass;
$b = new stdClass;
$a->prop = $b;
$b->prop = $a;
unset($a);
unset($b);

The two objects are unreachable — there’s no way to refer to them from PHP — but they will never be freed. They each have a refcount of 1, and the only way it could go to 0 is for the other object to be freed.

If we can detect such cycles, we can free them without introducing incorrect behavior. Going back to the two ways that PHP code can observe the runtime’s memory management:

  • “Correct” behavior (not freeing cycles) is that object destructors don’t run on cyclical garbage, so we can simply skip destructors while freeing cycles.
  • It’s impossible to observe array copy-on-write effects as shown above, because cyclical garbage is unreachable by definition.

With the cycle collector, we avoided the root-finding problem that we had with the string collector, because everything still had accurate refcounts. We could just look at references among heap allocations — not considering any references into the heap from outside it — and infer which ones were reachable and which weren’t. Here’s a sketch of the algorithm:

  1. Iterate over every array and object in the heap. For each one, trace its internal references (all values in an array, all properties in an object) and decrement the refcount of each of them.
  2. At this point, anything in the heap that still has a refcount greater than zero must have a reference to it from outside the heap, because its refcount has been decremented by one for each reference to it from within the heap. These allocations form an inferred root set.
  3. Put the inferred root set into a queue. Iterate over the queue; for each allocation in it, add each allocation it references to the queue and increment its refcount. This reconstructs their original refcounts, which we decremented in step 1.
  4. Free everything that still has zero refcount; it wasn’t reachable from the inferred root set.

So really, the algorithm isn’t specifically looking for cycles at all. All it’s doing is calculating reachability and then freeing unreachable memory. It just so happens that the only way to produce unreachable, unfreed memory with PHP code is to create a cycle.

It turned out that Facebook’s PHP codebase simply doesn’t produce much cyclical garbage. The majority of it comes from a handful of places in the code, and from a small number of very large cyclical structures. When their memory usage gets to be problematic, it’s easiest just to break the cycle by modifying the PHP code. That being the case, it’s simply not cost-effective to have a cycle collector constantly operating on production web servers. The idea is to use it as an on-demand cycle detector (so PHP developers can find cycles to break), and as a collector in long-running scripts.

The cycle collector is checked in (src/runtime/vm/backup_gc.cpp), but currently disabled due to bugs. We are aiming to fix it in the near future.

Spammers are still a thing, it seems

Been getting two or three spam comments/day.  They don’t get through because moderation is enabled.  They are an annoyance however, so I’ll be passing the annoyance on to you, dear reader, in the form of a very simple CAPTCHA.

When commenting you’ll get an additional input reading something like:

3 x four = [    ]

Performing the 2nd grade math will get your comment approved.  The spammers could probably work around this with some basic scripting, but I’m betting that they won’t bother.  If they do, there are other WP spam plugins out there.

The good news is that this plugin, like the FB plugin, also worked out-of-the-box with no special HipHop tweaks needing to be made.

Adding an opcode to HHBC

As part of our drive to improve HHVM’s performance, I started looking into the general topic of builtin functions. Currently, HHVM is calling implementations of builtin functions that were originally written for HPHPc, the static compiler. We wrote compatibility wrappers around them for HHVM, to get it up and running as quickly as possible. We have evidence that this compatibility layer is hurting HHVM’s performance, so we’re looking at ways to reduce or eliminate its impact.

I loaded some Facebook pages in an instrumented build of HHVM, to find out which builtins we call most. Here are the top 10 most-called builtins.

Percentage of calls Cumulative pct.
9.68 9.68 strlen
7.36 17.05 count
5.07 22.12 strncmp
4.57 26.69 strpos
4.09 30.79 substr
4.06 34.86 apc_store
2.98 37.84 key
2.86 40.70 is_numeric
2.50 43.21 reset
2.44 45.65 error_reporting

The top one, strlen, has very simple behavior for the common case where the argument is a string. The C++ class that we use to represent strings has a member variable that holds the string’s length — it takes a single machine instruction to retrieve it.

strlen‘s simplicity makes it a very good candidate for turning into an HHBC opcode. That way, we’ll be able to manually compile it to a handful of machine instructions. The verbal description of what the new opcode will do is simple: it pops a value off the evaluation stack, converts that value to a string if it’s not already a string, and pushes the string’s length onto the stack as an integer.

You can look at the final commit on GitHub. In the rest of this post, I’ll explain the important parts of this commit: how to create the new opcode, implement its behavior, and make it fast by implementing it in the just-in-time compiler. By the end of it, you should be able to implement count, the next most-called builtin, as an opcode by following along with what I did for strlen. Their behavior is very similar.

Continue reading

Go Faster!

If you’ve looked at my previous entry Getting WordPress Running on HHVM, you’ve already got a general idea for how to create a config.hdf file and get a web server up and running. Now you want to make sure it’s running as quickly, and efficiently as possible. But first, it’s worth understanding how HHVM runs your PHP scripts.

How HHVM serves up a page

When a request comes in, HHVM considers the hostname of the request against VirtualHost and Sandbox configuration directives to determine the right base directory to serve files from.

Once it’s identified the file, it checks a code cache stored in a SQLite database to see if it has already compiled the file. If so, and the file hasn’t changed, HHVM will execute that cached version very similarly to how APC caches PHP’s compilation process. If not, then HHVM’s analysis engine is invoked to compile the script, perform type inference, optimization, and store the result for next time.

As you might expect, there is a short warm-up period when a new server is started with a fresh, empty cache. Just like any other compilation cache, this only slows the first request of a given page. Unlike APC, however, HHVM’s cache is on disk, and therefore survives restarts.

If you’re curious about the code cache repository, you can find it in the location specified by the setting Repo.Central.Path, which defaults to ~/.hhvm.hhbc (you probably want to change that). Refer to doc/repo for more info on Repo settings.

Repo {
  Central {
    Path = /var/run/hhvm.hhbc.sq3
  }
}

Just In Time

For production servers, you’ll almost certainly want to enable JIT (Just in Time) compilation to progressively turn the bytecode contained in hhvm.hhbc.sq3 into native code. Depending on exactly what your code base is doing, this will result in performance improvements of anywhere from 50% to 300% (100% seems to be a fairly common average).

To do so, just add the following snippet to your config.hdf file. Yes, it really is a “go faster” button.

Eval {
  Jit = true
}

Bear in mind that JIT compilation is meant for long-running web servers where the extra effort of compiling to native code pays off after a few page views. For single-run uses like command line scripts, the extra work of compiling to native code will actually slow it down compared to just executing bytecode.

Note that as of Oct 18, 2012, HHVM will enable JIT by default for server, daemon, and replay modes, and disable by default for cli, debug, and translate modes.  Ref: 57f063d2 e5824bea

Pre-analyzing

As stated already, all compilation from source to bytecode is cached on disk, so restarting your web server doesn’t lose any of that information and there will be no “warm-up” period on your second and later server starts; But why have that first warm-up period at all?

When you built HipHop (with USE_HHVM=1), the build process actually made two binaries. One, in src/hhvm/hhvm, is the web server and PHP engine you know and love. The other, in src/hphp/hphp (aka hhvm-analyze), is a version of HPHPc meant for HHVM use. It’s this latter binary which will allow you to pre-generate that cache before your web server ever starts up.

If you’re using our pre-built `hiphop-php` package for Ubuntu 12.04, this HHVM version of hphp has been delivered as /usr/bin/hhvm-analyze. The name has been changed to avoid confusion with /usr/bin/hphp which will be the normal HPHPc compiler in non-HHVM mode. For the purpose of this article, I’ll refer to running hhvm-analyze. Those who compiled HipHop themselves should take this to mean src/hphp/hphp built using USE_HHVM=1

To build the cache, you’ll need to identify the files you want included and invoke the analyzer:

$ cd /var/www
$ find . -name '*.php' > /tmp/files.list
$ hhvm --hphp --target hhbc --input-list /tmp/files.list --output-file hhvm.hhbc.sq3 -k1 -l3

The newly generated bytecode cache will be in /tmp/hphp_XXXXX/hhvm.hhbc.sq3 where XXXXX is a unique label produced in the output.

In addition to avoiding the initial warm-up by doing that ahead of time, hhvm-analyze will actually work a little harder than hhvm would at runtime. It’ll find a few more optimizations, be a little more sure about type inference, and produce better bytecode.

Repo.Authoritative

Even with the cache completely primed by running the analysis step above, HHVM is still going to default to checking the subject file each time it’s requested to make sure it hasn’t changed. That means disk I/O, and that means time. For a production server where source files are confidently not-changing, that’s just a waste. APC users will be familiar with this concept from the “apc.stat=0″ option.

To ignore the state of the source files during runtime on your production servers, just tell HHVM that the code cache (or Repo in HipHop parlance) is the authoritative source of all scripts on the system. With this setting enabled, if the file is found in the cache, it’s used without checking for updates. If it’s not found in the cache, the server will offer up a 404 without even trying to grab the file.

Repo {
  Authoritative = true
}

Note that the bytecode cache is also tagged with the build ID of the hhvm which generated it. If a new hhvm binary is used, it will invalidate any previous cache and start fresh. So when running in Repo/Authoritative mode, be sure to regenerate your cache any time you replace your hhvm binary.

Getting WordPress running on HHVM

This article aims to walk through the installation process for getting WordPress running on HHVM (HipHop Virtual Machine).

Installing HHVM

For the sake of this case-study, I used Ubuntu “Precise” 12.04 as my linux distribution since I’d be able to install from a simple binary package.  Instructions are currently available for building for Ubuntu 12.04 yourself, or building for CentOS 6.3.  Other distros will probably work with some effort, and you’re encouraged to try.

Unpacking WordPress

Head to wordpress.org/download to grab the latest tar.gz version of WordPress and unpack it in a suitable location.  For the purposes of this post, I’ll be using /var/www/wp/

 $ cd /var/www
 $ wget 'http://wordpress.org/latest.tar.gz'
 $ tar -zxf latest.tar.gz
 $ mv wordpress wp

 Making a few… tweaks

WordPress doesn’t quite work “right out of the box” with HHVM because a few of HipHop’s behaviors differ from the PHP it was built for.

First, case-insensitive constants are simply not supported by HipHop.  They’re a bit silly, rarely used, and are more expensive (computationally) than they’re worth.  Fortunately, wordpress only uses this feature in one place (to maintain BC with older plugins), and we can work around it by defining the most common variants.

Open up wp-includes/wp-db.php and look for the line which defines the ‘OBJECT’ constant (line 20 in my version).  We’ll want to take off the third parameter saying to declare it case-insensitively, and add in the lower-case and drop-case versions as alternates. This should be enough to cover 99% of plugins out there.

define('OBJECT', 'OBJECT');
define('Object', 'OBJECT');
define('object', 'OBJECT');

The rest of the changes are actually just temporary work-arounds for fixes which will be in HipHop in the next week or so.  I’ll update this post as those fixes reach github.

 

As of this writing, ini_get(‘post_max_size’) will return an empty string, and ini_get(‘upload_max_filesize’) will return a size in megabytes, but without the ‘M‘ suffix, so it is interpreted as bytes.  Fixes for both of these are currently pending, so just set them both manually for now.

 wp-admin/includes/template.php in wp_max_upload_size()

#$u_bytes = wp_convert_hr_to_bytes(ini_get('upload_max_filesize'));
#$p_bytes = wp_convert_hr_to_bytes(ini_get('post_max_size'));
$u_bytes =
$p_bytes = 100 << 20; // 100MB, or whatever you'll be setting

 

Update: Both post_max_size and upload_max_filesize have been updated to return sane values and these changes are no longer required.

 

magic_quotes_runtime is (happily) not supported by HipHop.  get_magic_quotes_runtime() probably should have just quietly returned false, but for whatever reason it was implemented as throwing a “not implemented” exception.  A fix for this is currently pending, so hard-code a zero (false) for now.

wp-admin/includes/class-pclzip.php in privDisableMagicQuotes()

#$this->magic_quotes_status = @get_magic_quotes_runtime();
$this->magic_quotes_status = 0;
 wp-includes/class-phpmailer.php in EncodeFile()

#$magic_quotes = get_magic_quotes_runtime();
$magic_quotes = 0;

 

Update: get_magic_quotes_runtime() has been updated to always return 0 (false) rather than throwing an exception.  This tweak no longer needs to be made.

Create a config file

The syntax for HipHop’s web server is pretty extensive, but the following placed in /etc/hhvm.hdf should be enough to get you going.  Refer to options.compiled for other settings.

 /etc/hhvm.hdf

Server {
  Port = 80
  SourceRoot = /var/www/
}

Eval {
  Jit = true
}
Log {
  Level = Error
  UseLogFile = true
  File = /var/log/hhvm/error.log
  Access {
    * {
      File = /var/log/hhvm/access.log
      Format = %h %l %u %t \"%r\" %>s %b
    }
  }
}

VirtualHost {
  * {
    Pattern = .*
    RewriteRules {
      dirindex {
        pattern = ^/(.*)/$
        to = $1/index.php
        qsa = true
      }
    }
  }
}

StaticFile {
  FilesMatch {
    * {
      pattern = .*\.(dll|exe)
      headers {
        * = Content-Disposition: attachment
      }
    }
  }
  Extensions {
    css = text/css
    gif = image/gif
    html = text/html
    jpe = image/jpeg
    jpeg = image/jpeg
    jpg = image/jpeg
    png = image/png
    tif = image/tiff
    tiff = image/tiff
    txt = text/plain
  }
}

You may wish to add other directives and static file extensions, but this will get you started.  The RewriteRule is to accomodate the fact that HipHop does not (currently) support a DirectoryIndex style directive.  While this feature is currently in development, we can get by with a general case rewrite rule.

Start the webserver

Pretty straight-forward, just press go:

sudo /usr/bin/hhvm --mode daemon --user web --config /etc/hhvm.hdf

Assuming of course you have a user designated for your web server to run as named ‘web’.  Rename this one as needed.

WordPress setup

From here, setup follows the normal “Famous 5 Minute Installation“.  If you run into any trouble, check that you updated system files as described above, look into your error.log, or leave a comment here or on our Facebook Page.

WordPress 3.4.2 running on HHVM

Heated seats!

While you wait,
here’s my sister’s cat.

Welcome to the inaugural post on the HipHop for PHP blog.  In the coming days, weeks, months, and (dare I predict) years, we’ll be creating posts about the inner workings of HipHop, the practical implementation details of running it, and whatever else manages to fit on this site.

So sit back, add us to your RSS reader, and enjoy.