Sunday, July 22, 2012

Using V8 Javascript engine as a PHP extension (update: write PHP session)

"We Are Borg PHP. We Will Assimilate You. Resistance Is Futile!"
Just got to something described as: This extension embeds the V8 Javascript Engine into PHP.
It is called v8js and the documentation is already available on php.net, examples and the sources are here. V8 is known to work well in browsers and webservers like node.js, but does it work inside PHP? YES!

Here is the installation on Ubuntu 12.04:
sudo apt-get install php5-dev php-pear libv8-dev build-essential
sudo pecl install v8js
sudo echo extension=v8js.so >>/etc/php5/cli/php.ini
sudo echo extension=v8js.so >>/etc/php5/apache2/php.ini
php -m | grep v8

Let's run a small test script:
<?php
$start = microtime(true);
$array = array();
for ($i=0; $i<50000; $i++) $array[] = $i*2;

$array2 = array();
for ($i=20000; $i<21000; $i++) $array2[] = $i*2;

foreach ($array as $val) {
  foreach ($array2 as $val2) if ($val == $val2) {}
}
echo (microtime(true)-$start)."\n"; // 8.60s


$start = microtime(true);
$v8 = new V8Js();
$JS = <<< EOT
var array = [];
for (i=0; i<50000; i++) array.push(i*2);

var array2 = [];
for (i=20000; i<21000; i++) array2.push(i*2);

for (key=0; key<array.length; key++) {
  for (key2=0; key2<array2.length; key2++) if (array[key] == array2[key2]) {}
}
print('done.');
EOT;
$v8->executeString($JS, 'basic.js');
echo ' '.(microtime(true)-$start)."\n"; // 3.49s

Using Javascript – inside PHP – can make some operations at least 2 times faster.
Running the same Javascript code with node.js gives:
time node /tmp/test.js
real    0m0.729s
Since v8js is currently in beta status, we can expect even more performance coming with the next releases.

Here is an example using databases, sessions and request handling from PHP inside V8:
// v8js currently crashes with mysqli_result, so wrap it
class DB extends MySQLi {
  public function query($sql) {
    return parent::query($sql)->fetch_all(MYSQLI_ASSOC);
  }
}

// v8js currently has no support for references or magic __set(), so wrap it
class Session {
  public function set($key, $value) {
    $_SESSION[$key] = $value;
  }
  public function __get($key) {
    if (isset($_SESSION[$key])) return $_SESSION[$key];
    return null;
  }
  public function toJSON() {
    return $_SESSION;
  }
}

session_start();
$js = new V8Js();
$js->db = new DB('localhost', 'root', '', 'mydb');
$js->session = new Session();
$js->request = $_REQUEST;

$js->executeString(<<<EOT
  print( PHP.db.query('select * from some_table limit 1')[0].some_column );
  print( PHP.request.hello );

  print( JSON.stringify( PHP.session ) ); // calls toJSON
  print( PHP.session.invalid ); // null
  print( PHP.session.hello );
  PHP.session.set('hello', 'world');
EOT
);

More coming soon: Javascript form validation on server side, Javascript template rendering on server side, APC-Cache

10 comments:

  1. At the point where you have to install system-level packages to embed another language into PHP, you might just want to use the other language, like js/node.

    For existing apps, bottlenecks are rarely in algorithmic code anyway.

    And one last thing, PHP's foreach loop is much slower (50% slower) than it's for loop (surprise). For that reason, that benchmark is kind of unfair.

    http://stackoverflow.com/questions/3430194/performance-of-for-vs-foreach-in-php

    ReplyDelete
    Replies
    1. Kenny meant to say foreach is faster than for :-P

      Delete
  2. Compiling from source is currently needed since the extension is not yet available as a binary package. But I'm sure there will be binary packages for most common distributions when the extension gets the stable status.

    I also evaluated For vs. Foreach in the "nested" form: http://we-love-php.blogspot.de/2012/06/php-for-or-foreach.html

    ReplyDelete
  3. Its cool but really pointless, and your not really comparing the speed difference when your comparing a for loop with a foreach loop they do completely different things, that's like comparing the time it takes to cook something using a oven and a fridge.
    foreach inequivalent in JavaScript is for(var i in array), and if you were really worried about speed you would use a normal for loop anyway.

    Second there are setters and getters inside every object inside JavaScript, by default they are defined, you don't have to worry about it just pass the session in as it is.

    ReplyDelete
  4. Nested For in Javascript is 5 times faster than nested foreach.
    Nested Foreach in PHP is 5 times faster than nested For.
    So I used the fastest loops in each language for the NLjoin, see: http://we-love-php.blogspot.de/2012/06/php-for-or-foreach.html

    For the session, I would prefer: $js->session = &$_SESSION; but that doesn't work. Using PHP.session.hello = 'world'; does not write the changes back to $js->session;

    ReplyDelete
  5. The spidermonkey extension seems more mature.
    Anyone tested the two and has some feedback?

    ReplyDelete
    Replies
    1. Yes, in our testing V8 is more stable, take from that hat you will.

      Delete
  6. But you should have written about the Output in first script. What was the time differences?

    ReplyDelete
  7. There's one thing the author is leaving out. The Pecl spidermonkey project makes it easier (said also: efficient) to move data between the 2 languages. I see C functions like "_jsval_to_zval" and "_zval_to_jsval." I also see PHP functions that reference these C functions. No such functions exist for the v8js module. This will make a difference if you're moving data between contexts.

    Serialization is going to be slow, and that's the only way with the PHP <-> v8js module for now. Correct me if I'm missing something.

    ReplyDelete
  8. Spidermonkey currently has no documentation on Pecl, so I only tested v8. v8 is well documented: http://php.net/manual/en/book.v8js.php

    ReplyDelete