面经 | ML Systems | 🔥🔥🔥 | Meta
一共 4 轮,两个 Coding,一个 ML System Design,一个 Behavioral。第一轮起得有点慢,第二轮 Cache 反而最顺。整体比预想的更偏 systems,题目刚开始听着都还好,写着写着就开始往 latency、memory、concurrency 上加条件。
System Design 里有个叫法记得不一定准,应该是 Meta AI 的 next action ranking。反正意思是用户刚做完一件事,系统给几个下一步建议。这个聊得挺久。
Round 1:Coding / Inference Batch
题目大概是做一个 inference request dispatcher。
每个 request 有:
{requestId, modelKey, tokenBudget, arrivalMs, deadlineMs}
要求把同一个 model version 的请求组成 batch,不能超过 token limit,也不能把已经过 deadline 的请求继续发出去。
先按 modelKey 建 queue,batch 满了或者等一小段时间就 dispatch。写到一半开始加 follow-up:
- request 可能乱序到达
- 等待中的 request 可以 cancel
- premium traffic deadline 更短,但是不能把普通请求饿死
- 流量突然上来时 queue 不能无限长
乱序用一个 min-heap 看 deadline,cancel 做 lazy deletion。Priority 那里最开始想直接拆两个 queue,马上被问 normal traffic 一直拿不到资源怎么办。这里停了一下,后来补了 quota / aging。流量太大时也不能假设所有 request 最后都会跑完,得有明确的 reject 规则。
等待时间最开始写成了固定值,后来又被问到大模型和小模型的 batch cost 差很多怎么办,才想到 latency 和 token 这两个限制得分开看。第一轮就是这种感觉:代码没多复杂,但前面写的东西一直要回头改。
Round 2:Coding / Model Shard Cache
第二轮一上来很像 LRU:
Serving worker 会加载
(model, version, shard)对应的模型分片。实现一个有 byte budget 的 memory cache,命中要快,空间不够时淘汰旧 entry,但是正在被 inference request 使用的 shard 不能被删。
用了 hash map + doubly linked list,entry 里除了 key/value 还有 size 和 ref count。被 request 拿走以后先 pin,release 的时候再减。Eviction 从尾部找,但碰到 pinned entry 要跳过。
后面没有在 LRU 细节上停太久,主要聊并发 load 和版本切换:
- 两个 worker 同时 miss 同一个 shard 怎么办
- 下载完成但是 checksum fail 怎么办
- 新 version rollout 时老请求还在跑怎么办
- cache hit rate 变高了,为什么 inference p99 反而可能更差
同一个 shard 被两个 worker 同时 miss,答的是 per-key single-flight。下载中的 entry 至少要有 loading 的状态,checksum 没过不能让另一个 request 把它当成可用的 shard 读走。
版本切换那里,把新流量该走哪个 version,和内存里的旧 version 还在不在用分开讲。Pointer 可以切到新的,但老 shard 还被 request 引用着就不能立刻清。
最后问 cache hit rate 变高,为什么 inference p99 还能变差。这个一开始也觉得有点反直觉,后来想到可能是新 version 的 shard 更大、GPU transfer 更慢,或者 eviction churn 把真正热的 shard 顶掉了。这里问得比较细,感觉主要在看会不会只盯着 hit rate。
这轮聊得比较顺。原来以为就是 LRU,后来更像一个小的 model distribution / rollout deep dive。
Round 3:ML System Design / Next Action Ranking
题目就是给 Meta AI 设计 next action ranking。名字不确定是不是这么叫,反正用户做完一次交互以后,页面会给几个下一步建议,比如 summarize image、draft reply、set reminder,或者打开相关 conversation。要从里面挑几个真的有用的,也不能推一些很离谱的 action。
先确认 useful 怎么定义。Click 不一定代表有用,也可能是误触,所以 completed action、快速 dismiss、后面会不会再用,都比单看 CTR 更靠谱。这个点没有马上放过去,后面一直拿 metric 反问。
画的东西其实很简单:先从 context 里召回一批 action,做一层 eligibility / policy filter,再排个序。后面要是还有时间和 feature,才进更重一点的 ranker。大概是这个意思:
context
-> candidate generation
-> eligibility / policy filter
-> lightweight ranker
-> second-stage ranker
-> safety + freshness check
-> top actions
Feature 提了 conversation state、recency、device capability,还有过去 action completion。训练数据里更在意 completed action;没点不能直接全当 negative,位置也会影响点击。
后面他主要沿着几件事追:
- feature store latency 突然升高怎么办
- offline NDCG 涨了,但是 serving p99 也涨了怎么办
- 新用户没有历史行为怎么办
- CTR 涨了,completed action 和满意度反而掉了怎么办
- 新模型如何跨 region rollout
feature store 突然慢下来,不能一直等,先用 context 和比较简单的规则兜底。新用户没有历史行为,也只能先靠 context 和 population prior。Position bias 那块提了 exploration bucket 和 IPS,但没有展开到特别细。
最后问 aggregate metric 是正的,但一个小 slice 明显变差要不要发。这个不能被平均数盖过去,至少先 shadow 和小流量看着,过了线就停。具体 threshold 没有真的往下算。
模型本身反而没聊特别深,更多是在问目标到底是什么、数据怎么偏、线上慢了怎么处理。这轮很像平时设计一个东西以后被人拿着边界条件一直问。
Round 4:Behavioral / Project Deep Dive
主要围绕一个把 model serving 做快、但可能引入 correctness risk 的项目深挖。
项目讲的是 request coalescing。它能减少重复计算,但一部分 request 可能会复用到过期 feature。面试官一直抓着这个点问:当时为什么敢发、谁不同意、第一次 rollout 到底发生了什么。
当时主要讲到这些:
- 高峰期 serving path 达不到 latency target
- 我负责 proposal、benchmark 和 rollback plan
- 先跑 shadow traffic,再按流量类型拆 metric
- 第一次 rollout 有一个 slice 的 freshness 不太对,所以先暂停
- 后来调整了 policy 才继续放量
也问了如果 researcher 还想多收一点质量数据,但工程这边急着降成本怎么办;还有需求只有一句“把系统变便宜”时怎么定 scope。这两题就按做过的项目聊,没有再上特别多技术细节。
最后顺带问了 AI coding assistant。会拿它看不熟的 code path、整理 hypothesis、补实验草稿;不过复现、看 diff、跑 benchmark,还有 release decision 还是自己来。这个没有追很久。
整体感受
面完最大的感觉是,前面把一个东西讲顺不难,后面被追着问异常、版本和边界时更容易露怯。第一轮就有点这个感觉;第二轮 Cache 因为之前想得多一点,反而舒服很多。