Search code examples
phpjavascriptperformancev8

Why is a function re-definition faster than calling the first one?


I am reading javascript files in php and execute them using v8js.

Simplified example:

$javascriptCode = file_get_contents($filename);
$funcName = 'func'.md5($filename);
$v8js->executeString("
 function {$funcName} () {
  {$javascriptCode}
 }");
$v8js->executeString("var testVariable = {$funcName}();");

~50 calls = 200ms

To improve performance I reduced subsequent calls to only calling the function name if the function was already defined:

if ( !isset($this->cache[$filename]) ) {
 $javascriptCode = file_get_contents($filename);
 $funcName = 'func'.md5($filename);
 $v8js->executeString("
  function {$funcName} () {
   {$javascriptCode}
  }");
  $this->cache[$filename] = $funcName;
}
else {
 $funcName = $this->cache[$filename];
}
$v8js->executeString("var testVariable = {$funcName}();");

~50 calls = 900ms

For some reasons this is slower than re-running the function definition (first code part).

I have several dozen of javascript files and functions I am calling, all which ran within 200ms using the first code example. After adding a caching for already defined function names and no longer re-defining them, the runtime for the exact same code was about 900ms.

To verify that the missing re-definition is the only reason for the performance loss I changed the if:

if ( !isset($this->cache[$filename]) || true ) {

… having the function name still been saved to the array, excluding the php-array as a possible problem.

Where does the big performance loss come from or how can I debug this further?


Solution

  • I created several tests to identify everything further and finally found the reason hidden deep inside my own javascript code.

    Even that the code was identical, some if in the javascript decided to sideways and made everything slower :-)

    Here is the source code to the last test I did which has shown that without the re-definition it is faster like it was expected:

    <?php
    $runList = array(10, 100, 1000, 10000, 100000);
    $jsFunc = 'function myTestFunc () { return {foo: "bar"}; } ';
    foreach ($runList as $runs ) {
    
        $start = mstime();
        $js = new V8Js('Test');
        for ( $i = $runs; $i > 0; $i-- ) {
            $js->executeString($jsFunc, 'Test.Context');
            $js->executeString("myTestFunc();", 'Test.Context');
        }
        echo "#1: " . (mstime() - $start)." ({$runs} with re-definition)<br />";
        unset($js);
    
        $start = mstime();
        $js = new V8Js('Test');
        $js->executeString($jsFunc, 'Test.Context');
        for ( $i = $runs; $i > 0; $i-- ) {
            $js->executeString("myTestFunc();", 'Test.Context');
        }
        echo "#2: " . (mstime() - $start)." ({$runs} without re-definition)<br />";
        unset($js);
        echo "<hr />";
    }
    
    function mstime() {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }
    

    Results:

    #1: 0.000640869140625 (10 with re-definition)
    #2: 0.0003800392150878906 (10 without re-definition)
    #1: 0.001749992370605469 (100 with re-definition)
    #2: 0.0009560585021972656 (100 without re-definition)
    #1: 0.01554703712463379 (1000 with re-definition)
    #2: 0.04881501197814941 (1000 without re-definition)
    #1: 0.503957986831665 (10000 with re-definition)
    #2: 0.1761679649353027 (10000 without re-definition)
    #1: 4.813416957855225 (100000 with re-definition)
    #2: 1.93553900718689 (100000 without re-definition)