Meta F3 Lightning:拆开发布与执行之后,为什么CPU仍然偏高
Meta广告ML特征框架F3将所有特征编译为一张合并图,共享计算但发布变成全有或全无。F3 Lightning把发布粒度拆到每个特征独立,但早期原型CPU显著上升。团队通过四步优化恢复效率:稳定标识符、单一执行计划、共享内存缓冲、稠密索引映射。其中最隐蔽的发现是:即使去重完成、计算不再重复,内存布局差异仍让CPU高于传统F3——在20,000+特征的规模下,结果存在哪里和存在多少份,与执行多少次计算同样重要。
Meta的广告排序模型每次请求需要提取超过20,000个特征。这些特征并非计算独立的:一个七天点击计数和一个七天展示计数可能读取相同的事件源、应用相同的时间窗口过滤器,而点击率特征则复用两者的结果。Facebook特征框架F3的做法是把所有特征编译成一张有向无环图,编译器将重复节点合并,共享计算只执行一次。这大幅降低了执行成本,但代价是:合并图同时也是发布单元。一个特征定义出错,整张图无法上线,所有其他特征被迫等待。
随着特征数量从最初几百增长到两万,这个问题越来越严重。F3团队做过几轮改进来恢复发布速度,但每次收益都被新落地的特征侵蚀,因为所有特征仍然在合并图中一起发布。F3 Lightning的目标很明确:让每个特征独立发布,同时不丢失F3的计算效率。
最直接的方案是只拆发布。每个特征编译为独立配置,各自验证、各自上线、各自回滚。但早期原型暴露了预期之外的代价:独立配置到达服务进程后各自执行,F3合并图消除的重复读取、过滤和聚合又全部回来了,CPU用量显著上升。问题不是发布粒度而是执行粒度——拆开发布的同时也拆开了共享计算。
F3 Lightning的核心思路是:把共享执行带回来,但不把合并图带回来。具体做法是在服务启动时,将当前所有活跃特征配置中共享的工作整合到一个执行顺序中,而不是在编译时就把所有特征合并成一张图。这分四步完成。
第一步是稳定标识符。要识别两个独立编译的配置中包含相同计算,运行时必须能匹配它们。节点编号做不到,因为编号是各特征图内部的局部序号,同一个七天过滤器在一个配置中可能是节点2,在另一个中是节点7,插入一个节点还会导致后续全部重新编号。F3 Lightning让编译器从计算本身派生标识符:操作类型、参数、以及所有输入的标识符。因为输入的标识符已经反映了它上游的全部工作,这个标识符捕获了产生该结果的整条计算链。相同操作作用于相同输入的特征,即使编译和发布时间相隔数周,也会得到相同的标识符。
第二步是单一执行计划。服务启动时,F3 Lightning对所有活跃特征做拓扑排序,将每个节点放在其依赖节点之后。当多个特征包含相同标识符的节点时,计划只保留一份,所有消费者指向它。这保证了每个计算只执行一次。但计划仍然是图结构,执行器需要逐指针遍历。同一次启动处理将其展平为有序列表,每次请求变成沿列表的单次前向扫描,而不是遍历图。
到这一步,计算已经不再重复,但CPU仍然高于传统F3。团队定位到剩余成本来自内存布局:每个配置仍携带自己的请求时数据容器,中间结果分散在独立内存分配中。按顺序执行计划意味着在不同容器之间跳转,每次跳转都是一次缓存未命中。
第三步是共享内存缓冲。F3 Lightning用每请求一个缓冲替代了各特征独立容器。启动时,每个特征的引用被重绑定到该缓冲中的槽位。这带来三个改善:相邻计划步骤触碰相邻内存,恢复了合并图一直拥有的顺序访问模式;一个独立的就绪向量替代了逐特征的状态追踪,执行器可以检查值是否就绪而无需读取值本身;消费者不再获得自己的副本,共享槽位持有生产者结果的引用,一个被20个特征使用的计算只存储一次。标量值仍然复制,因为复制小值比跟随指针更便宜。
第四步是稠密索引映射。值虽然集中了,但找到它们仍然昂贵:每次访问需要解析标识符——哈希一个键、探测桶、跟随指针。在Meta的服务量级下,这种查找不高效。最终修复将标识符在初始化时映射为连续整数槽位,并重写所有配置中的引用指向这些槽位。读取结果变成对共享缓冲的直接数组索引,请求路径上没有哈希,没有查找表。
四步完成后,F3 Lightning实现了与F3可比的计算效率,同时每个特征可以独立发布。团队报告,同等提取特征到达生产环境的速度约为传统F3的3.6倍,基础设施计算成本没有增加。
但这个项目最反直觉的结论不是加速数字,而是第三步的发现。团队已经消除了所有重复计算,CPU仍然偏高。原因不是执行了多少次计算,而是结果存在哪里、存在多少份。两个设计可以执行完全相同的操作、以完全相同的顺序,但一个把结果分散在多个独立容器中,另一个集中在一个连续缓冲里——后者在缓存行为上完全不同。在现代CPU上,数据的物理位置和副本数量,与计算次数同等重要。这个认识比任何单步优化都更具迁移性:它意味着去重只是效率工程的一层,内存布局是另一层,而后者往往在去重完成之后才暴露出来。