文献声明
本文不是 Rust 教程,也不是量化策略教程。
本文是《Rust 量化算子》基础篇第三篇,目标是定义时间(Time)在算子执行模型中的工程语义。
文中代码仅用于验证定义是否成立,而非教学示例。
作者
yuer
EDCA OS 作者
可控 AI 标准提出者
工程仓库:https://github.com/yuer-dsl
联系邮箱:lipxtk@gmail.com
1. 为什么"时间"不能继续被当成索引
在前两篇中,我们已经确定:
-
算子是可调度的执行单元
-
状态是跨执行存在的数据实体
一旦状态存在,一个问题不可避免:
状态是"按什么顺序"演化的?
在大量实现中,默认答案是:
-
行号
-
index
-
数据到达顺序
这种处理方式在以下场景中会系统性失效:
-
数据乱序(out-of-order)
-
回放与重算(replay)
-
批量与逐条混合执行
-
不同数据源对齐
索引不是时间,只是存储顺序。
2. 核心定义:Time Semantics
在本文中,时间语义(Time Semantics) 被定义为:
约束算子执行顺序与状态演化方式的外部语义坐标。
关键点在于:
-
时间不等同于 index
-
时间不由算子内部生成
-
时间影响"何时执行",而非"如何计算"
时间语义明确不等于:
-
行号
-
数组下标
-
批次序号
3. TimeIndex:最小时间表达
本文不试图一次性覆盖 event-time / processing-time 的全部复杂性,
而是引入一个最小可共识对象:
3.1 定义:TimeIndex
TimeIndex 是一个与输入数据绑定的、可比较的时间标识。
最小约束:
-
可比较(PartialOrd / Ord)
-
单调性由外部保证
-
不要求连续
pub trait TimeIndex: Ord + Clone {}
说明:
-
TimeIndex 是语义接口,不是具体时间类型
-
可以是时间戳、序号、逻辑时钟
4. 状态如何绑定时间
一旦引入 TimeIndex,状态不再是"无条件累积"。
本文做出以下定义:
状态的演化必须受时间语义约束。
这意味着:
-
相同输入,在不同时间顺序下,可能产生不同状态
-
时间错序必须是显式问题,而非隐式 bug
5. WindowedState:窗口状态模型(v0.1)
5.1 定义
WindowedState 被定义为:
只对某一时间区间内的数据负责的状态实体。
最小窗口语义包括:
-
固定窗口(fixed)
-
滑动窗口(sliding)
-
滚动窗口(rolling)
本文不区分实现策略,只定义语义边界。
5.2 最小接口示意
pub trait WindowedState<T, Time: TimeIndex> { fn advance(&mut self, time: Time); fn apply(&mut self, value: T); fn reset(&mut self); }
说明:
-
advance明确时间推进 -
apply只在当前窗口生效 -
不引入 watermark / lateness 处理
6. 一个最小时间感知示例
下面示例仅用于说明:
时间改变状态演化路径。
pub struct SimpleWindow { sum: f64, } impl WindowedState<f64, u64> for SimpleWindow { fn advance(&mut self, _time: u64) {} fn apply(&mut self, value: f64) { self.sum += value; } fn reset(&mut self) { self.sum = 0.0; } }
这个实现并不完整,但明确了一点:
状态更新不再只依赖"被调用",
而依赖"在什么时间被调用"。