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]