CまたはPHPでのRust
私の基本的な開始点は、コンパイル可能な Rust コードをライブラリに書き込み、そのコード用の C ヘッダー ファイルを書き込み、呼び出された PHP 用に C で拡張機能を作成することです。簡単ではありませんが、楽しいです。
Rust FFI (外部関数インターフェース)
リーリー
コンパイルすると、.a、libhello_from_rust.a というファイルが得られます。これは独自の依存関係をすべて含む静的ライブラリであり、C プログラムをコンパイルするときにリンクして、後続の処理を実行できるようにします。注: コンパイル後、次の出力が得られます:
リーリー
CからRustを呼び出す
ライブラリを作成したので、それを C から呼び出し可能にするために 2 つのことを行う必要があります。まず、C ヘッダー ファイル hello_from_rust.h を作成する必要があります。次に、コンパイル時にリンクします。以下はヘッダーファイルです:
リーリー
リーリー
リーリー
次のコードを実行すると、バイナリ ファイルを取得できます:
リーリー
phpからcを呼び出す
リーリー
これにより、php 拡張機能の作成に必要な基本的なスケルトンが生成されます。次に、拡張機能をローカルに保持したい場所にフォルダーを移動します。そしてを移動してください
リーリー
1つのディレクトリ、11のファイル
これらのファイルについては、上記の php ドキュメントで詳しく説明しています。拡張ファイルを作成します。まず config.m4 を編集します。説明は省略しますが、私の結果は次のとおりです:
リーリー
構成のセットアップが完了したので、実際に PHP スクリプトからライブラリを呼び出す必要があります。これを行うには、自動生成されたファイル hello_from_rust.c を変更する必要があります。まず、hello_from_rust.h ヘッダー ファイルを include コマンドに追加します。次に、confirm_hello_from_rust_compiled の定義メソッドを変更する必要があります。
リーリー
ここで、拡張機能を構築してみましょう:
リーリー
これで、実行できるようになりました。
リーリー
悪くありません。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); }
无用的微基准
那么为什么你还要这样做?我还真的没有在现实世界里使用过这个。但是我真的认为斐波那契序列算法就是一个好的例子来说明一个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微基准!