symisc / PH7

An Embedded Implementation of PHP (C Library)
http://ph7.symisc.net
Other
494 stars 68 forks source link

Recursive function call stuck? #29

Closed morpheouss closed 5 years ago

morpheouss commented 5 years ago

I have tried to benchmark PH7 Engine and compare it to other interpreters available on the open-source market. I used the following snippet of code and it equivalent in different <?php

<?php

function fibR($n)
{
    if ($n < 2) return $n;
    return (fibR($n-2) + fibR($n-1));
}

function fibI($n)
{
    $last = 0;
    $cur = 1;
    --$n;
    while($n > 0)
    {
        --$n;
        $tmp = $cur;
        $cur = $last + $cur;
        $last = $tmp;
    }
    return $cur;
}

$N = 43;
echo 'fibR: ' . fibR($N) . "\n";
echo 'fibI: ' . fibI($N) . "\n";

First of all I tried Wren, which aims to be very fast. It executed the fibR() function in less than 2 minutes. On the other hand fibI() function executed over 1 hour. Having this tested, I checked what is the PH7 Engine performance. fibI() function executes immediately (around 0.007s). When comparing to Wren - it is extremely fast! Unfortunately I have not managed to execute fibR() successfully. I had to break script execution after 5 hours on 6th gen Core i7 CPU! Code looks good and works well in Zend PHP (fibR() executes around 2mins, fibI executes in less than 1s). What's wrong with PH7?

symisc commented 5 years ago

PH7 have a recursion limit that you can alter easily via ph7_vm_config() with the verb PH7_VM_CONFIG_RECURSION_DEPTH. This is a security measure against stack overflow since PH7 is designed to be embedded in environment where memory/stacks are scare.

Here is a copy of what the documentation say: PH7_VM_CONFIG_RECURSION_DEPTH One Argument: int nMaxDepth

This option is used to set a recursion limit to the running script. That is, a function may not call itself (recurse) more than this limit. If this limit is reached then the virtual machine abort the call and null is returned to the caller instead of the function return value. Note that passing this limit will not stop program execution, simply a null value is returned instead.

The default limit (depending on the host environment) is adequate for most situations (PH7 is smart enough to peek the appropriate value) and thus we recommend the users to not set a limit of their own unless they know what they are doing.

This option accept a single argument which is an integer between 0 and 1024 specifying the desired recursion limit.

Let us know your benchmark results!

morpheouss commented 5 years ago

I tried to change this according to the documentation, but with no go. I also think, that if this limit is reached, some error should be shown. The actual result is app hanging, whether I increase this option or not. There is no output on the console. Have you tried to execute attached script?

belliash commented 5 years ago

Full script executed for me within around 50 minutes on i7-4800mq:

fibR: 433494437
fibI: 433494437

real    50m56.633s
user    50m56.455s
sys     0m0.051s

Terribly slow, but working.

morpheouss commented 5 years ago

Thats weird. It does not seem to work for me and I dont have mobile CPU. Symisc, does it also work for you?

symisc commented 5 years ago

@morpheouss Could you share the C code you used to trigger the test? Did you messed internally with the engine source code?

morpheouss commented 5 years ago

I think, I found a problem. I have incorrectly increased the recursion depth. In fact there was still a limit set to 32. Somehow it is broken, because there is no error shown and it enters some infinite loop, I think.

Actually, whole script execution time is nearly half an hour. This is still long enough. In comparision, Wren execute fibR() in less than 2 minutes. On the other side, fibI() execute immediately, while Wren needs additional four minutes.

Why is function call so slow under PH7?

symisc commented 5 years ago

PH7 should output a warning when you reach a recursion limit. You probably did not activate warnings nor install a log consumer callback. Please read the docs carefully before throwing issues.