Help us understand the problem. What is going on with this article?

PHP7.4の新機能、FFIでGoを使ってみた

PHP7.4がリリースされた

今回のアップデートも目まぐるしい機能が多く追加されました
機能の紹介は既にあるたくさんの記事にお任せするとして、個人的に気になっていた新機能 FFIを試してみます

FFIとは

Foreign Function Interface の略でPHPで別の言語を動かせる機能です
Cなどで実装した共有ライブラリを読み込んで、利用することができます
メリットとしては実行速度が速くなることが大きいかと思われます

FFIでGoを利用する

本題です
Goを利用するにはGoで共有ライブラリを実装する必要があります

何故Goを利用するのか

Cより簡単だから

Goで共有ライブラリを作るには

  • packageをmainにする
  • main関数が必要
  • exportする関数に // export [function name] のコメントを書く
  • ビルド時は -buildmode=c-shared を付ける

今回はフィボナッチ数を計算するプログラムを用意しました
再帰を使っていて数が大きくなるほど計算に時間がかかるサンプルです

package main

import "C"

//export calcFibonacci
func calcFibonacci(i uint) uint {
    if i < 2 {
        return i
    }

    return calcFibonacci(i - 1) + calcFibonacci(i - 2)
}

func main() {}

ビルド

説明通り -buildmode=c-shared を付けてビルドします
出力ファイル名は、Windowsの場合は *.dll 、macOSでは *.dylib 、Linuxでは *.so でしょうか
今回はmacOSを使っているので、 fibonacci.dylib というファイル名を指定しました

ビルドを済ませると指定したファイル名のバイナリと同じ名前の *.h ファイルが生成されているはずです
このヘッダーファイルを用いることで他のプログラムから利用することができます
(今回はこのヘッダーファイルを利用しない方法で説明します)

PHPからFFIでGoを利用する

準備が整ったのでいよいよFFIを利用します

cdef関数を利用します
一つ目の引数にC形式での関数定義
二つ目の引数に共有ライブラリへのパスを指定します

$ffi = FFI::cdef(
    'extern unsigned int calcFibonacci(unsigned int p0);',
    '[PATH]/fibonacci.dylib'
);

あとはこのように呼び出すことができます

echo($ffi->calcFibonacci(10)); // 55

以上がFFIからGoを利用する方法です
では、続いてどれだけ実行速度に差が出るか比較してみます

FFIでPHPはどれだけ速くなるか

以下のような検証用プログラムを作成しました
FFIとPHPそれぞれでフィボナッチ数列を40番目まで求めるプログラムです
実行はCLIから行いました

const COUNT = 40;

$ffi = FFI::cdef(
    'extern unsigned int calcFibonacci(unsigned int p0);',
    '[PATH]/fibonacci.dylib'
);

// 比較対象のPHP実装サンプル
function calcFibonacci(int $i): int {
    if ($i < 2) {
        return $i;
    }

    return calcFibonacci($i - 1) + calcFibonacci($i - 2);
}

// Go(FFI)実装の実行速度計測
$startTime = microtime(true);
for($i = 1; $i <= COUNT; $i++) {
    $ffi->calcFibonacci($i);
}
$time = microtime(true) - $startTime;
echo "Go(FFI): {$time}\n";

// PHP実装の実行速度計測
$startTime = microtime(true);
for($i = 1; $i <= COUNT; $i++) {
    calcFibonacci($i);
}
$time = microtime(true) - $startTime;
echo "PHP: {$time}\n";

結果

Go(FFI): 1.5760109424591 秒
PHP: 33.616943836212 秒

かなり大きな差が出ていると思います
PHPで重い処理を行う場合に、FFIはかなり強みになると感じました

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away