LuneScript のタプルの go 実装
Page content
LuneScript の v1.5.3 からタプルを対応している。
このタプルの go 実装についてパフォーマンスを調べた内容を載せておく。
LuneScript のタプルの go 変換初期実装
ここでは、LuneScript のタプルを go に変換した際に、 どのような実装になっているかを説明する。
LuneScript で次のようなタプル (int と str のペア) を定義した場合、
(int,str)
go では次の型として扱う。
[]any
つまり、タプルの各要素の型情報は一旦 any に丸め、 タプルから値を取得する際に型変換を行なう。
例えば次の LuneScript のコードは、
fn hoge() (int,str) {
return (= 1, "abc" );
}
fn sub() {
let val1, val2 = hoge()...;
print( val1, val2 );
}
go に変換すると次のようなコードになる。
func hoge() []LnsAny {
return []LnsAny{1, "abc"}
}
func sub() {
tuple := hoge()
val1 := tuple[0].(int) // 型変換
val2 := tuple[1].(string) // 型変換
fmt.Print( "%v %v", val1, val2 )
}
上記の通り、タプルから値を取得する際に型変換を行なうため、 実行時のオーバーヘッドがかかる。
このオーバーヘッドを削減するため、 go の generics を利用する方法を検討し、 両者のパフォーマンスを実測し、より良い方を採用する。
go の generics を利用した実装
上記の LuneScript を go の generics を利用した実装は以下になる。
type Tuple2[T1,T2 any] struct {
Val1 T1
Val2 T2
}
func hoge() *Tuple2[int,string] {
return &Tuple2[int,string]{1, "abc"}
}
func sub() {
tuple := hoge()
val1 := tuple.Val1
val2 := tuple.Val2
fmt.Print( "%v %v", val1, val2 )
}
ベンチマーク用 LuneScript コード
ベンチマークを測る LuneScript のコードは以下のものを利用する。
fn sub(flag:bool) : (int,str)!,str! {
if flag {
return (= 1,"abc"), nil;
}
return nil, "err";
}
fn func(flag:bool) : (int,str)!,str! {
let val1, val2 = sub(flag)!...;
let val3, val4 = sub(flag)!...;
return (= val1 + val3, val2 .. val4 ), nil;
}
for _ = 1, 1000 * 1000 * 10 {
func( true );
}
さらに、 tuple を使わずに多値返却のみで等価な動作をする次のコードも参考に速度を測る。
fn sub(flag:bool) : int!,str!, str! {
if flag {
return 1, "abc", nil;
}
return nil, nil, "err";
}
fn func(flag:bool) : int!,str!,str! {
let val1, val2, err1 = sub(flag);
if err1 {
return nil, nil, err1;
}
let val3, val4, err2 = sub(flag);
if err2 {
return nil, nil, err2;
}
when! val1, val2, val3, val4 {
return val1 + val3, val2 .. val4,nil;
}
error( "" );
}
for _ = 1, 1000 * 1000 * 10 {
func( true );
}
ベンチマーク結果
- tuple: []any を利用した場合
real 0m1.925s
user 0m1.968s
sys 0m0.111s
- tuple: generics を利用した場合
real 0m0.996s
user 0m1.064s
sys 0m0.033s
- 非tuple: 多値返却
real 0m0.980s
user 0m1.015s
sys 0m0.021s
まとめ
まとめると、それぞれの実行時間は次の結果となった。
(tuple: []any) >>>> (tuple: generics) >≒ (非tuple: 多値返却)
少し意外だったのは、 タプルを使わない多値返却と比べても、 ほとんどパフォーマンスが変わらない処理時間になることが判った。
以上から、LuneScript のタプルの go 変換実装は、ジェネリクスを使用する。