Skip to content

How to use Rust to help PHP

llgoer edited this page Aug 4, 2019 · 1 revision

Last week, an article about "PHP and Zend co-founder Zeev Suraski announced the departure from Zend" exploded in Chinese programmers. The departure of Zend's founder in the article caused an uproar, and various unfriendly voices were also passed out. Although the core PHP developer from China, “Xinchen Hui” said something for PHP, but many programmers are not very optimistic about it.

As my introductory language in the Web field, my feelings for PHP are as good as first love. But along with the various ridicules and ironies of PHP in recent years, plus the new language Go, Rust, Javascript and other strong rise, it seems that "I am a PHP programmer" is not good enough. From the Web, brilliant to the Web, limited by the Web, I have to say, this makes the scripting language PHP into trouble, because the Web is no longer the PC era of rendering web pages, although there are currently excellent asynchronous solutions like Swoole, but it's still so hard to get enough interest in PHP.

As an emerging language that has become popular in recent years, Rust has entered the field of vision with security, efficiency and reliability. Over the weekend, I picked up PHP and wanted to see if I could make a little spark between PHP and Rust. Of course, just some experiments done for personal interest, this does not bring any changes to the two excellent languages. But in my opinion, let everyone know more about PHP and Rust at least.

FFI in PHP 7.4

At first, the new feature FFI in PHP 7.4 made me a lot of interest, because many languages ​​already have such features, such as: Python, NodeJS, Ruby, etc., the first introduction of PHP, let me think about that the PHP team is moving towards Work hard in this regard. Firstly, I wrote a Fibonacci sequence by using Rust. Here I implemented a simple calculation function, which I exported as a dynamic link library for PHP FFI calls.

#[no_mangle]
pub extern fn fib(n: i32) -> i32 {
  return match n {
    1 | 2 => 1,
    n => fib(n - 1) + fib(n - 2)
  }
}

After writing, I tried to make a call in PHP's FFI. In order to compare the performance, I also wrote a PHP function:

// PHP's fib function
function fib($n) {
    if ($n === 1 || $n === 2) {
		return 1;
	} else {
		return fib($n-1) + fib($n-2);
	}	 
}

Next, I call them in PHP, in order to be able to see the performance difference, I will call 1000000 times:

// release mode
$ffiRelease = FFI::cdef(
    "int32_t fib(int32_t n);",
    "r2p-fib/target/release/libr2pfib.$libExtension");

$time_start = microtime(true);
for ($i=0; $i < 10000000; $i++) { 
	$v = $ffiRelease->fib(12);
}

echo '[Rust]Release execution time:' . (microtime(true) - $time_start).PHP_EOL;

From the results of test, the results of Rust's FFI are surprising. PHP's calculation took more than 30 seconds, while Rust only took 6 seconds. When I was ecstatic about this, I tried the PHP FFI call to generate a string, which is similar to this method in PHP:

function text_generate($num) {
	$result = "💣";
	$result .= str_repeat("na ",$num);
	$result .= "Batman! 💣";
	return $result;
}

As a result, Rust has not achieved the expected performance due to the loss of string conversion in PHP's FFI.

PHP extension calls Rust dynamic library

Because the first operation, let me know that the performance loss of FFI in multiple calls was very big, then I want to implement the Rust dynamic library in the PHP extension. At the same time, I wrote a C Fab function for comparison. I created a PHP extension called rust and completed the call on the Rust function we wrote above.

ZEND_BEGIN_ARG_INFO(arginfo_rust_fib, 0)
	ZEND_ARG_INFO(0, number)
ZEND_END_ARG_INFO()

/* {{{ int rust_fib( [ int $var ] )
 */
PHP_FUNCTION(rust_fib)
{
	zend_long number = 0;
	zend_long result = 0;
	ZEND_PARSE_PARAMETERS_START(0, 1)
		Z_PARAM_OPTIONAL
		Z_PARAM_LONG(number)
	ZEND_PARSE_PARAMETERS_END();

	if (number == 0) {
		RETURN_LONG(result);
	} else {
		result = fib(number);
		RETURN_LONG(result);
	}
}
/* }}}*/

The result also surprised me, it really improved performance by about 20% without FFI. But obviously, its implementation is more complicated.

Writing PHP extensions with Rust

Export the PHP Zend API structure and write the PHP module directly in Rust. Although I don't have enough energy to do such an attempt, but I think this is still a viable method. I want to complete such an attempt in the future.

Summary

Although the main factor affecting the better development of PHP is not performance, and this article only tests PHP from the performance improvement, but I think that as a Web language with such a large development group, it is time to learn to the emerging languages ​​such as Go, Rust, Node to build a new ecosystem that is not limited to the Web domain. Some new attempts should be made to make PHP no longer be the "world's first language."

For complete test code and results, please refer to: https://github.com/llgoer/php-ffi-rust/