Performance Tuning with Array Caching


It’s been a while since we produced our last benchmarks, but the wait is over. Although we still aren’t focusing on performance per se, the recently implemented array caching feature in Peachpie can really help boost your app’s speed.

Introduction

As you may know, Peachpie is a compiler and runtime for PHP, wherein the entire PHP language is re-implemented in .NET. Essentially, Peachpie processes sources in the PHP language, analyzes them and spits out .NET `dll` or `exe` files. One of the great benefits of this is that the compilation phase can be quite smart and can perform various kinds of magic. Most recently, we implemented a neat optimization that comes with Peachpie for free, but can save you some serious memory and CPU usage.

In today’s tests, we benefit from the fact that our compiled PHP code runs as a .NET application, which has real application-wide static variables. This means that these variables live in-memory and won’t die after the request ends. What is a completely standard and routinely used feature in .NET can serve as an amazing tool for fast in-memory caching in PHP thanks to Peachpie – something that is otherwise often a source of significant performance bottlenecks.

Arrays

PHP has a special data type called `array`, which is used regularly in every PHP program. Internally it is a really fast and efficient hashtable. Every time an array has to be constructed in your PHP code, memory has to be allocated, the whole data structure must be initialized and the array items have to be copied and hashed into this data structure.

Most of the arrays are immutable – they are always the same, they consist of constants and literals and they are not even changed later by the program. Usually you find these arrays as some kind of application configuration, and they can be fairly huge. So why construct them over and over with every single request?

Benchmark #1

Let’s first measure how fast the code runs in Peachpie and PHP 7.1 prior to our enhancements. The code above is repeated 10,000,000 times in a `for` loop, simulating an evaluation of the code within 10M requests. Our test configuration is the same machine for both:

Core i7 2600, 16GB DDR3, Windows 10

Since the source code is executed on the same bare metal, the benchmarks are perfectly valid, as the only thing we are testing is the relationship between PHP and Peachpie. The results are as follows:

So PHP executed the code in 2.837 seconds and Peachpie took 3.95 seconds. Not bad, but it seems as though our implementation of hashtables in the managed code is not as performant as it is in PHP. PHP has a definite edge here, as there is obviously a benefit of its implementation in C compared to the safe and managed C#.

Compilation

This is where our recently implemented in-memory caching of immutable arrays comes into play. It allows us to conserve a lot of memory and CPU usage, because the arrays are not created over and over. So how is it done?

The compiler checks whether the array contains only constants, i.e. whether it will be the same in every request, every time.

Such an array is a great candidate for caching. The compiler generates an invisible static field (not a PHP static, but a real .NET static, which will survive across requests). Something equivalent to this:

[CompilerGenerated]
internal static PhpArray __arr<>1;

and when the array has to be created in PHP code, it uses the field above as a lazily initialized singleton for the array. Something like this:

__arr<>1 ?? (__arr<>1 = new PhpArray(){ item1, item2, ... })

`PhpArray` is our C# implementation of the standard PHP `array`. It is initialized lazily once and then the existing instance is reused. That simple.

Benchmark #2

While PHP is indisputably optimized since it is implemented in the C language, we could take advantage of the simplicity and power of the .NET platform. After the compiler optimization above, the results are the following:

While PHP 7 remains unchanged at 2.837 seconds, Peachpie can now execute these 10M requests in a mere 0.1299 seconds. Just imagine the cost savings you could achieve with this small optimization as a result of the lower CPU and memory usage.

And that’s it. We hope you can find use in this great performance optimization feature. If you are interested, the optimization is available since version 0.8.0-CI00442.

As always, you can help our project by spreading the word, contributing via issue submissions, bug fixes or feature implementations. Follow our progress on GitHub, Twitter and Facebook or ask a question on Gitter if you’re unsure about something.

Share this article:Share on FacebookShare on VKPin on PinterestShare on Google+Tweet about this on TwitterShare on LinkedInShare on Reddit
Posted on October 31, 2017, in category Benchmark, News, tags: , , , , , , , , , ,