backend/native 教程
本教程展示如何直接调用原生后端:运行带类型的内核,通过原始外部函数使用 Int64 内核以及最小值和最大值内核,把计划转换为原生请求,并在 C 调度器上运行工作流。
快速开始
在一个为原生目标构建的包中导入该后端:
import {
"Luna-Flow/luna_thread/backend/native",
}
supported_targets = "native"
用三个工作线程、块大小为二来计算前缀和:
test "native quick start" {
let input : FixedArray[Int] = [2, 4, 6, 8, 10, 12]
debug_inspect(
@native.execute_scan_sum_i32(input, 3, 2),
content="Ok(<FixedArray: [2, 6, 12, 20, 30, 42]>)",
)
}
日常任务
求 Int64 数据的最小值和最大值
原始函数显式接受长度,并直接返回结果:
test "min and max" {
let samples : FixedArray[Int64] = [12L, -7L, 40L, 3L, 0L, 25L]
let n = samples.length()
assert_eq(@native.ffi_execute_reduce_min_i64(samples, n, 2, 3), -7L)
assert_eq(@native.ffi_execute_reduce_max_i64(samples, n, 2, 3), 40L)
}
写入你自己的输出数组
映射和扫描写入你提供的数组,并返回状态码,成功时为 0:
test "output array" {
let input : FixedArray[Int64] = [1L, 2L, 3L, 4L]
let output : FixedArray[Int64] = FixedArray::make(4, 0L)
let status = @native.ffi_execute_map_i64(input, 4, 2, 2, output)
assert_eq(status, 0)
debug_inspect(output, content="<FixedArray: [2, 4, 6, 8]>")
}
把计划转换为请求
*_from_plan 函数检查计划,并把它转换为后端的带类型请求:
test "request from plan" {
let policy = @shared.make_execution_policy(worker_count=2, chunk_size=4).unwrap()
let plan = @plan.scan("prefix", @plan.i64_type(), 8, policy~)
guard @native.scan_request_from_plan(plan) is Ok(request) else {
fail("expected a request")
}
assert_true(@native.scan_request_is_valid(request))
assert_eq(request.element_count, 8)
assert_eq(@native.scan_request_value_type(request), 1)
}
运行工作流
submit_workflow_async 在工作线程上启动图;wait_workflow 阻塞直到它完成。由于 backend/native API 中描述的已知桥接缺陷,这个示例没有编译进文档测试:
let policy = @shared.make_execution_policy(worker_count=2).unwrap()
let graph = @workflow.Workflow::new("fork-join", policy~)
.add_node(@workflow.spawn_node(1, "spawn"))
.add_node(@workflow.join_node(2, "join"))
.add_edge(@workflow.Edge::new(1, 2, @workflow.control_dependency()))
guard @native.submit_workflow_async(graph) is Ok(handle) else { return }
let result = @native.wait_workflow(handle)
@native.drop_workflow(handle)
// result.state is Completed, result.completed_nodes is 2
进阶
内核都接受相同的三个数 、 和 ;原生后端设计推导了它们如何划分输入、为什么成功时结果与 和 无关,以及何时检测到溢出。若要在 MoonBit 之外以 OpenMP 链接 C 运行时,请按架构指南所述用 CMake 构建 native/。
常见陷阱
- 每个内核都需要 、 和 ;否则以
InvalidArgument失败(归约则返回0)。 - 原始函数信任你传入的
length。传入的值大于数组实际长度时,会读到数组末尾之外。 moon构建没有启用 OpenMP:即使supports_openmp()返回true,内核也在调用线程上运行。- 由计划构建的请求带有空缓冲区;目前没有任何东西执行它们。
- 若工作流的节点阻塞后从未被唤醒,它就永远不会结束,
wait_workflow也不会返回。
下一步
backend/native API 列出了每个函数,包括原始函数。backend/native 设计描述了线程和内存模型。core 教程通过门面展示了同样的内核。