面灵AI→

苏州睿芯算子开发一面面经:SIMD 尾部处理、RVV 指令与访存瓶颈

轮次
一面
base
苏州
时间
2026-10
来源
牛客网

《面试题目》

开场

  1. 请做一下自我介绍。

其他问题

  1. 你对 SIMD 是怎么理解的?
  2. 如果一个 SIMD 寄存器一次能算 32 个数,而这一批只需要计算 100 个数,占不满这个寄存器,尾部你会怎么处理?
  3. 如果不是乘加,而是取最值(最大 / 最小)的情况,尾部你会怎么做?
  4. 矩阵乘:A 是 128×128,B 也是 128×128,得到 C,需要经历多少次乘加?
  5. 你对汇编的那些指令熟悉吗?
  6. 如果做这种汇编类的工作,会有兴趣吗?
  7. RVV 的一些指令用过吗?
  8. vset / vadd 那类指令用过吗?
  9. 用 RVV 指令写过东西吗?
  10. 内联函数你有了解过吗?
  11. 它有什么好处?
  12. Makefile 里边需要配合内联函数做什么设置吗?
  13. 大模型这块有过了解吗?
  14. softmax 如何防溢出?
  15. 你判断一个算子是计算瓶颈还是访存瓶颈,一般是怎么做的?
  16. 这个判断是用统计工具看的,还是自己插装点看的?有实际测出来的时间吗?细粒度的时间统计有吗?
  17. 访存优化通常有哪些手段?

《参考解析》

SIMD 与尾部处理:填充值必须是该运算的恒等元。 SIMD 是一条指令同时处理多个数据(RVV 里叫向量扩展、x86 上是 SSE/AVX 这类),核心收益是同一份取指与译码摊到多路数据上,代价是要求数据能凑成整齐的向量宽度,所以「一批 100 个、寄存器一次算 32 个」必然遇到余数。常见处理有三种:一是尾部回落标量循环,最好写但最慢;二是掩码 / 谓词(RVV 的 vsetvli 直接设 vl,x86 用 AVX-512 的 mask),一次把尾部一起算、越界 lane 不写回;三是让最后一个完整向量往前重叠一段重算,用冗余计算换掉分支。追问取最值是个陷阱——乘加能用 0 或 1 填充是因为它们是加法和乘法的恒等元,而 max 的恒等元是负无穷、min 是正无穷,拿 0 去填,当整批数据都是负数时结果会错成 0。所以取最值要么走掩码 / 谓词,要么把填充值显式设成该类型的极小或极大值。

矩阵乘的乘加次数:先答数字,再答为什么优化它。 C 的每个元素是一条长度 128 的点积,即 128 次乘加;C 共 128×128 = 16384 个元素,合计 16384 × 128 ≈ 210 万次乘加(2^21 量级)。如果面试官问的是浮点运算量,要区分一次 FMA 折合 2 FLOPs,答案约 4.2 MFLOP,把口径说清楚比报一个数更稳。这题往下接的必然是复杂度与优化:朴素三重循环是 O(N^3) 计算、O(N^2) 访存(还是理想情况),算术强度很低,所以真正的功夫在分块——把 A、B 的块搬进寄存器 / 共享内存 / 片上缓存后反复复用,让访存次数随块尺寸下降,性能上限就由「计算峰值」和「带宽峰值」谁先到顶决定。能顺手提一句分块尺寸受片上存储容量与寄存器数量约束、太小则复用不足、太大则溢出,这题就算答透了。

RVV、汇编与内联函数:这三问筛的是真动手经历。 面试官连着问「熟悉吗 / 用过吗 / 写过吗」,是在探你是读过文档还是真跑过代码,如实回答最省事——没用过就直说没用过,再补一句你知道 RVV 是 RISC-V 的向量扩展、vsetvli 用来设 vl 和 SEW/LMUL、vadd.vv 这类是向量运算、长度无关编程让同一份代码适配不同 VLEN,不要临场硬编指令名。内联函数那组要分清问的是 C 的 inline 还是内联汇编:C 的 inline 只是给编译器的建议,省的是函数调用开销,而且有了函数体编译器才能跨边界做常量传播、向量化这类优化,真想强制内联得靠 __attribute__((always_inline)) 或 __forceinline;内联汇编的好处是能精确控制用哪条指令、哪个寄存器,可读性也比整段 .S 好。Makefile 这一问的落点通常是编译选项要配套——开优化等级、开对应的指令集扩展(如 -march 带上向量扩展)、必要时打开内联相关开关,内联汇编还要把输入输出约束和 clobber 列表写对,否则优化会把它改坏。

瓶颈判断与访存优化、softmax 防溢出。 判断计算瓶颈还是访存瓶颈,标准做法是先算算术强度(每字节访存对应多少次浮点运算),再和机器的计算访存比对比:落在拐点左侧就是访存受限,右侧是计算受限,这类分析用 roofline 模型表达最直接。落到实测上就是看指标而不是猜——GPU 侧用 Nsight Compute 看 SM 利用率、DRAM 带宽、L2 命中率与 stall 原因,CPU 侧用 perf 或 VTune,或者干脆自己插装计时点(CUDA event、clock64、rdtsc)把算子切成几段做细粒度计时。原帖里面试官追着问「是工具看的还是插装看的、有没有实测数字」,说明只讲方法论不够,得有真跑出来的时间分布。访存优化的手段大体是这几类:提高数据复用(分块、寄存器分块)、合并访存并保证对齐与连续、向量化宽加载、用共享内存或片上缓存暂存、软件预取、算子融合减少中间结果的读写往返、调整数据布局(行主序与列主序、NCHW 与 NHWC、AoS 与 SoA),再往下才是 bank conflict、padding 这类细节。softmax 的防溢出是另一道常规题:exp 在输入偏大时会直接冲上溢出,所以要先减去每行(或每个向量)的最大值再做指数,这个变换在数学上等价、不改变结果,只是把指数项的取值压回 0 附近;大规模场景还会用在线 softmax(边扫边更新 running max 与 running sum)避免多趟读数据。