Go の関数パフォーマンス
LuneScript は golang へのトランスコンパイルをサポートしている。
golang 対応の付加機能として、LuneScript には限定的なスレッド機能を提供している。
「限定的」の大きな理由の一つとして、 golang 向け LuneScript ランタイムのマルチスレッド対応問題がある。
golang 向け LuneScript ランタイム
golang 向け LuneScript ランタイムは、幾つか機能を持っている。 その機能の中には、次のものを含む。
- and or 演算子の処理を実現するためのスタック。
- lua ランタイム制御。
LuneScript は元々 Lua 向けのトランスコンパイラであり、 Lua はシングルスレッドの言語である。 そのため、 LuneScript の上記ランタイムもシングルスレッドを 想定した構成になっているため、そのままではマルチスレッドで利用できない。
シングルスレッド限定で良ければ、 マルチスレッドを考慮した作りに比べて簡単になるし、その分高速にもなる。
マルチスレッド対応
LuneScript ランタイムをマルチスレッド対応する際には、 次の 2 つの方法が考えられる。
- ランタイムの複数インスタンス化
- ランタイムの排他制御
ランタイムの複数インスタンス化
スレッド毎にランタイムをインスタンス化して管理する場合、 どのランタイムがどのスレッドのものかを管理する必要がある。
一方で、golang には go-routine の識別 ID がない。 つまり、 「必要な時に識別 ID からランタイムを取得する」 ということが 出来ないことになる。
cgo を利用すれば、 go-routine が動作している pthread ID を取得することは可能だが、 golang のタスクスケジューリングでは、 ある go-routine を実行する pthread が常に固定されている訳ではない。 つまり動的に取得した pthread ID は、 go-routine の ID にはならない。
ということは、 LuneScript ランタイム情報にアクセスするためには、 全ての関数にランタイム情報を渡していく必要がある。
ランタイムの排他制御
ランタイムの複数インスタンス化には、 全ての関数パラメータにランタイム情報の追加が必要になる。 これは、全ての関数でオーバーヘッドが追加になり、 当然プログラム全体がその分遅くなる。
そこで、ランタイムは複数インスタンス化せずに、 アクセスが必要な時だけ排他を掛けるケースを考える。
排他には次のケースがある。
- mutex を使う
- channel を使う
今回は両方検証する。
パファーマンス
golang の簡単なプログラムで、 複数インスタンス化したケースと、ランタイムの排他制御を実施したケースの パフォーマンスを測ってみた。
なお、実際に複数インスタンス化する訳ではなく、 関数に引数を追加するケースと、 排他制御を追加するケースとで、パフォーマンスを測っている。
結果以下となった。
引数に追加 <<<< mutex << channel
「引数に追加」が一番負荷が少なく、「channel」が一番負荷が高かった。
ただ「mutex」や「channel」の場合は、 必要な箇所だけに制御を追加すれば良いということを考えると、 「mutex」で対応するのが一番良いのかもしれない。
LuneScript のマルチスレッド対応は、「引数に追加」でいこうと思う。