betway必威-betway必威官方网站
做最好的网站

在PHP程序中使用Rust扩展的方法

更新: 初藳刚发表还未有几个小时作者发觉到自己的 PHP 基准测验是错的。为公平起见自身已经更新了 PHP 和 Rust 的本子。你能够在 GitHub 商旅里看看変更(链接在底层)。

 C或PHP中的Rust

二零一八年11月,笔者和 Etsy 的同事有过八个有关怎么着为像PHP样的解释性语言写扩充的切磋,Ruby或Python近日的景观相应会比PHP轻松。大家聊起了写六个中标创制扩充的绊脚石是它们平日须要用C来写,不过假若您不专长C那门语言的话很难有分外信心。

自个儿的宗旨出发点正是写一些得以编写翻译的Rust代码到三个Curry面,并写为它某个C的头文件,在C中为被调用的PHP做壹个进展。纵然并不是比较粗略,可是很有意思。
Rust FFI(foreign function interface)

从那时候起小编便萌发了用Rust写一个的主张,过去的几天平昔在品味。后天早上小编好不轻易让它运维了。

本人所做的首先件专门的学业正是摆弄Rust与C连接的Rust的外界函数接口。作者曾用轻松的点子(hello_from_rust)写过叁个心闲手敏的库,伴有单纯的宣示(a pointer to a C char, otherwise known as a string),如下是输入后输出的“Hello from Rust”。  

C或PHP中的Rust

笔者的中坚出发点正是写一些得以编写翻译的Rust代码到三个库里面,并写为它有些C的头文件,在C中为被调用的PHP做二个拓宽。即便并非异常粗略,可是很有趣。

// 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).to_bytes() };
 let str_name = String::from_utf8(buf_name.to_vec()).unwrap();
 let c_name = format!("Hello from Rust, {}", str_name);
 println!("{}", c_name);
}

Rust FFI(foreign function interface)

自身所做的率先件业务正是摆弄Rust与C连接的Rust的表面函数接口。笔者曾用简短的章程(hello_from_rust)写过贰个手疾眼快的库,伴有单纯的宣示(a pointer to a C char, otherwise known as a string),如下是输入后输出的“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).to_bytes() };
    let str_name = String::from_utf8(buf_name.to_vec()).unwrap();
    let c_name   = format!("Hello from Rust, {}", str_name);
    println!("{}", c_name);
}

作者从C(或别的!)中调用的Rust库拆分它。那有二个接下去会怎么样的很好的分解。

编写翻译它会获得.a的叁个文本,libhello_from_rust.a。那是三个静态的库,包涵它自身抱有的依据关系,何况大家在编写翻译二个C程序的时候链接它,那让大家能做持续的职业。注意:在大家编写翻译后会获得如下输出:

note: link against the following native artifacts when linking against this static library
note: the order and any duplication can be significant on some platforms, and so may need to be preserved
note: library: Systemnote: library: pthread
note: library: c
note: library: m

那便是Rust编译器在大家不利用那么些依靠的时候所告诉我们须求链接什么。

自家从C(或此外!)中调用的Rust库拆分它。那有二个接下去会怎么的很好的解说。

从C中调用Rust

既是大家有了叁个库,不能不做两件事来保管它从C中可调用。首先,大家须要为它创制叁个C的头文件,hello_from_rust.h。然后在大家编写翻译的时候链接到它。

上面是头文件:

// hello_from_rust.h
#ifndef __HELLO
#define __HELLO

void hello_from_rust(const char *name);

#endif

那是叁个一定基本功的头文件,仅仅为了三个简约的函数提供具名/定义。接着大家须要写四个C程序并采用它。

// hello.c
#include <stdio.h>
#include <stdlib.h>
#include "hello_from_rust.h"

int main(int argc, char *argv[]) {
    hello_from_rust("Jared!");
}

咱俩由此运转一下代码来编写翻译它:

gcc -Wall -o hello_c hello.c -L /Users/jmcfarland/code/rust/php-hello-rust -lhello_from_rust -lSystem -lpthread -lc -lm

留意在最后的-lSystem -lpthread -lc -lm告诉gcc不要链接那么些“本地的古文物”,为了当编写翻译大家的Rust库时Rust编写翻译器能够提供出来。

经运营上边包车型地铁代码大家能够赢得叁个二进制的文书:

$ ./hello_c
Hello from Rust, Jared!

了不起!大家刚刚从C中调用了Rust库。未来大家须要领会Rust库是什么样进入三个PHP扩大的。

从 php 中调用 c

该部分花了自家有的小时来弄明白,在这里个世界上,该文书档案在 php 扩张中并非最棒的。最棒的一对是发源绑定三个本子 ext_skel 的 php 源(大许多意味“扩充骨架”)即生成大好多您须求的样子代码。为了让代码运营,小编十分的大力地读书 php 文书档案,“扩大骨骼”。

您能够透过下载来开首,和未分配的定额的 php 源,把代码写进 php 目录况且运转:

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

那将转移需求创建 php 扩大的骨干骨架。今后,移动你随处想有个别地保持您的扩充的文书夹。况兼一抬手一动脚你的

.rust 源

.rust库

.c header

跻身同多少个目录。因而,未来您应当看看像那样的叁个索引:

.
├── 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

一个目录,10个文本

您能够在 php docs 在上头看见有关那些文件很好的汇报。创建三个恢弘的文件。大家将经过编制config.m4 来开端吧。

不表明,上面正是本人的收获:

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

正如小编所精晓的那样,这一个是主导的宏命令。不过关于那几个宏命令的文书档案是很倒霉的(举个例子:google”PHP_ADD_LIBRARY_WITH_PATH”并未现身PHP团队所写的结果)。作者一时那几个PHP_ADD_LIBRARY_必威官方网站,PATH宏命令在多少人所钻探的在三个PHP扩充里链接贰个静态库的先前的线程里。在信口雌黄中其余的推荐介绍应用的宏命令是在本身运维ext_skel后产生的。

既是我们开展了陈设安装,大家要求从PHP脚本中实际地调用库。为此大家得校正自动生成的文本,hello_from_rust.c。首先大家增加hello_from_rust.h头文件到含有命令中。然后大家要校订confirm_hello_from_rust_compiled的定义方法。

#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);
}

专心:我添加了hello_from_rust(“Jared (fromPHP!!)!”);。

当今,我们得以试着组建我们的扩展:

$ phpize
$ ./configure
$ sudo make install

正是它,生成我们的元配置,运营生成的计划命令,然后安装该扩充。安装时,作者不得不亲自使用sudo,因为自己的顾客并不具备安装目录的 php 扩充。

方今,大家得以运作它啦!

$ php hello_from_rust.php
Functions available in the test extension:
confirm_hello_from_rust_compiled

Hello from Rust, Jared (from PHP!!)!
Congratulations! You have successfully modified ext/hello_from_rust/config.m4. Module hello_from_rust is now compiled into PHP.
Segmentation fault: 11

还不易,php 已步入咱们的 c 扩大,看见大家的使用措施列表並且调用。接着,c 扩张已跻身大家的 rust 库,最早打字与印刷大家的字符串。那很有意思!不过……这段错误的结果发生了怎么?

 

正如本身所波及的,这里是应用了 Rust 相关的 println! 宏,不过本身还未对它做进一层的调试。假如大家从大家的 Rust 库中去除并回到三个 char* 代替,段错误就可以灭绝。

这里是 Rust 的代码:

#![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 = unsafe { 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()
}

并变更 C 头文件:

#ifndef __HELLO
#define __HELLO

const char * hello_from_rust(const char *name);

#endif

还要改换 C 扩大文件:

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);
}

编写翻译它会获得.a的叁个文书,libhello_from_rust.a。那是多少个静态的库,富含它自个儿装有的依据关系,并且大家在编写翻译三个C程序的时候链接它,这让大家能做持续的事情。注意:在我们编写翻译后会获得如下输出:  

不行的微基准

那么为何您还要如此做?小编还真的未有在具体世界里应用过这么些。然而小编真正感觉斐波那契体系算法正是一个好的例证来证美赞臣(Meadjohnson卡塔尔国个PHP扩充怎么着很基本。经常是直截了当(在Ruby中):

def fib(at) do
    if (at == 1 || at == 0)
        return at
    else
        return fib(at - 1)   fib(at - 2)
    end
end

而且能够经过不接受递回来改善这不佳的天性:

def fib(at) do
    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 total
end

那么我们围绕它来写七个例子,一个在PHP中,三个在Rust中。看看哪位越来越快。下边是PHP版:

def fib(at) do
    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 total
end

那是它的运转结果:

$ time php php_fib.php

real    0m2.046s
user    0m1.823s
sys 0m0.207s

现行反革命大家来做Rust版。上面是库财富:

#![crate_type = "staticlib"]

fn fib(at: usize) -> usize {
    if at == 0 {
        return 0;
    } else if at == 1 {
        return 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)
}

在乎,小编编写翻译的库rustc – O rust_lib.rs使编写翻译器优化(因为大家是这里的行业内部)。这里是C扩张源(相关摘录):

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));
}

运行PHP脚本:

<?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);
}
?>

这正是它的周转结果:

$ time php rust_fib.php

real    0m0.586s
user    0m0.342s
sys 0m0.221s

您能够瞥见它比前面一个快了三倍!完美的Rust微基准!

note: link against the following native artifacts when linking against this static library
note: the order and any duplication can be significant on some platforms, and so may need to be preserved
note: library: Systemnote: library: pthread
note: library: c
note: library: m

总结

此地大约一直不吸取什么结论。作者不鲜明在Rust上写三个PHP的恢宏是八个好的想法,不过花费一些时日去研讨Rust,PHP和C,那是二个很好的措施。

假诺您期待查看全体代码也许查看校正记录,能够访谈GitHub Repo。

那正是Rust编写翻译器在大家不接收这些依赖的时候所告诉我们要求链接什么。

从C中调用Rust

既然如此我们有了壹个库,不能不做两件事来作保它从C中可调用。首先,大家必要为它成立叁个C的头文件,hello_from_rust.h。然后在我们编写翻译的时候链接到它。

下边是头文件:  

// hello_from_rust.h
#ifndef __HELLO
#define __HELLO

void hello_from_rust(const char *name);

#endif

那是二个一定底蕴的头文件,仅仅为了二个简易的函数提供签字/定义。接着我们必要写七个C程序并应用它。  

// hello.c
#include 
#include 
#include "hello_from_rust.h"

int main(int argc, char *argv[]) {
 hello_from_rust("Jared!");
}

大家经过运维一下代码来编写翻译它:  

gcc -Wall -o hello_c hello.c -L /Users/jmcfarland/code/rust/php-hello-rust -lhello_from_rust -lSystem -lpthread -lc -lm

当心在终极的-lSystem -lpthread -lc -lm告诉gcc不要链接那个“本地的古文物”,为了当编写翻译大家的Rust库时Rust编译器能够提供出来。

经运维下边包车型大巴代码大家能够得到二个二进制的公文:  

$ ./hello_c
Hello from Rust, Jared!

大好!大家刚刚从C中调用了Rust库。以后大家须求明白Rust库是什么样踏向叁个PHP扩大的。

从 php 中调用 c

该部分花了自己有些岁月来弄精通,在此个世界上,该文书档案在 php 扩大中并非最棒的。最棒的某些是缘于绑定三个剧本 ext_skel 的 php 源(大多数代表“扩大骨架”)即生成大大多您必要的榜样代码。  你能够经过下载来开首,和未分配的定额的 php 源,把代码写进 php 目录并且运维:

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

  那将扭转要求创设 php 扩大的为主骨架。今后,移动你随处想一些地维持你的扩充的文书夹。何况一举手一投足你的

  •     .rust 源
  •     .rust库
  •     .c header

进去同多少个索引。因而,将来您应该看看像那样的一个索引:

 .
├── 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

几个索引,10个公文

您能够在 php docs 在上头看见有关这么些文件很好的汇报。创建八个扩张的文本。大家将透过编制config.m4 来初阶吧。

不表明,上边正是本身的硕果:  

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

正如笔者所精晓的那样,那个是主导的宏命令。可是至于那个宏命令的文书档案是比较倒霉的(比方:google"PHP_ADD_LIBRARY_WITH_PATH"并未现身PHP团队所写的结果)。笔者偶然那些PHP_ADD_LIBRARY_PATH宏命令在多少人所斟酌的在七个PHP扩充里链接一个静态库的先前的线程里。在批评中其余的推荐应用的宏命令是在本人运转ext_skel后发生的。

本文由betway必威发布于网页设计,转载请注明出处:在PHP程序中使用Rust扩展的方法

Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。