$30 off During Our Annual Pro Sale. View Details »

PHPからC#のライブラリを呼べるようにしたdotnet_ffiを趣味でつくってみた

johgus
September 24, 2022

 PHPからC#のライブラリを呼べるようにしたdotnet_ffiを趣味でつくってみた

PHP Conference Japan 2022 の 09/25に行なわれる
dotnet_ffiについての講演資料です。
https://fortee.jp/phpcon-2022/proposal/6fa15e19-4a11-4af0-b5ff-ee19aa8ff931

dotnet_ffiソース
https://github.com/pg-ito/dotnet_ffi

文: よーがす
Twitter: @pg_ito
Blog: https://b64.pw/blog/

johgus

September 24, 2022
Tweet

Other Decks in Programming

Transcript

  1. PHPからC#のライブラリを 呼べるようにしたdotnet_ffiを 趣味でつくってみた よーがす @pg_ito (アバター: 佐波野いりこ @SabanoIriko) PHP Conference

    Japan 2022 09/25
  2. 自己紹介 • エンジニア 最近は主にPHP、C#、たまにC++、JavaScript サーバの負荷対策が好き • 趣味 配信、電子工作、スプラトゥーン3 名前: よーがす

    Twitter: @pg_ito Blog: https://b64.pw/blog/
  3. もくじ • dotnet_ffiってなに? • 作った動機 • 想定される利用ケース • dotnet_ffiの構成 •

    dotnet_ffiの簡単な使い方 • CoreCLRって? • Extensionを作るための情報源 • まとめ • 今後の展望
  4. dotnet_ffiってなに?

  5. dotnet_ffiってなに? PHPからC#のライブラリを直接呼べるようにしたPHP Extension CoreCLRをつかってC#のDLLを動的にロード、実行している PHPプロセスが作り直されるまでC#のVMを使いまわすので C#のJITの恩恵を得られて高速に動作 リポジトリ: https://github.com/pg-ito/dotnet_ffi (MITライセンス)

  6. パフォーマンス比較 fibonacci数を計算させた場合PHPのみに比べて約16倍ほど高速(PHP8.1.6の場合)

  7. 作った動機

  8. 作った動機 ・Unityと同じ処理をPHPサーバ側でも行いたい ・Extensionを書かずに手軽にPHPの処理を高速化したい ・C#にしかない機能を使いたい

  9. 想定される利用ケース

  10. UnityClientと処理の共通化 ・ゲームのチート対策 クライアントから送られてきたデータが不正なものでないか 正規のクライアント側のC#の処理と比較できる ・バリデータの共通化 APIのエラーチェックなどを一元化できる

  11. 処理の高速化 ・CPU使用率の高い処理をC#側で高速に行える 例えばfibonacci数を求めるような処理で効果を発揮 (リポジトリ内のベンチマークにこのケースを想定したコードを同梱)

  12. C#にしかないライブラリの利用 ・Linqでのデータの抽出 多次元配列を任意の形に整形 (おそらくPHPのみで書かれている限りPHP移植版Linqにくらべて高速な…ハズ)

  13. dotnet_ffiの構成

  14. PHP Process 設計の特徴 ・PHP ExtensionがC/C++で書けることを利用してCoreCLR経由でC#とバインド PHP ➡ dotnet_ffi(C,C++) ➡ CoreCLR(C++)

    ➡ C# Library PHP 標準 Extension CoreCLR (C++) C# Library PHP 標準関数等 PHP 言語 構造 dotnet_ffi (C, C++) ※CoreCLRについては後述
  15. PHP8対応での影響 ・ARG_INFOの対応 PHP8でビルドするとついにwarningが出るようになった。 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &arg1, &arg2) == FAILURE)

    { ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ret_s64_arg_s64_s64, 0, 2, IS_LONG, 0) ZEND_ARG_INFO(0, long_arg1) ZEND_ARG_INFO(0, long_arg2) ZEND_END_ARG_INFO() PHP_ME(DotnetFFI, ret_s64_arg_s64_s64, arginfo_ret_s64_arg_s64_s64, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) 型情報を追加
  16. dotnet_ffiの簡単な使い方

  17. ビルド ・ソースのクローン git clone https://github.com/pg-ito/dotnet_ffi.git . ・docker composeでビルドと実行 cd build_env

    docker compose -up -d --build docker compose exec php_fpm /bin/bash -c "cd /var/www && ./br.sh && ./run.sh" ・もし直接ビルドする場合 yum install -y devtoolset-7-gcc devtoolset-7-gcc-c++ yum install -y --enablerepo=epel,remi,remi-php81 php php-devel yum install -y dotnet-sdk-6.0 ./br.sh
  18. dotnet_ffiの簡単な使い方 ・引数がint、戻り値もintの場合の呼び出し方 $retInt64 = DotnetFFI::ret_s64_arg_s64(30); ※デフォルト設定だとdotnet_dllディレクトリにある fibonacci数のサンプルコードを呼び出すようになっている(iniで変更可) ・実行 docker compose

    exec php_fpm /bin/bash -c "cd /var/www && ./run.sh mycode.php" ・その他サンプルコード ./ext_test.php … 各種メソッドの動作サンプル ./benchmarker/runbench.php … フィボナッチのベンチマーク
  19. CoreCLRって?

  20. CoreCLRって? ・.NETの実行環境 ほかのC/C++のプログラムに.NETのVM(※CLI)を 組み込むことができるライブラリ(というかこれ自体で.NETを動かしている) dotnet_ffiはこれを使ってPHP ExtensionからC#を実行している ※CLI…Common Language Infrastructure ※CoreCLRリポジトリ

    https://github.com/dotnet/coreclr/tree/v3.1.29 ・CoreFX、Mono Runtime等と類似の役割 ※参考: Embedding Mono https://www.mono-project.com/docs/advanced/embedding/
  21. CoreCLRの使い方(初期化) ・libcoreclr.soをdlopenする coreClr = dlopen(coreClrPath.c_str(), RTLD_NOW | RTLD_LOCAL); ・dlsymでそれぞれのシンボルのアドレスを取得 initializeCoreClr

    = (coreclr_initialize_ptr)dlsym(coreClr, "coreclr_initialize"); createManagedDelegate = (coreclr_create_delegate_ptr)dlsym(coreClr, "coreclr_create_delegate"); shutdownCoreClr = (coreclr_shutdown_ptr)dlsym(coreClr, "coreclr_shutdown");
  22. CoreCLRの使い方(初期化) ・CoreCLR初期化 CoreCLRにpublishされたC#の.dllファイルパスを「:」区切りで propertyValuesに一通りわたして初期化します int hr = initializeCoreClr( runtimePath, //

    App base path "HostingOnPHP", // AppDomain friendly name sizeof(propertyKeys) / sizeof(char*), // Property count propertyKeys, // Property names propertyValues, // Property values &hostHandle, // Host handle &domainId); // AppDomain ID dotnet_ffiではPHPプロセスが作られるタイミングで初期化 PHP_MINIT_FUNCTION(dotnet_ffi)
  23. CoreCLRの使い方(C#呼び出し) ・C#のメソッドの関数ポインタを取得 C#のプロジェクト名、名前空間までつけた完全修飾のクラス名、メソッド名とC#のクラスへ のアドレスが格納されるポインタを渡す *hr = createManagedDelegate ( hostHandle, domainId,

    target_project_name .c_str(), target_class_name.c_str(), method_name, (void**)&managedDelegateInvokeReturnString); ・C#のメソッド呼び出し std::string ret = managedDelegateInvokeReturnString(inStr); この関数ポインタを実行することで C#のメソッドを呼び出せる
  24. CoreCLRの使い方(終了) ・C#のCoreCLRの終了 使い終わったらshutdown dotnet_ffiではphpプロセスが作り直されるタイミングにあわせてシャットダウン (VMの生成と初回JITコンパイルコストが高いので) int hr = shutdownCoreClr(hostHandle, domainId);

    dotnet_ffiではPHPのプロセスが終了するタイミング PHP_MSHUTDOWN_FUNCTION(dotnet_ffi)
  25. Extensionを作るための情報源

  26. Extensionについての公式ドキュメント 公式のExtensionのドキュメントはない(2022年9月時点) 以前は「PHP のコア: ハッカーの手引き」という項目があったが無くなっている それもPHP5.3.3頃で止まっている模様 検索しても非公式のミラーがいくつか引っ掛かる程度 (それもTLS対応していないサイトだったりする)

  27. PHPのソースを追いましょう 現時点で一番確実なのはPHPのソースを追う PHPのソースにあたることで下記の情報が得られる ・Extensionのひな形の作り方 ・Extensionとしてのクラスや関数の宣言のしかた ・PHP用のconfig.m4の書き方 ※ソースを追うときには使いたい PHPバージョンのタグを指定して追うのが安全

  28. Extensionのひな形の作り方 そもそもどこからExtension開発に手を付けていいかがわかりづらいが PHPのソース内のextディレクトリを探すと ext_skel.phpというスクリプトがあり、これでひな形が作れる https://github.com/php/php-src/blob/php-8.1.10/ext/ext_skel.php php ext_skel.php --help とするとExtensionのひな形の作り方やビルドのしかたがでてくる

  29. Extensionのひな形の作り方 extension_exampleというExtensionを作る場合の例 php ext_skel.php --ext extension_example cd extension_example phpize ./configure

    make make test ※PECLでXMLを書いてひな形にする方法もあります ここに作りたいExtension名を指定する
  30. Extensionとしてのクラスや関数の宣言のしかた 旧公式ドキュメントでもExtensionの関数の宣言のやり方は載っていたが Extensionとしてのクラスの宣言方法は載っていなかった PHPの標準クラスで似たようなインターフェイスのクラスを探して その書き方を参考にするのが楽 同じextディレクトリ内にあるので色々見てみると参考になる (個人的にはSQLite3のクラスが一通りそろっていて参考になった)

  31. PHP用config.m4の書き方 ライブラリのリンクのしかたなど、automakeから拡張されているものもある 例えばライブラリをstatic linkする際の書き方などは、ほかのExtensionを調べたほうが 確実(一番調べるのに苦労した) 例 PHP_ADD_LIBRARY_WITH_PATH("coreclr_ctlpp", $DOTNET_FFI_DIR/coreclrhost, EXTRA_LDFLAGS)

  32. まとめ

  33. まとめ ・PHPからC#のクラスが呼べる UnityなどのC#で動くクライアントとの親和性が向上できる ・手軽に処理速度の向上が見込める Extensionを書かなくてもC#などで高速化したい処理を書くだけでOK ・PHPのソースを追うの大事 Extensionを作るときにはもちろん 標準のクラスや関数の細かい挙動の理解の助けになる

  34. 今後の展望

  35. 今後の展望 ・.NET7への対応 .NET7自体が速度向上しているのもあり、 間接的なPHPの処理速度向上が期待できそう ・呼び出しメソッドを可変化 C#側のメソッド名をPHPから動的に指定して実行できるようにしたい 但し、パフォーマンスが若干犠牲になる可能性あるので慎重にゆきたい (現在はiniで指定する形、一度見つけた関数ポインタは使いまわし) ・PHP8.2への対応 リリースされ次第対応予定

  36. 要望等お気軽にお寄せください

  37. おまけ: エンジニア系Vtuberはじめました Twitter @SabanoIriko Youtube 佐波野いりこチャンネル https://www.youtube.com/channel/UCQoB5I4tj36wgcvzIQrjpVA ↑よろしければチャンネル登録 よろしくお願いします_(._.)_