Zenにはループをコンパイル時に明示的にインライン展開する機能があります。ここではinline while
/ inline for
について説明します。
ループのインライン展開 (ループ展開、もしくは、ループアンローリング) とは、プログラムサイズを犠牲に、プログラムの実行速度を向上する最適化手法です。各ループごとに発生するループのオーバーヘッド (ループ終了条件のテスト) を減少させることで、ループの実行速度を向上させます。
例えば次のwhile
ループでは、1ループごとに、ループ終了条件のテスト (i < 3
) が行われます。
var result: u32 = 0;
var i: u32 = 0;
while (i < 3) : (i += 1) {
result += i;
}
このwhile
ループをインライン展開すると次のコードになります。このように、ループ条件終了のテスト (i < 3
) の実行をなくすことができます。
var result: u32 = 0;
var i: u32 = 0;
// whileループ内の処理を展開
result += i;
i += 1;
result += i;
i += 1;
result += i;
i += 1;
通常のwhile
やfor
ではこのようなループのインライン展開は行われません。
ノート: 実際には、Zenのコンパイラ (およびLLVM) のリリースモード最適化は極めて優秀なので、この程度のループであれば定数への畳込みが行われます。
inline while
/ inline for
を使うことで、プログラマはコンパイラに対して明示的にループのインライン展開を指示できます。
通常のwhile
/ for
と異なり、inline while
/ inline for
はループの制御がコンパイル時計算可能でなければなりません。
上述の例をinline while
を使ってインライン展開するコードは、以下の通りです。ループの制御変数i
をcomptime var
とし、コンパイル時計算可能な変数にしている部分が重要です。
examples/ch11-advanced/inlining/src/inlining.zen:4:11
test "inline while" {
var result: u32 = 0;
comptime var i: u32 = 0;
inline while ( i < 3 ) : (i += 1) {
result += i;
}
ok(result == 3);
}
上のコードは、下のようなコードに展開されます。
var result: u32 = 0;
// whileループ内の処理を展開
result += 0;
result += 1;
result += 2;
もしアセンブリに馴染みがあるようでしたら、ぜひコンパイルして確認してみて下さい。ただし、リリースモードでビルドを行うと最適化により、定数に畳み込まれるため、デバッグモードでビルドして下さい。
次に、inline for
を使用したコードの例を示します。ループ制御はコンパイル時計算可能であれば良いため、型を使うことも可能です。
test "inline for" {
var result: usize = 0;
inline for ([_]type{ f16, f32, f64, f128 }) |T| {
result += @sizeOf(T);
}
ok(result == 30);
}
このような利用方法は、例えば、ジェネリックなライブラリを作成した場合のテストケース作成に役立ちます。
組込み関数と組み合わせてメタ情報を取得することで、次のような応用も可能です。
ls
、cat
、grep
というコマンドがあり、文字列を入力として受け取り、列挙型として返したいとします。その場合は、次のように書くことができます。
const Commands = enum {
ls,
cat,
grep,
};
fn stringMatch(cmd: []u8) !Commands {
const info = @typeInfo(Commands);
inline for (info.Enum.fields) |field| {
if (std.mem.equal(u8, cmd, field.name)) {
return @field(Commands, field.name);
}
}
return error.NotFound;
}
test "string match" {
const cmd = try stringMatch("cat");
testing.equal(Commands.cat, cmd);
}
少し解説すると、@typeInfo
でCommand
列挙型のメタ情報を得ます。このメタ情報には、フィールド名の配列が含まれます(info.Enum.fields
)。これをinline for
で展開しています。
フィールド名から列挙型の値を取得するには、@field
に列挙型とフィールド名を渡します。
☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2020 connectFree Corporation. All rights reserved. | 特定商取引法に基づく表示
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.