Computing Pi with Peachpie

We have been working hard to produce something tangible and/or executable to demonstrate some of the usecases, features, and benefits of Peachpie. In honor of Pi Day last week, it seemed like a good idea to produce the first demo of the PHP compiler to .NET by allowing it to compute Pi.

Peachpie Leibniz Demo

First and foremost, we would like to emphasize that Peachpie is still in its absolute infancy and the development is currently in the early stages. However, we felt that in order to get the message across, we invariably needed to show something tangible. Therefore, we taught Peachpie to compute simple algebraic operations, and we demonstrated it via the calculation of Pi.

Test Configuration

  • Lenovo Thinkpad Yoga 260
  • Core i7 6500U
  • 8GB DDR4 RAM

Test Source Code

The method used was the Leibniz formula for p. Please refer to this article to see how this formula works. Please be aware that this method is strictly designed for demonstration purposes and is by no means suitable for a precise computation of Pi.

Test Results

The test results indicate that Peachpie outperforms both tested versions of PHP as well as Phalanger by a significant distance. The speed of computation (approximately 11.5 seconds) of Phalanger was comparable to that of PHP 7.0.4 on a x86 processor, while Peachpie calculated the same 100,000,000-step accuracy in just over 2 seconds, as shown by the following table:

This performance discrepancy is thanks to Peachpie’s type analysis and C#-like emitted code, subsequent .NET JIT optimizations, and the compilation to actual CPU machine code.

Interestingly, both Peachpie and Phalanger were able to calculate a more precise result than the pure PHP versions. Finally, they both run on x32, x64, and any other CPU supported by .NET. It is important to stress that Peachpie is still a work in progress and numerous optimizations can still be made, which will presumably increase the speed even further.

Other Considerations

Aside from the aforementioned performance benefits, there are additional advantages associated with the use of a compiler within Microsoft Visual Studio. Firstly, this popular IDE produces some incredibly useful and user-friendly statistics, as the following examples indicate:

Peachpie:

Phalanger:

Furthermore, if we compare Phalanger with Peachpie, we can clearly note how Phalanger stresses the garbage collector, while Peachpie’s improved architecture represents an insignificant burden for the GC, keeping the memory consumption at a minimum.

Intermediate Code Comparison

For completion sake, we have also included the comparison of the PHP 5.6 OpCode, the MSIL output produced by Peachpie, as well as the decompiled C# from the Peachpie MSIL, and finally the Phalanger-produced MSIL decompiled into C#:

PHP 5.6 OpCode:

We obtained this code using the Vulcan Logic Disassembler:

function name:  leibniz_pi
number of ops:  39
compiled vars:  !0 = $pi, !1 = $top, !2 = $bot, !3 = $minus, !4 = $i
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   5     0  >   EXT_NOP
   7     1      EXT_STMT
         2      ASSIGN                                                   !0, 4
         3      EXT_STMT
         4      ASSIGN                                                   !1, 4
         5      EXT_STMT
         6      ASSIGN                                                   !2, 3
         7      EXT_STMT
         8      ASSIGN                                                   !3, true
   9     9      EXT_STMT
        10      ASSIGN                                                   !4, 0
        11  >   IS_SMALLER                                       ~5      !4, 100000000
        12      EXT_STMT
        13    > JMPZNZ                                       11          ~5, ->35
        14  >   POST_INC                                         ~6      !4
        15      FREE                                                     ~6
        16    > JMP                                                      ->11
  11    17  >   EXT_STMT
        18    > JMPZ                                                     !3, ->23
        19  >   DIV                                              ~7      !1, !2
        20      SUB                                              ~8      0, ~7
        21      QM_ASSIGN                                        ~9      ~8
        22    > JMP                                                      ->25
        23  >   DIV                                              ~10     !1, !2
        24      QM_ASSIGN                                        ~9      ~10
        25  >   ASSIGN_ADD                                    0          !0, ~9
  12    26      EXT_STMT
        27    > JMPZ                                                     !3, ->30
        28  >   QM_ASSIGN                                        ~12     false
        29    > JMP                                                      ->31
        30  >   QM_ASSIGN                                        ~12     true
        31  >   ASSIGN                                                   !3, ~12
  13    32      EXT_STMT
        33      ASSIGN_ADD                                    0          !2, 2
  14    34    > JMP                                                      ->14
  16    35  >   EXT_STMT
        36    > RETURN                                                   !0
  17    37*     EXT_STMT
        38*   > RETURN                                                   null

branch: #  0; line:     5-    9; sop:     0; eop:    10; out1:  11
branch: # 11; line:     9-    9; sop:    11; eop:    13; out1:  35; out2:  17
branch: # 14; line:     9-    9; sop:    14; eop:    16; out1:  11
branch: # 17; line:    11-   11; sop:    17; eop:    18; out1:  19; out2:  23
branch: # 19; line:    11-   11; sop:    19; eop:    22; out1:  25
branch: # 23; line:    11-   11; sop:    23; eop:    24; out1:  25
branch: # 25; line:    11-   12; sop:    25; eop:    27; out1:  28; out2:  30
branch: # 28; line:    12-   12; sop:    28; eop:    29; out1:  31
branch: # 30; line:    12-   12; sop:    30; eop:    30; out1:  31
branch: # 31; line:    12-   14; sop:    31; eop:    34; out1:  14
branch: # 35; line:    16-   17; sop:    35; eop:    38
path #1: 0, 11, 35,
path #2: 0, 11, 17, 19, 25, 28, 31, 14, 11, 35,
path #3: 0, 11, 17, 19, 25, 30, 31, 14, 11, 35,
path #4: 0, 11, 17, 23, 25, 28, 31, 14, 11, 35,
path #5: 0, 11, 17, 23, 25, 30, 31, 14, 11, 35,
End of function leibniz_pi.

Peachpie-produced MSIL:

 

 

.method public static 
    float64 leibniz_pi (
        class [pchpcor]Pchp.Core.Context '<ctx>'
    ) cil managed 
{
    // Method begins at RVA 0x2054
    // Code size 102 (0x66)
    .maxstack 3
    .locals init (
        [0] float64,
        [1] float64,
        [2] float64,
        [3] bool,
        [4] int64,
        [5] valuetype [pchpcor]Pchp.Core.PhpNumber
    )

    IL_0000: ldc.r8 4
    IL_0009: stloc.0
    IL_000a: ldc.r8 4
    IL_0013: stloc.1
    IL_0014: ldc.r8 3
    IL_001d: stloc.2
    IL_001e: ldc.i4.1
    IL_001f: stloc.3
    IL_0020: ldc.i4.0
    IL_0021: conv.i8
    IL_0022: stloc.s 4

    IL_0024: br.s IL_0058
    // loop start (head: IL_0058)
        IL_0026: ldloc.0
        IL_0027: ldloc.3
        IL_0028: brtrue.s IL_002f

        IL_002a: ldloc.1
        IL_002b: ldloc.2
        IL_002c: div
        IL_002d: br.s IL_0033

        IL_002f: ldloc.1
        IL_0030: ldloc.2
        IL_0031: div
        IL_0032: neg

        IL_0033: add
        IL_0034: stloc.0
        IL_0035: ldloc.3
        IL_0036: brtrue.s IL_003b

        IL_0038: ldc.i4.1
        IL_0039: br.s IL_003c

        IL_003b: ldc.i4.0

        IL_003c: stloc.3
        IL_003d: ldloc.2
        IL_003e: ldc.i4.2
        IL_003f: conv.r8
        IL_0040: add
        IL_0041: stloc.2
        IL_0042: ldloc.s 4
        IL_0044: ldc.i4.1
        IL_0045: conv.i8
        IL_0046: call valuetype [pchpcor]Pchp.Core.PhpNumber [pchpcor]Pchp.Core.PhpNumber::Add(int64, int64)
        IL_004b: stloc.s 5
        IL_004d: ldloca.s 5
        IL_004f: call instance int64 [pchpcor]Pchp.Core.PhpNumber::ToLong()
        IL_0054: stloc.s 4
        IL_0056: br.s IL_0024

        IL_0058: ldloc.s 4
        IL_005a: ldc.i4 100000000
        IL_005f: conv.i8
        IL_0060: clt
        IL_0062: brtrue.s IL_0026
    // end loop

    IL_0064: ldloc.0
    IL_0065: ret
} // end of method PhpNumberTest::leibniz_pi

Decompiled C# from Peachpie MSIL:

[csharp]
public static double leibniz_pi(Context <ctx>)
{
????double num = 4.0;
????double num2 = 4.0;
????double num3 = 3.0;
????bool flag = true;
????for (long num4 = 0L; num4 < 100000000L; num4 = PhpNumber.Add(num4, 1L).ToLong())
????{
????????num += (flag ? (-(num2 / num3)) : (num2 / num3));
????????flag = !flag;
????????num3 += (double)2;
????}
????return num;
}
[/csharp]

 

Phalanger-produced MSIL decompiled to C#:

[csharp]
public static object leibniz_pi(ScriptContext <context>)
{
????object obj = 4.0;
????object obj2 = 4.0;
????object obj3 = 3.0;
????object obj4 = true;
????object obj5 = 0;
????while (PhpComparer.CompareOp(obj5, 100000000, false) < 0)
????{
????????object obj6 = (!Convert.ObjectToBoolean(obj4)) ? Operators.Divide(obj2, obj3) : Operators.Minus(Operators.Divide(obj2, obj3));
????????obj = Operators.Add(obj, obj6);
????????obj4 = ((!Convert.ObjectToBoolean(obj4)) ? true : false);
????????int num = 2;
????????obj3 = Operators.Add(obj3, num);
????????obj5 = Convert.ObjectToInteger(Operators.Add(obj5, 1));
????}
????return obj;
}
[/csharp]

Posted on March 22, 2016, in category Benchmark, tags: , , , , , , , , ,