tune API
Luna-Flow/mare_mark/tune 提供自动调优的策略部分:候选空间、预算和留出集、稳健分数、带实际阈值的选择、Pareto 前沿,以及带种子的候选顺序。它不构建也不运行候选;由你的应用测量它们(通常使用 runner),再把数值交给这些函数。参见 tune 设计。
import {
"Luna-Flow/mare_mark/tune",
}
候选
CandidateSpace
CandidateSpace 描述调优器可以尝试的配置。
pub struct CandidateSpace[Candidate] {
enumerate : () -> Array[Candidate]
candidate_id : (Candidate) -> String
valid : (Candidate) -> Bool
neighbors : (Candidate) -> Array[Candidate]
}
pub fn[Candidate] CandidateSpace::new(() -> Array[Candidate], (Candidate) -> String, (Candidate) -> Bool, (Candidate) -> Array[Candidate]) -> Self[Candidate]
enumerate 以确定的顺序列出空间,candidate_id 给出唯一且稳定的 id,valid 在测量之前拒绝违反约束的配置,neighbors 为你自己编写的局部搜索列出邻近配置(本包中没有函数使用它)。
BuiltCandidate
BuiltCandidate 把候选 id 与为它构建的可运行实现配对。
pub struct BuiltCandidate[Prepared, Output, Context] {
candidate_id : String
implementation : @runner.Implementation[Prepared, Output, Context]
kernel_id : String
}
pub fn[Prepared, Output, Context] BuiltCandidate::new(String, @runner.Implementation[Prepared, Output, Context], String) -> Self[Prepared, Output, Context]
BuildEvent
BuildEvent 记录候选是否被接受,以及未被接受的原因。
pub struct BuildEvent {
candidate_id : String
kernel_id : String
accepted : Bool
reason : String
}
对被接受的候选,reason 为 "";对被 valid 拒绝的候选为 "constraint";对分数不可用的候选为 "measurement"。
预算与目标
TuningBudget
TuningBudget 限定一次调优运行。
pub struct TuningBudget[Scale] {
max_candidates : Int
max_measurements : Int
max_elapsed_us : Double?
exploration_samples : Int
confirmation_samples : Int
finalists : Int
holdout : HoldoutPlan[Scale]
}
pub fn[Scale] TuningBudget::new(Int, Int, Double?, Int, Int, Int, HoldoutPlan[Scale]) -> Self[Scale]
这些字段描述一个两阶段搜索:用 exploration_samples 探索每个候选,用 confirmation_samples 确认最好的 finalists 个候选,并在 holdout 上检查胜者。该记录是供你的调优循环使用的数据;本包中没有函数强制执行它。
HoldoutPlan
HoldoutPlan 指明为检验胜者能否泛化而保留的数据。
pub(all) enum HoldoutPlan[Scale] {
MeasurementHoldout(Int)
DatasetHoldout(Array[Int])
ShapeHoldout(Array[Scale])
WorkloadHoldout(String)
Combined(Array[HoldoutPlan[Scale]])
}
| 构造器 | 保留的数据 |
|---|---|
MeasurementHoldout(n) | 最后 n 次测量 |
DatasetHoldout(ids) | 这些数据集 |
ShapeHoldout(scales) | 这些规模或形状 |
WorkloadHoldout(name) | 一个命名的工作负载 |
Combined(plans) | 上述所有已列出的内容 |
TuningObjective
TuningObjective 说明“最好”的含义。
pub struct TuningObjective {
practical_delta_pct : Double
max_workspace_bytes : UInt64?
minimize_secondary : Bool
}
pub fn TuningObjective::new(Double, UInt64?, Bool) -> Self
与最快者相差在 practical_delta_pct 以内的候选视为持平;持平时按次要指标(最小化或最大化)决出。
分数
CandidateScore
CandidateScore 是一个候选的测量结果。
pub struct CandidateScore {
candidate_id : String
primary : Double
secondary : Double
valid : Bool
}
pub fn CandidateScore::new(String, Double, Double, Bool) -> Self
primary 是要最小化的代价(时间),secondary 是决胜指标(内存、代码大小)。当 valid 为真且两个值都是有限且非负的数时,分数可用;下面的函数会忽略其他分数。
score_samples
score_samples 把计时样本转化为分数:可用样本的中位数。
pub fn score_samples(String, Array[Double], Double) -> CandidateScore
NaN、无穷大或负数的样本会被丢弃。若没有剩余样本,或次要值不是有限非负数,则分数的 valid = false 且 primary = 0.0。
test "median score" {
let score = @tune.score_samples("64x64", [9.0, 1.0, 5.0, -1.0, 3.0], 4096.0)
inspect(score.primary, content="4")
inspect(score.valid, content="true")
}
select_best
select_best 在可用分数中选出胜者。
pub fn select_best(Array[CandidateScore], Double, Bool) -> CandidateScore?
参数:分数、以百分比表示的实际阈值 ,以及是否最小化次要指标。设 为最小的主要值,决赛候选是满足 的分数(当 时,为 的分数)。在它们之中,返回次要值最好的那个,再按最小 id 决出。NaN、无穷大或负的阈值按 0 处理。没有可用分数时返回 None。结果不取决于输入的顺序。
test "fast enough, then small" {
let scores = [
@tune.CandidateScore::new("fastest", 100.0, 900.0, true),
@tune.CandidateScore::new("lean", 103.0, 100.0, true),
@tune.CandidateScore::new("leaner-but-slow", 110.0, 10.0, true),
]
inspect(@tune.select_best(scores, 5.0, true).unwrap().candidate_id, content="lean")
inspect(@tune.select_best(scores, 0.0, true).unwrap().candidate_id, content="fastest")
}
pareto_frontier
pareto_frontier 保留不被任何其他可用分数支配的可用分数。
pub fn pareto_frontier(Array[CandidateScore]) -> Array[CandidateScore]
当 、 且两者之一为严格不等时,分数 支配 (两个指标都取最小化)。结果先按主要值、再按次要值、最后按 id 排序。代价:。
test "Pareto front" {
let front = @tune.pareto_frontier([
@tune.CandidateScore::new("a", 1.0, 9.0, true),
@tune.CandidateScore::new("b", 2.0, 4.0, true),
@tune.CandidateScore::new("c", 3.0, 5.0, true),
@tune.CandidateScore::new("d", 4.0, 1.0, true),
])
debug_inspect(front.map(s => s.candidate_id), content="[\"a\", \"b\", \"d\"]")
}
搜索辅助函数
exhaustive_scores
exhaustive_scores 为候选空间的一个前缀打分,并指出最快的可用候选。
pub fn[Candidate] exhaustive_scores(CandidateSpace[Candidate], Int, (Candidate) -> CandidateScore) -> TuningResult
它取 enumerate() 的前 个候选。每个有效候选由回调打分;无效候选则不打分。策略字符串为主要值最小的可用候选的 "global:<id>"(持平时取第一个),若没有可用候选则为 "global:"。
test "exhaustive search" {
let space = @tune.CandidateSpace::new(
() => [8, 16, 32, 64],
n => "block-" + n.to_string(),
n => n <= 32,
_ => [],
)
let cost = [8.0, 5.0, 6.0, 1.0]
let result = @tune.exhaustive_scores(space, 10, n => {
let index = if n == 8 { 0 } else if n == 16 { 1 } else if n == 32 { 2 } else { 3 }
@tune.CandidateScore::new("block-" + n.to_string(), cost[index], 0.0, true)
})
inspect(result.policy, content="global:block-16")
inspect(result.build_events[3].reason, content="constraint")
}
TuningResult
TuningResult 是 exhaustive_scores 的结果。
pub struct TuningResult {
scores : Array[CandidateScore]
build_events : Array[BuildEvent]
policy : String
}
scores 保存有效候选(无论是否可用)的分数,build_events 为每个被检查的候选保存一个事件。
seeded_order
seeded_order 按候选 id 的带种子哈希对候选进行置换。
pub fn[Candidate] seeded_order(Array[Candidate], UInt64, (Candidate) -> String) -> Array[Candidate]
每个 id 用以种子为密钥的 FNV-1a 进行哈希;候选先按哈希、再按 id 排序。结果取决于 id 集合和种子,而不取决于输入顺序。可取它的一个前缀作为可复现的随机子集。
test "seeded order is input-order independent" {
let a = @tune.seeded_order(["x", "y", "z"], 9UL, s => s)
let b = @tune.seeded_order(["z", "x", "y"], 9UL, s => s)
inspect(a == b, content="true")
}
confirmation_count
confirmation_count 根据测得的不确定度调整确认样本的数量。
pub fn confirmation_count(Int, Double, Int) -> Int
对于 、 和 ,confirmation_count(base, u, budget) 分别为 base 的 1、2、3 倍,并截断到 。u 是你自选的相对不确定度,例如 IQR 除以中位数。
test "more samples when noisy" {
inspect(@tune.confirmation_count(10, 0.01, 100), content="10")
inspect(@tune.confirmation_count(10, 0.1, 100), content="20")
inspect(@tune.confirmation_count(10, 0.5, 25), content="25")
}