文献声明
本文不是 Rust 教程,也不是性能调优指南。
本文是《Rust 量化算子》基础篇第四篇,目标是定义批处理(Batch)在算子系统中的工程语义。
文中代码仅用于验证定义成立,而非教学示例。
作者
yuer
EDCA OS 作者
可控 AI 标准提出者
工程仓库:https://github.com/yuer-dsl
联系邮箱:lipxtk@gmail.com
1. 为什么"批处理 = 循环"是一个工程误解
在大量量化代码中,批处理通常被理解为:
for x in data { operator.apply(x); }
这种写法在工程语义上是错误的。
原因不在性能,而在一致性。
一旦系统同时支持:
-
单条执行(step)
-
批量执行(batch)
-
回放 / 重算(replay)
你就必须回答一个问题:
同一算子,在 step 与 batch 下,结果是否必须一致?
如果答案是"是",
那 batch 绝不能被视为 step 的重复调用。
2. 核心定义:Batch Consistency
本文给出一个不可回避的定义。
Batch Consistency(批处理一致性)
指:
在相同时间语义与输入序列下,
批量执行与逐条执行必须产生等价的状态演化与输出结果。
关键点:
-
等价 ≠ 执行路径相同
-
等价 ≠ 状态更新次数相同
-
等价 = 语义结果一致
3. 为什么 for-loop 破坏一致性
for-loop 模式隐含了多个未声明假设:
-
状态更新是可交换的
-
时间推进是逐条发生的
-
中间状态对最终结果无影响
这些假设在以下场景中失效:
-
窗口算子
-
有 warmup 的算子
-
数值稳定性依赖聚合顺序
-
SIMD / 向量化执行
4. Batch 作为一等执行语义
本文明确:
Batch 是一种执行语义,而不是控制流结构。
因此,Batch 必须被建模为对象。
4.1 定义:BatchInput
pub struct BatchInput<T, Time> { pub values: &[T], pub times: &[Time], }
约束:
-
values 与 times 一一对应
-
顺序必须显式
-
时间语义不因批量而消失
5. BatchOperator:批处理算子接口
在本文中,BatchOperator 被定义为:
能够在单次调用中,对一组输入完成一致性处理的算子。
pub trait BatchOperator<T, Time: TimeIndex> { fn apply_batch( &mut self, input: BatchInput<T, Time>, ) -> OperatorResult; }
重要约束:
-
apply_batch不是apply的循环封装 -
算子内部可以选择最优策略
-
对外必须满足 Batch Consistency
6. 时间语义在 Batch 中不被弱化
本文明确一条硬约束:
Batch 执行不能跳过时间语义。
这意味着:
-
批量 ≠ 同一时间点
-
时间推进规则必须显式
-
WindowedState 的边界必须被尊重
7. 一个最小一致性示例
impl BatchOperator<f64, u64> for MovingSum { fn apply_batch( &mut self, input: BatchInput<f64, u64>, ) -> OperatorResult { for (v, t) in input.values.iter().zip(input.times.iter()) { self.advance_time(*t); self.apply(*v); } Ok(()) } }
这段代码不是推荐实现 ,
而是一个语义锚点:
批处理必须显式对齐时间与状态推进。
8. 向量化不等于语义放松
本文明确否定一个常见误区:
"为了 SIMD / 性能,可以牺牲一致性。"
错误。
正确关系是:
-
向量化是实现策略
-
一致性是语义约束
任何 SIMD / Polars / Arrow 优化,
都必须在 Batch Consistency 之下成立。
9. 不变量与失败边界
本文冻结以下不变量:
-
step 与 batch 语义等价
-
batch 不改变时间语义
-
批处理不隐藏状态演化
刻意不支持:
-
"批量特例行为"
-
batch-only 算子
-
隐式时间合并