backend/native API
パッケージ Luna-Flow/luna_thread/backend/native は @native としてインポートされ、C の外部関数インターフェースによるバックエンドです。C ランタイムの整数カーネルを MoonBit の配列に対して実行し、ワークフローを C スケジューラに投入し、プランを型付きのネイティブリクエストに変換します。native ターゲットでのみビルドでき、ネイティブスタブから C ランタイムをリンクします。
このパッケージの列挙型はパッケージの外では読み取り専用です。コンストラクタでパターンマッチはできます。リクエストレコードは *_from_plan 関数で作ります。
バックエンド情報
backend_target
Native を返します。
pub fn backend_target() -> @shared.BackendTarget
default_plan_kind
Map を返します。
pub fn default_plan_kind() -> @plan.PlanKind
package_name
"backend/native" を返します。
pub fn package_name() -> String
supports_openmp
true を返します。
pub fn supports_openmp() -> Bool
この値は定数です。moon によるビルドは OpenMP なしで C スタブをコンパイルするため、そのビルドではカーネルは呼び出し元のスレッドで実行されます。ネイティブバックエンドの設計を参照してください。
カーネル
すべてのカーネルは長さ の FixedArray[Int]、ワーカー数 、チャンクサイズ を受け取り、終わるまでブロックし、入力を変更しません。次の条件が必要です。
満たさない場合は InvalidArgument で失敗します。core API にある同名のファサード関数は、これらを省略可能な引数付きでラップしています。
execute_map_i32
すべての要素を 2 倍にした新しい配列を返します。ある が 32 ビットに収まらない場合は Overflow を返します。
pub fn execute_map_i32(FixedArray[Int], Int, Int) -> Result[FixedArray[Int], NativeRequestError]
execute_reduce_sum_i32
要素の総和を返します。失敗した場合は常に 0 を返します。
pub fn execute_reduce_sum_i32(FixedArray[Int], Int, Int) -> Int
カーネルは各チャンクを左から順に足し、次にチャンクの和を左から順に足します。これらの部分和のどれかがオーバーフローすると失敗します。返された 0 以外の値は正確な和です。0 は和である場合も失敗である場合もあります。
execute_scan_sum_i32
包含的な累積和を返します。
pub fn execute_scan_sum_i32(FixedArray[Int], Int, Int) -> Result[FixedArray[Int], NativeRequestError]
部分和がオーバーフローすると Overflow で失敗します。
test "kernels" {
let input : FixedArray[Int] = [3, 1, 4, 1, 5, 9]
debug_inspect(
@native.execute_scan_sum_i32(input, 3, 2),
content="Ok(<FixedArray: [3, 4, 8, 9, 14, 23]>)",
)
assert_eq(@native.execute_reduce_sum_i32(input, 3, 2), 23)
debug_inspect(@native.execute_map_i32(input, 2, 2), content="Err(InvalidArgument)")
}
ワークフロー
workflow_is_supported
@workflow.validate が問題を見つけず、ワークフローのポリシーがネイティブバックエンドを指定しているとき true を返します。
pub fn workflow_is_supported(@workflow.Workflow) -> Bool
submit_workflow
@workflow.submit(workflow, backend=Native) を返します。これは検証済みの投入記録で、ワークフローは実行しません。
pub fn submit_workflow(@workflow.Workflow) -> @workflow.Submission
WorkflowRuntimeState
C ランタイムにおけるワークフローの状態です。
pub enum WorkflowRuntimeState {
Submitted
Running
Completed
Failed
Rejected
} derive(Eq, @debug.Debug)
ワーカーが最初のノードを取り出すまで、状態は Submitted のままです。ランタイムが Rejected を設定することはなく、拒否された投入では代わりに空のハンドルが返ります。
WorkflowResult
ワークフローのスナップショットです。状態、ランタイムのステータスコード、完了したノード数、失敗したノードの id(失敗がなければ -1)からなります。
pub struct WorkflowResult {
state : WorkflowRuntimeState
status : Int
completed_nodes : Int
failed_node_id : Int
} derive(Eq, @debug.Debug)
status はアーキテクチャガイドに挙げたコードのいずれかです。
NativeWorkflowHandle と WorkflowHandle
C ランタイムで実行中のワークフローを指す不透明なポインタと、その MoonBit ラッパーです。
#external
pub type NativeWorkflowHandle
pub struct WorkflowHandle {
raw : NativeWorkflowHandle
}
submit_workflow_async
ワークフローを平坦な整数配列にコピーし、ポリシーの worker_count 個のスレッドで C スケジューラ上で開始します。
pub fn submit_workflow_async(@workflow.Workflow) -> Result[WorkflowHandle, NativeRequestError]
ワークフローは MoonBit では検証されません。C ランタイムが検査し、グラフを拒否した場合は空のハンドルを返します。この関数は常に Ok を返します。計算ノードはスケジュールされますが、そのプランは実行されません。
poll_workflow
待たずにスナップショットを返します。
pub fn poll_workflow(WorkflowHandle) -> WorkflowResult
wait_workflow
ワークフローが Completed か Failed になるまで待ち、スナップショットを返します。
pub fn wait_workflow(WorkflowHandle) -> WorkflowResult
空のハンドルに対しては、状態 Submitted、ステータス 7 ですぐに返ります。ワークフローがデッドロックすると戻りません。
drop_workflow
ランタイムのスレッドを停止して合流させ、ワークフローを解放します。ハンドルごとに一度だけ呼んでください。空のハンドルに対しては何もしません。
pub fn drop_workflow(WorkflowHandle) -> Unit
型付きのネイティブリクエスト
NativeValueType と NativeReductionKernel
C ランタイムが実装している値の型とリダクションカーネルです。
pub enum NativeValueType {
I32
I64
} derive(Eq, @debug.Debug)
pub enum NativeReductionKernel {
Sum
Min
Max
} derive(Eq, @debug.Debug)
NativeRequestError
プランをネイティブリクエストにできなかった理由、またはカーネルが失敗した理由です。
pub enum NativeRequestError {
UnsupportedBackend
UnsupportedMode
UnsupportedValueType
UnsupportedReductionKernel
InvalidArgument
Overflow
} derive(Eq, @debug.Debug)
NativeMapRequest、NativeReduceRequest、NativeScanRequest
C のリクエスト構造体に対応する型付きのレコードです。
pub struct NativeMapRequest {
input : @shared.NativeBuffer
output : @shared.NativeBuffer
element_count : Int
value_type : NativeValueType
worker_count : Int
chunk_size : Int
} derive(Eq, @debug.Debug)
pub struct NativeReduceRequest {
input : @shared.NativeBuffer
output : @shared.NativeBuffer
element_count : Int
value_type : NativeValueType
reduction_kernel : NativeReductionKernel
worker_count : Int
chunk_size : Int
} derive(Eq, @debug.Debug)
pub struct NativeScanRequest {
input : @shared.NativeBuffer
output : @shared.NativeBuffer
element_count : Int
value_type : NativeValueType
reduction_kernel : NativeReductionKernel
worker_count : Int
chunk_size : Int
} derive(Eq, @debug.Debug)
プランから作ったリクエストの input と output のバッファは NativeBuffer::new(0, 0) です。プランが表すのはデータの形であってデータそのものではありません。このモジュールにはこれらのレコードを C に送る関数はありません。将来の実行器がバッファを埋めるための、検証済みの記述です。
map_request_from_plan
Map プランを NativeMapRequest に変換します。
pub fn map_request_from_plan(@plan.Plan) -> Result[NativeMapRequest, NativeRequestError]
プランが Map プランでなければ UnsupportedMode で失敗します。そうでなければ @plan.validate の最初の問題から対応づけたエラーで失敗します。値の型、リダクションカーネル、バックエンド、モードの問題は同名のエラーに、MissingReductionKernel は UnsupportedReductionKernel に、残りの問題は InvalidArgument に対応します。
reduce_request_from_plan
Reduce または MapReduce プランを NativeReduceRequest に変換します。エラーの規則は同じです。
pub fn reduce_request_from_plan(@plan.Plan) -> Result[NativeReduceRequest, NativeRequestError]
scan_request_from_plan
Scan プランを、カーネルが Sum の NativeScanRequest に変換します。エラーの規則は同じです。
pub fn scan_request_from_plan(@plan.Plan) -> Result[NativeScanRequest, NativeRequestError]
map_request_is_valid、reduce_request_is_valid、scan_request_is_valid
C の検証器も検査するリクエストのフィールドを検査します。要素数、ワーカー数、チャンクサイズが正であること、値の型がサポートされていること、スキャンではカーネルが Sum であることです。
pub fn map_request_is_valid(NativeMapRequest) -> Bool
pub fn reduce_request_is_valid(NativeReduceRequest) -> Bool
pub fn scan_request_is_valid(NativeScanRequest) -> Bool
map_request_value_type、reduce_request_value_type、scan_request_value_type、reduce_request_kernel、scan_request_kernel
リクエストの値の型(I32 は 0、I64 は 1)とカーネル(Sum は 0、Min は 1、Max は 2)の C のコードを返します。
pub fn map_request_value_type(NativeMapRequest) -> Int
pub fn reduce_request_value_type(NativeReduceRequest) -> Int
pub fn scan_request_value_type(NativeScanRequest) -> Int
pub fn reduce_request_kernel(NativeReduceRequest) -> Int
pub fn scan_request_kernel(NativeScanRequest) -> Int
test "requests from plans" {
let policy = @shared.make_execution_policy(worker_count=2, chunk_size=4).unwrap()
let plan = @plan.map_reduce("sum", @plan.i64_type(), 8, @plan.max_reduction(), policy~)
guard @native.reduce_request_from_plan(plan) is Ok(request) else {
fail("expected a request")
}
assert_true(@native.reduce_request_is_valid(request))
assert_eq(@native.reduce_request_value_type(request), 1)
assert_eq(@native.reduce_request_kernel(request), 2)
let scan = @plan.scan("prefix", @plan.i32_type(), 8, policy~)
debug_inspect(@native.map_request_from_plan(scan), content="Err(UnsupportedMode)")
}
生の外部関数
これらの extern "C" 宣言は公開されており、型付きの関数がラップしていないカーネル、たとえば Int64 のカーネルや最小値・最大値のリダクションを呼び出せます。配列は呼び出しの間だけ借用されます。マップとスキャンはステータスコードを返して output に書き込みます。output には少なくとも length 個の要素が必要です。リダクションは結果を返し、失敗時は 0 を返します。引数は入力、その長さ、ワーカー数、チャンクサイズの順です。
ffi_execute_map_i32 と ffi_execute_map_i64
すべての要素を 2 倍にして output に書き込みます。
pub fn ffi_execute_map_i32(FixedArray[Int], Int, Int, Int, FixedArray[Int]) -> Int
pub fn ffi_execute_map_i64(FixedArray[Int64], Int, Int, Int, FixedArray[Int64]) -> Int
ffi_execute_reduce_sum_i32、ffi_execute_reduce_sum_i64、ffi_execute_reduce_min_i32、ffi_execute_reduce_min_i64、ffi_execute_reduce_max_i32、ffi_execute_reduce_max_i64
入力の総和、最小値、または最大値を返します。
pub fn ffi_execute_reduce_sum_i32(FixedArray[Int], Int, Int, Int) -> Int
pub fn ffi_execute_reduce_sum_i64(FixedArray[Int64], Int, Int, Int) -> Int64
pub fn ffi_execute_reduce_min_i32(FixedArray[Int], Int, Int, Int) -> Int
pub fn ffi_execute_reduce_min_i64(FixedArray[Int64], Int, Int, Int) -> Int64
pub fn ffi_execute_reduce_max_i32(FixedArray[Int], Int, Int, Int) -> Int
pub fn ffi_execute_reduce_max_i64(FixedArray[Int64], Int, Int, Int) -> Int64
最小値と最大値はオーバーフローしません。
ffi_execute_scan_sum_i32 と ffi_execute_scan_sum_i64
包含的な累積和を output に書き込みます。
pub fn ffi_execute_scan_sum_i32(FixedArray[Int], Int, Int, Int, FixedArray[Int]) -> Int
pub fn ffi_execute_scan_sum_i64(FixedArray[Int64], Int, Int, Int, FixedArray[Int64]) -> Int
ffi_submit_workflow_async
平坦な配列からワークフローを開始します。引数はワーカー数、ケイパビリティの id・種類コード・個数、ノードの id・種類コード・ケイパビリティ id(なければ -1)・個数、エッジの始点・終点・種類コード・個数です。ランタイムがグラフを拒否するとヌルハンドルを返します。
pub fn ffi_submit_workflow_async(Int, FixedArray[Int], FixedArray[Int], Int, FixedArray[Int], FixedArray[Int], FixedArray[Int], Int, FixedArray[Int], FixedArray[Int], FixedArray[Int], Int) -> NativeWorkflowHandle
ffi_workflow_poll_state、ffi_workflow_poll_status、ffi_workflow_poll_completed_nodes、ffi_workflow_poll_failed_node_id、ffi_workflow_wait_status、ffi_workflow_destroy
ワークフローのスナップショットのフィールドを 1 つ読む、待って最終ステータスを返す、またはワークフローを破棄します。
pub fn ffi_workflow_poll_state(NativeWorkflowHandle) -> Int
pub fn ffi_workflow_poll_status(NativeWorkflowHandle) -> Int
pub fn ffi_workflow_poll_completed_nodes(NativeWorkflowHandle) -> Int
pub fn ffi_workflow_poll_failed_node_id(NativeWorkflowHandle) -> Int
pub fn ffi_workflow_wait_status(NativeWorkflowHandle) -> Int
pub fn ffi_workflow_destroy(NativeWorkflowHandle) -> Unit
ヌルハンドルに対しては、ポーリング関数は 0 を、ffi_workflow_wait_status は 7 を返し、ffi_workflow_destroy は何もしません。
test "raw kernels" {
let input : FixedArray[Int64] = [5L, -2L, 7L, 0L]
assert_eq(@native.ffi_execute_reduce_min_i64(input, 4, 2, 2), -2L)
assert_eq(@native.ffi_execute_reduce_max_i64(input, 4, 2, 2), 7L)
let output : FixedArray[Int64] = FixedArray::make(4, 0L)
assert_eq(@native.ffi_execute_scan_sum_i64(input, 4, 2, 2, output), 0)
debug_inspect(output, content="<FixedArray: [5, 3, 10, 10]>")
}
等価性
T::equal
構造的な等価性で、NativeMapRequest、NativeReduceRequest、NativeScanRequest、NativeReductionKernel、NativeRequestError、NativeValueType、WorkflowResult、WorkflowRuntimeState のメソッドとして昇格されています。== と != を使ってください。WorkflowHandle には等価性がありません。
pub fn WorkflowResult::equal(Self, Self) -> Bool