
寒武纪2019秋招后端岗笔试一这套题我到现在还记得几个印象特别深的场景。那时候寒武纪刚在AI芯片领域攒起名气做后端的人普遍好奇一家做芯片的公司后端岗笔试到底考什么是会像BAT那样刷一堆LeetCode还是会围绕AI推理服务、工具链、平台开发出题我后来把整套卷子复盘了一遍发现它的考察逻辑其实是“偏系统、重实战、有芯片味儿”跟纯互联网公司的后端笔试有明显区别。这篇文章先把笔试的整体风格和第一部分核心题目拆开讲清楚后面再补齐剩余部分。1. 笔试整体风格与考察范围1.1 寒武纪后端岗在招什么人先说一个很多人会误判的点寒武纪的后端不是只做业务API那套东西。结合当时招聘JD和内部业务方向来看后端岗位主要覆盖两类工作一类是AI工具链和云平台的业务后端比如模型仓库、任务调度、资源管理、用户鉴权这些另一类是偏系统软件的后端比如推理服务的部署框架、算子仓库的版本管理、分布式训练平台的控制面。这两类工作对候选人的要求有一个共同点既要懂常规后端工程又要对底层系统有一定敏感度。所以笔试题目不会只停留在“写个快排”“讲下HTTP状态码”这个层面而是会往系统设计、并发模型、内存管理这些方向延展。我当时做完第一反应是这套题更像是在筛选“能理解芯片工具链的工程师”而不是“只会调框架的CRUD选手”。这个定位直接决定了复习方向——光刷算法题是不够的。1.2 试卷结构与时间分配笔试总共分为四个部分不定项选择题、编程题、系统设计题、简答题。总分100分其中编程题占比最大接近40分选择题和简答题各占20分左右系统设计题20分。考试时间120分钟在线作答支持C、Java、Python三种语言但题目默认偏好C。时间分配上有个明显的坑选择题不定项很耗时间很多选项看着都对实际上是在考边界条件。如果你在前面选择题耗太多时间后面编程题基本写不完。比较合理的节奏是选择题控制在25分钟内编程题留60分钟简答和设计题用35分钟收尾。我那次就是选择题做得太细导致最后一道编程题只写了一半思路上去教训很深。2. 算法与数据结构笔试的硬骨头2.1 三道编程题还原与解题思路编程题一共三道难度阶梯很明显第一题是送分题第二题考基础数据结构的综合应用第三题是压轴题。第一题数组去重并保持相对顺序题目大意是给定一个无序整数数组要求去掉重复元素并保持元素第一次出现时的相对顺序输出。输入规模是10^5级别。这题其实考察的是一个很基础的思路用哈希表记录元素是否出现过遍历一次没出现过的加入结果数组并标记。当时我用C写的#include vector #include unordered_set std::vectorint removeDuplicates(const std::vectorint nums) { std::unordered_setint seen; std::vectorint result; result.reserve(nums.size()); for (int num : nums) { if (seen.find(num) seen.end()) { seen.insert(num); result.push_back(num); } } return result; }复杂度是O(n)空间复杂度也是O(n)。注意C的unordered_set在哈希冲突严重时性能会退化但笔试数据量下没影响。这道题真正的坑在于题目要求保持相对顺序所以你不能先排序再去重排序会破坏相对位置。第二题二叉树的层序遍历变体这题不是简单的层序遍历而是要求按层输出并且奇数层从左到右、偶数层从右到左。也就是常见的锯齿形层序遍历变体。解题思路是使用队列配合层数标记。每层处理完之后根据当前层数的奇偶性决定是否反转该层的结果。我用的方案是#include queue #include vector #include algorithm struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; std::vectorstd::vectorint zigzagLevelOrder(TreeNode* root) { std::vectorstd::vectorint result; if (!root) return result; std::queueTreeNode* q; q.push(root); bool leftToRight true; while (!q.empty()) { int levelSize q.size(); std::vectorint level(levelSize); for (int i 0; i levelSize; i) { TreeNode* node q.front(); q.pop(); int index leftToRight ? i : levelSize - 1 - i; level[index] node-val; if (node-left) q.push(node-left); if (node-right) q.push(node-right); } leftToRight !leftToRight; result.push_back(level); } return result; }这里有个细节值得说不要每层结束再reverse而是根据方向直接计算元素插入的索引位置省一次reverse操作代码也更清晰。面试官如果追问这个优化点能体现你对细节的敏感度。第三题带权区间调度问题变体这道题是压轴题目背景包装成了“多个AI推理任务竞争GPU资源每个任务有开始时间、结束时间和收益要求选择不相交的任务使总收益最大”。本质是带权区间调度经典解法是动态规划先按结束时间排序然后dp[i]表示前i个任务中的最大收益转移方程是dp[i] max(dp[i-1], profit[i] dp[p(i)])其中p(i)表示与第i个任务不冲突的前一个任务的下标。关键点在于求p(i)时要用二分查找否则整体复杂度会变成O(n^2)。我当时写的是#include vector #include algorithm struct Job { int start; int end; int profit; }; int jobScheduling(std::vectorJob jobs) { std::sort(jobs.begin(), jobs.end(), [](const Job a, const Job b) { return a.end b.end; }); int n jobs.size(); std::vectorint dp(n 1, 0); for (int i 1; i n; i) { int l 0, r i - 1; while (l r) { int mid (l r) / 2; if (jobs[mid].end jobs[i - 1].start) { l mid 1; } else { r mid; } } int p l; dp[i] std::max(dp[i - 1], jobs[i - 1].profit dp[p]); } return dp[n]; }注意二分的边界找的是“最后一个结束时间不超过当前任务开始时间”的任务p是那个任务在排序数组中的下标。dp[p]对应前p个任务的最大收益。这个边界如果没想清楚二分很容易写错。2.2 做题顺序与时间管理编程题的做题策略其实比你想的更重要。我的建议是拿到题目先看数据范围快速判断每道题应该用什么复杂度。第一题看到10^5就知道O(n)哈希表没问题第二题看到树的结构就知道基于队列的BFS第三题看到任务数量在10^5级别就要立刻反应过来O(n^2)的DP会超时必须用二分优化。做题顺序也有讲究先做送分题把基础分拿稳再做中等题最后啃压轴题。千万不要在第一题上炫技用花哨的解法浪费时间。我见过有同学第一题用位图去重结果数组元素范围是int位图根本不现实白白浪费了20分钟。3. C与Java核心考点编程语言基础3.1 寒武纪偏爱的语言考点这部分是不定项选择题覆盖面广、题目灵活。根据我回忆和同行讨论寒武纪的语言题有比较明显的偏好C考得多且深Java也有涉及但相对基础Python基本没出。C部分的高频考点集中在智能指针、虚函数与多态、内存布局、STL容器底层实现、左值右值与移动语义。这些方向其实很“系统软件”因为寒武纪的工具链和推理框架基本都是C写的对内存管理能力有硬性要求。举个例子我记得有道题问shared_ptr在循环引用时为什么会导致内存泄漏以及如何解决。这个知识点如果只背结论很容易漏掉“控制块和引用计数的关系”这个关键。shared_ptr管理的不只是资源本身还有一个控制块里面存放引用计数和弱引用计数。循环引用时两个对象各自持有对方的shared_ptr导致引用计数永远不为0析构函数永远不被调用。解决办法是把其中一个方向的引用改成weak_ptrweak_ptr不增加引用计数。3.2 Java部分与易错点Java考的内容相对传统HashMap的底层原理、线程安全的集合类、JVM内存区域划分、类加载机制。这些题对做过Java后端的人来说不算难但有几个细节很容易翻车。比如HashMap的扩容机制负载因子默认0.75扩容后容量变为原来的2倍元素需要重新计算哈希索引。很多人会忽略JDK 1.8之后链表长度超过阈值8时会转成红黑树但如果桶中元素数量低于6又会被转回链表。这个“8转树、6转回”的差异就是常见考点。还有一题考察volatile关键字它保证可见性和有序性但不保证原子性。题目给了四个选项其中一个说“可以用volatile实现线程安全的计数器”这个明显是错的。但如果只背过“volatile不保证原子性”而不知道原因遇到变体还是会懵。原因在于count在字节码层面是读-改-写三步操作volatile只保证了读和写各自的原子性和可见性这三步之间是可以被其他线程插入操作的。3.3 语言基础题的答题策略不定项选择题难在“少选多选都不得分”还是“少选得部分分”寒武纪这套题的规则是漏选得一半分错选不得分。这个规则意味着如果你对一个选项没有绝对把握宁可少选一个也不要冒险多选。我当时就靠这个策略保住了不少分。选择题里有一些明显是凑数的干扰选项比如把标准库的复杂度说错、把某个容器的迭代器失效规则说反。遇到这种情况先在草稿纸上标记“确定对”“确定错”“不确定”三类最后只选确定对的不确定的干脆不选。保守拿分比激进丢分划算得多。4. 操作系统与计算机网络理论必须结合实践4.1 高频考点与真题还原操作系统部分的题目紧跟后端开发的实际场景不是死记硬背的概念题。主要考点集中在进程和线程的区别、死锁产生的四个必要条件、虚拟内存与页面置换算法、进程间通信方式。有一道简答题让我印象很深题目给出了一个多进程服务架构要求分析进程间通信的性能瓶颈并给出优化方案。这题没有标准答案考察的是你对IPC机制的理解深度。我当时写了三个层面第一如果进程间通信用的是共享内存瓶颈可能在于锁竞争可以考虑用无锁队列或者读写锁优化第二如果用的是消息队列瓶颈在于消息的序列化和反序列化开销可以改用共享内存加自旋锁第三如果通信频率极高可以考虑把部分逻辑合并到同一个进程里减少跨进程通信次数。这几种方案分别对应不同的业务场景没有绝对的好坏关键是要能说出每个方案的适用条件和代价。网络部分的考点也很集中TCP三次握手和四次挥手、TIME_WAIT、滑动窗口与拥塞控制、HTTP与HTTPS的区别。有个题目问为什么TCP主动关闭方要进入TIME_WAIT状态并等待2MSL这个题考得很细。答案是第一确保最后一个ACK能够到达对端如果ACK丢失可以让对端重发FIN第二让本连接产生的所有报文段在网络中消失避免旧连接的报文干扰新连接。2MSL的时间正好是报文在网络上存活的最大时间的两倍。这个知识点本身不难但答题时要把两层原因都写完整只写一层会扣分。4.2 简答题的答题框架简答题在笔试里其实是最容易拿分的因为评分标准是“采分点给分”。我在复盘时总结了一个答题框架基本上能覆盖大多数情况先给结论再展开理由最后补充边界条件。比如问“进程和线程的区别”不要上来就列五条区别。先写一句话定义进程是资源分配的最小单位线程是CPU调度的最小单位。然后从资源、切换开销、通信方式、稳定性四个维度展开。最后补充在Linux中线程本质上是轻量级进程通过clone系统调用创建与进程共享地址空间但不共享某些资源。这个结构的好处是即使你对某个知识点记得不够全面结论句也能帮你拿到基础分展开部分又能按踩分点逐步凑分。试卷的评分标准通常会标注“答出关键词即得分”所以简答题的语言要尽量使用术语不要用大白话绕圈子。4.3 系统设计题的实战解法系统设计题的分值有20分对笔试结果的影响很大。寒武纪的题目场景是设计一个AI模型推理服务的任务调度系统要求支持多用户提交推理任务任务有优先级GPU资源有限需要做排队和调度。这道题考察的是知识迁移能力。我当时把类似外卖订单系统的架构思路搬了过来用户请求先经过网关做鉴权和限流然后进入消息队列削峰调度器从队列拉取任务根据任务优先级和GPU资源情况进行调度调度结果通过异步回调通知用户。但光有这个框架还不够题目还要求设计调度策略。我写了两种一种是FIFO配合优先级队列简单但不公平低优先级任务可能长期饥饿另一种是多级反馈队列短任务优先执行长任务逐渐降低优先级兼顾公平和响应时间。最后我建议用多级反馈队列并解释了理由。这题想拿高分还要会画系统架构图。笔试平台支持画图但我当时没画只写文字描述后来复盘发现这个是扣分点。建议后续参加笔试的同学系统设计题一定要画架构图哪怕画得简单也要把模块边界和数据流画清楚。文字描述是给论文看的架构图才是给工程思维看的。5. 备考经验与踩坑记录5.1 我踩过的坑这部分每一条都是我真实踩过或身边同学踩过的写出来给大家提个醒。第一个坑低估了不定项选择的难度。我当时以为选择题嘛选一个最可能的就行结果题目明确写“少选得一半分错选不得分”。这意味着碰到不确定的选项要根据自己的知识边界做出取舍不能靠感觉全选。我前几道题就是因为“感觉都对”全选了结果错了一大片。第二个坑编程题环境不熟。寒武纪的在线笔试平台用的是牛客网系统输入输出有时需要自己处理。我平时刷题习惯用LeetCode的模板到了牛客的ACM模式第一道题就卡在了输入读取上浪费了5分钟。强烈建议提前去牛客网熟悉ACM模式的输入输出写法尤其是C的cin/cout和Java的Scanner/BufferedReader。第三个坑系统设计题没有留够时间。我这套卷子的设计题分值很大但我在前面选择题上耗了太多时间最后设计题只用了15分钟草草写了思路。后来复盘如果我把选择题控制好至少能挤出10分钟给设计题把架构图画出来总分至少能再高一截。第四个坑忽略了业务背景调研。寒武纪做AI芯片后端题目会和AI场景结合比如任务调度、资源池管理、模型版本控制。我在笔试前没查岗位相关的业务资料遇到“GPU资源池”这种场景时只能凭通用后端知识硬套。建议笔试前花半天时间了解公司的主要产品线和后端技术栈这个投入的回报率极高。5.2 一套可以直接用的复习清单如果时间紧张我建议按照下面的优先级来准备性价比从高到低排序编程题三件套哈希表应用、二叉树层序遍历变体、动态规划尤其带二分的区间DP。这三个方向几乎覆盖了寒武纪这类公司编程题的大部分考点。C深度重点智能指针原理、虚函数表、内存对齐、移动语义。这些是芯片公司后端面试的高频区也是最能体现系统能力的地方。操作系统高频块进程线程模型、死锁与并发、页面置换、IPC机制。不要只背定义要能结合场景分析。网络重点块TCP状态机、拥塞控制、HTTPS握手流程。用抓包软件实际看几次连接过程比看十遍书都有用。系统设计模板准备一套通用的后端架构模板网关、队列、调度、存储、回调再针对AI场景准备一个调度器设计。这套组合在笔试和面试里都能复用。我在复习阶段把上面的内容做成了一页笔记考前一周只看笔记不再刷题。很多人觉得考前应该大量刷题但临考前其实应该回归框架按章节回忆知识结构对每个考点能不看书写出三条以上的关键点。如果写不出来说明这个点还需要补。参加这类笔试心态上也要调整好。寒武纪这种公司笔试题目相对偏底层是它的特点也是它的筛选逻辑。如果你平时做惯了Spring Boot增删改查遇到这套题会很不适应但这不代表你能力不行只是还没进入芯片公司的考察语境。提前了解、针对训练效果会明显好过临时抱佛脚。这套题的第一部分就先写到这里。后面还有一场关于数据库、分布式系统、场景设计题的拆分等下一篇再更新。准备参加秋招的同学建议先把这套题的复习思路消化掉尤其是那个带权区间调度题一定要吃透二分边界它在寒武纪这类公司笔试里出现的频率比我预想的高很多。