PHP extension rust

Source: Internet
Author: User
Tags benchmark configuration settings ffi
This article focuses on the use of rust Extensions in PHP programs, and Rust is a recent emerging compilation language with outstanding performance. We hope to help you.

C or Rust in PHP

My basic starting point is to write some of the rust code can be compiled into a library, and write it some C header file, in C for the called PHP to do an extension. It's not very simple, but it's fun.
Rust FFI (Foreign function interface)

The first thing I did was to fiddle with the external function interfaces of rust and the C-linked rust. I used a simple method (Hello_from_rust) to write a flexible library with a single declaration (a pointer to a C char, otherwise known as a string), followed by the input output "Hello from rust".

hello_from_rust.rs#! [Crate_type = "Staticlib"] #! [Feature (LIBC)]extern crate Libc;use Std::ffi::cstr; #[no_mangle]pub extern "C" FN hello_from_rust (name: *const libc::c_char) {Let Buf_name = unsafe {cstr::from_ptr (name). _bytes ()}; Let Str_name = String::from_utf8 (Buf_name.to_vec ()). Unwrap (); Let C_name = format! ("Hello from Rust, {}", str_name); println! ("{}", c_name);}

I'm from C (or other!) ) to split it in the Rust library called. Here's a good explanation for what happens next.

Compiling it will get a file of. A, libhello_from_rust.a. This is a static library that contains all of its own dependencies, and we link it when compiling a C program, which allows us to do the following things. Note: After we compile we will get the following output:

Note:link against the following native artifacts when linking against this static librarynote:the order and any Duplicat Ion can is significant on some platforms, and so could need to be preservednote:library:Systemnote:library:pthreadnote: Library:cnote:library:m

This is where the rust compiler tells us what to link when we don't use this dependency.

Calling Rust from C

Now that we have a library, we have to do two things to make sure it is callable from C. First, we need to create a C header file for it, Hello_from_rust.h. Then link to it when we compile.

Here is the header file:

Hello_from_rust.h#ifndef __hello#define __hello void hello_from_rust (const char *name); #endif

This is a fairly basic header file that provides signature/definition only for a simple function. Then we need to write a C program and use it.

Hello.c#include <stdio.h> #include <stdlib.h> #include "hello_from_rust.h" int main (int argc, char *argv[] {Hello_from_rust ("jared!");}

We compile it by running the code:

Gcc-wall-o Hello_c hello.c-l/USERS/JMCFARLAND/CODE/RUST/PHP-HELLO-RUST-LHELLO_FROM_RUST-LSYSTEM-LPTHREAD-LC-LM

Note at the end of the-LSYSTEM-LPTHREAD-LC-LM tells GCC not to link those "local antiques", in order to compile our rust library when the rust compiler can provide it.

By running the following code we can get a binary file:

$./hello_chello from Rust, jared!

Pretty! We just called the Rust Library from C. Now we need to understand how the rust library goes into a php extension.


Calling C from PHP

It took me some time to figure out that in this world, this document is not the best in PHP extensions. The best part is from a PHP source that binds a script Ext_skel (most of which represents the "extended skeleton") that generates most of the boilerplate code you need. You can start with the download, and the non-quota PHP source, write the code into the PHP directory and run:

$ cd ext/$./ext_skel--extname=hello_from_rust

This will generate the basic skeleton needed to create the PHP extension. Now, move your folders everywhere you want to keep your extensions in place. and move your

    • . Rust Source

    • . Rust Library

    • . C Header

into the same directory. So now you should look at a directory like this:

. ├──credits├──experimental├──config.m4├──config.w32├──hello_from_rust.c├──hello_from_rust.h├──hello_from_ Rust.php├──hello_from_rust.rs├──libhello_from_rust.a├──php_hello_from_rust.h└──tests└──001.phpt

One directory, 11 files

You can see a good description of these files in PHP docs above. Create an extended file. We're going to start by editing config.m4.

Not explained, here is my result:

Php_arg_with (Hello_from_rust, for Hello_from_rust support,[--with-hello_from_rust    Include hello_from_rust Support]) if test "$PHP _hello_from_rust"! = "no"; Then Php_subst (hello_from_rust_shared_libadd)  Php_add_library_with_path (Hello_from_rust,., HELLO_FROM_RUST_ Shared_libadd)  php_new_extension (Hello_from_rust, hello_from_rust.c, $ext _shared) fi

As I understand it, these are basic macro commands. But the documentation for these macro commands is pretty bad (for example: Google "Php_add_library_with_path" doesn't show the results that the PHP team wrote). I stumbled upon this Php_add_library_path macro command in a previous thread that some people are talking about linking a static library in a PHP extension. Other recommended macro commands in the comments are generated after I run the Ext_skel.

Now that we have the configuration settings, we need to actually call the library from the PHP script. For this we have to modify the automatically generated files, hello_from_rust.c. First we add the Hello_from_rust.h header file to the containing command. Then we need to modify the Confirm_hello_from_rust_compiled definition method.

#include "hello_from_rust.h"//a bunch of comments and code removed ... Php_function (confirm_hello_from_rust_compiled) {char *arg = NULL; int Arg_len, Len; char *strg;  if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "s", &arg, &arg_len) = = FAILURE) {  return;}  Hello_from_rust ("Jared (from php!!)!");  Len = spprintf (&STRG, 0, "congratulations! You have successfully modified EXT/%.78S/CONFIG.M4. Module%.78s is now compiled into PHP. "," Hello_from_rust ", Arg); Return_stringl (STRG, Len, 0);}

Note: I added Hello_from_rust ("Jared (fromphp!!)!");.


Now, we can try to build our extensions:

$ phpize$./configure$ sudo make install

That's it, build our meta configuration, run the generated configuration command, and then install the extension. When installing, I have to use sudo myself because my users do not own the PHP extension of the installation directory.

Now, we can run it!

$ PHP hello_from_rust.phpfunctions available in the test Extension:confirm_hello_from_rust_compiledhello from Rust, Jared (from php!!)! congratulations! You have successfully modified EXT/HELLO_FROM_RUST/CONFIG.M4. Module Hello_from_rust is now compiled to PHP. Segmentation fault:11

Also good, PHP has entered our C extension, see our list of application methods and call. Then, the C extension has entered our rust library and begins printing our string. That's funny! But...... What happened to the wrong ending?

As I mentioned, this is the use of Rust-related println! Macro, but I didn't do any further debugging on it. If we remove from our Rust library and return a char* substitution, the segment error disappears.

Here's the Rust code:

#! [Crate_type = "Staticlib"] #! [Feature (LIBC)]extern crate libc;use std::ffi::{cstr, CString}; #[no_mangle]pub extern "C" FN hello_from_rust (name: *const libc::c_char), *const Libc::c_char {let    buf_name = un Safe {cstr::from_ptr (name). To_bytes ()};    Let Str_name = String::from_utf8 (Buf_name.to_vec ()). Unwrap ();    Let c_name   = format! (" Hello from Rust, {} ", str_name);     Cstring::new (C_name). Unwrap (). As_ptr ()}

and change the C header file:

#ifndef __hello#define __hello Const char * hello_from_rust (const char *name); #endif

Also change the C extension file:

Php_function (confirm_hello_from_rust_compiled) {char *arg = NULL; int Arg_len, Len; char *strg;  if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "s", &arg, &arg_len) = = FAILURE) {  return;}  Char *str; str = Hello_from_rust ("Jared (from php!!)!"); printf ("%s\n", str);  Len = spprintf (&STRG, 0, "congratulations! You have successfully modified EXT/%.78S/CONFIG.M4. Module%.78s is now compiled into PHP. "," Hello_from_rust ", Arg); Return_stringl (STRG, Len, 0);}

Useless micro-benchmark

So why are you doing this? I really didn't use this in the real world. But I really think that the Fibonacci sequence algorithm is a good example of how basic a php extension is. is usually straightforward (in Ruby):

def fib (at) does if (at = = 1 | | at = = 0)  return at Else  return fib (at-1) + fib (at-2) endend

And you can improve this poor performance by not using recursive returns:

def fib (at) does if (at = = 1 |-at = = 0)  return at elsif (val = @cache [at]). Present?  Return Val  End Total  = 1 parent = 1 GP  = 1  (1..at).  each do |i| Total = parent + GP  gp  = parent  parent = Total End  return totalend

So we're going to write two examples around it, one in PHP and one in rust. See which one is faster. Here is the PHP version:

def fib (at) does if (at = = 1 |-at = = 0)  return at elsif (val = @cache [at]). Present?  Return Val  End Total  = 1 parent = 1 GP  = 1  (1..at).  each do |i| Total = parent + GP  gp  = parent  parent = Total End  return totalend

This is the result of its operation:

$ time PHP php_fib.php Real 0m2.046suser 0m1.823ssys 0m0.207s

Now let's do the rust version. Here is the Library resource:

#! [Crate_type = "Staticlib"] fn fib (at:usize), usize {    if at = = 0 {        return 0;    } else if at = = 1 {        Retu RN 1;    }     Let Mut total  = 1;    Let mut parent = 1;    Let mut gp     = 0;    For _ in 1.. At {        Total  = parent + GP;        GP     = parent;        parent = total;    }     return total;} #[no_mangle]pub extern "C" FN RUST_FIB (at:usize), usize {    fib (at)}

Note that I compiled the library rustc-o rust_lib.rs to make the compiler optimized (because we are the standard here). Here is the C extension source (related excerpt):

Php_function (confirm_rust_fib_compiled) {long number;  if (Zend_parse_parameters (Zend_num_args () tsrmls_cc, "L", &number) = = FAILURE) {  return;}  Return_long (RUST_FIB (number));}

To run the PHP script:

<?PHP$BR = (Php_sapi_name () = = "CLI")? "": "<br>"; if (!extension_loaded (' Rust_fib ')) {DL (' Rust_fib. '). Php_shlib_suffix);} for ($i = 0; $i < 100000; $i + +) {confirm_rust_fib_compiled (92);}? >

This is the result of its operation:

$ time PHP rust_fib.php Real 0m0.586suser 0m0.342ssys 0m0.221s

You can see it three times times faster than the former! Perfect Rust micro-benchmark!

Related recommendations:

Error resolution for dynamically adding PHP extensions using Phpize in MacOS

Linux under PHP extension CURL compilation installation

PHP Extension Library

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.