plan チュートリアル
このチュートリアルでは、データ並列操作を Plan として記述し、v1 のサブセットに照らして検査し、プランが拒否されたときに理由を読む方法を示します。プランは単なるデータなので、このパッケージはすべてのターゲットで使えます。
クイックスタート
パッケージと、ポリシーのための shared をインポートします。
import {
"Luna-Flow/luna_thread/plan",
"Luna-Flow/luna_thread/shared",
}
8 個の Int 値に対するマッププランを作って検証します。
test "plan quick start" {
let plan = @plan.map("double", @plan.i32_type(), 8)
inspect(@plan.is_runnable(plan), content="true")
inspect(plan.label(), content="double")
}
日常的なタスク
プランにポリシーを与える
既定のポリシーはワーカー 1、チャンクサイズ 1 です。make_execution_policy で並列のポリシーを作り、ビルダーに渡します。
test "policy" {
let policy = @shared.make_execution_policy(worker_count=4, chunk_size=16).unwrap()
let plan = @plan.map_reduce("sum", @plan.i64_type(), 64, @plan.sum_reduction(), policy~)
assert_eq(plan.policy().worker_count, 4)
assert_true(@plan.has_sum_reduction(plan))
assert_true(@plan.is_runnable(plan))
}
プランが拒否された理由を読む
validate はすべての問題を一度に返すので、まとめて報告できます。
test "issues" {
let policy = @shared.make_execution_policy(worker_count=8, chunk_size=8).unwrap()
let plan = @plan.reduce("tiny", @plan.f32_type(), 4, @plan.custom_reduction("xor"), policy~)
let issues = @plan.validate(plan)
assert_true(issues.any(@plan.is_unsupported_value_type))
assert_true(issues.any(@plan.is_chunk_size_too_large))
assert_true(issues.any(@plan.is_unsupported_reduction_kernel))
assert_eq(issues.length(), 4)
}
4 つ目の問題は WorkerCountExceedsInput です。要素 4 つに対してワーカーが 8 つあります。
珍しいプランを手で作る
Plan::new は渡されたものを何でも保存するので、スキャンの順序の要件のような検証規則をテストできます。
test "relaxed scan" {
let plan = @plan.Plan::new(
"prefix",
@plan.scan_plan_kind(),
@plan.DataDomain::new(@plan.i32_type(), 8),
ordering=@shared.relaxed_order(),
)
let issues = @plan.validate(plan)
assert_true(issues.any(@plan.is_unsupported_ordering))
assert_true(issues.any(@plan.is_scan_requires_stable_ordering))
}
プランを調べる
アクセサは各フィールドを返し、種類の述語を使えばパターンマッチが要りません。
test "inspect" {
let plan = @plan.scan("prefix", @plan.i64_type(), 32)
assert_true(@plan.is_scan(plan))
assert_eq(plan.reduction(), None)
assert_true(plan.ordering() is @shared.PreserveInputOrder)
assert_eq(plan.domain().input_length(), 32)
}
さらに進んで
プランは backend/native を通じて実行可能になります。その map_request_from_plan、reduce_request_from_plan、scan_request_from_plan は有効なプランを型付きのリクエストに変換します。また、ワークフローでは @workflow.compute_node がプランをタスクグラフに埋め込み、@workflow.validate がその問題を InvalidComputePlan として報告します。ライブラリのコードはファサードではなく Plan と ValidationIssue に対して書けば、すべてのターゲットでビルドできます。
よくある落とし穴
is_runnableは、ネイティブカーネルが要求する「チャンクごとに 1 つのワーカー」()を検査しません。reduceとscanは常に入力順序を保ちます。ほかの順序を試すにはPlan::newを使ってください。- スキャンプランにはカーネルがなく、ネイティブのスキャンは常に累積和です。
- 列挙型はパッケージの外では読み取り専用です。値を作るときは
@plan.I32ではなく@plan.i32_type()と書いてください。@plan.I32でのパターンマッチは使えます。
次のステップ
plan API に validate のすべての規則が載っています。plan の設計では、各種類のプランが何を計算するか、スキャンに順序付きの入力が必要な理由を説明しています。workflow チュートリアルではプランをタスクグラフに組み込みます。