💡应聘技术问题
问题:"C++ 中智能指针(shared_ptr、unique_ptr、weak_ptr)的实现原理是什么?在性能敏感场景下如何权衡使用?"
参考答案:
智能指针的核心是 RAII:构造时获取资源,析构时释放。这个思想贯穿了 C++ 的资源管理。
unique_ptr 独占所有权,禁止拷贝,只允许移动语义。内部实现很简单,就是裸指针加一个删除器(默认 std::default_delete),析构时调用删除器释放资源。因为没有任何引用计数开销,它的性能和裸指针基本一致,适合大多数单一所有权场景。
shared_ptr 共享所有权,通过引用计数管理生命周期。内部有两个指针:一个指向对象本身,一个指向控制块。控制块里存了强引用计数、弱引用计数和删除器。每次拷贝 shared_ptr 时强引用计数 +1,析构时 -1,减到 0 就释放对象。控制块是独立分配的(除非用了 make_shared),这带来了额外的内存开销和缓存不友好问题。
weak_ptr 是 shared_ptr 的"观察者",它持有指向控制块的指针,但不增加强引用计数。调用 lock() 时会检查强引用计数是否大于 0,如果是就返回一个有效的 shared_ptr,否则返回空。典型用途是打破 shared_ptr 的循环引用——比如父子节点互相引用时,父节点用 shared_ptr 指向子节点,子节点用 weak_ptr 指回父节点。
在性能敏感场景下的权衡:
unique_ptr- 能用
unique_ptr 就不用 shared_ptr - 必须共享所有权时,用
make_shared 创建,它能把对象和控制块分配在一起,减少一次内存分配和缓存 miss - 如果对象很大但生命周期明确,可以用
unique_ptr 加裸指针/引用传给其他模块,而不是用 shared_ptr - 在多线程环境下,
shared_ptr 的引用计数操作使用原子操作,有额外开销,如果不需要跨线程共享,考虑用 boost::local_shared_ptr 或在单线程场景下避免它
问题:"如何在 Linux 环境下对 C++ 程序进行性能 Profiling?你用过哪些工具,各有什么适用场景?"
参考答案:
profiling 这件事没有银弹,不同场景下工具的选择差异很大。按场景来说:
CPU 热点分析:perf 是 Linux 下最常用的采样型 profiler。perf record 加 perf report 能快速定位 CPU 热点函数,开销很低,适合线上环境。perf top 可以实时看。火焰图(FlameGraph)是把 perf 的数据可视化,一眼就能看出调用栈的瓶颈在哪。如果觉得 perf 的数据不够直观,gperftools 的 CPU profiler 也值得一试,它的输出可以直接生成调用图。
内存分析:valgrind 的 memcheck 查内存泄漏和非法访问很有效,但会让程序慢 10-50 倍,不适合线上。线上场景用 heaptrack 或 gperftools 的 heap profiler,开销可控,能看出内存分配的调用栈和峰值。AddressSanitizer (ASan) 是编译器插桩方案,检测 use-after-free、buffer overflow 非常快且准,适合测试和 CI 流程。
锁竞争和并发问题:perf lock 能分析内核级的锁竞争。用户态的可以用 ThreadSanitizer (TSan) 检测 data race,也是 clang/gcc 的编译选项。Intel 的 VTune 在分析线程并发和微架构层面很强,不过需要 Intel CPU 才能发挥全部功能。
微架构级(跟这条 JD 的"极度性能调优"比较相关):perf stat 看 IPC、cache miss rate、分支预测失败率等硬件计数器,是判断程序是否"跑满"CPU 的第一步。VTune 的微架构探索(Microarchitecture Exploration)能定位到具体哪行代码导致了 pipeline stall。对于移动端 ARM 芯片,ARM 官方的 Streamline 或高通 Snapdragon Profiler 是常用工具。
另外,benchmark 库(Google Benchmark)配合 perf 做微基准测试,是日常优化迭代的标配。先用 benchmark 建立基线,改代码,再跑 benchmark 验证——这个流程比直接 profile 整个业务逻辑要高效得多。
问题:"深度学习模型推理部署中,常见的优化手段有哪些?请从算子、模型和框架三个层面简述。"
参考答案:
模型推理优化主要盯着两件事:延迟和吞吐。内存占用是副产品,但往往也很关键。三个层面的优化经常互相配合。
算子层面:这是最底层的优化。首先是算子融合,比如把 Conv + BN + ReLU 合并成一个 kernel,避免中间的显存读写。其次是手工编写高性能 kernel,针对特定硬件用 SIMD(移动端 ARM NEON、x86 AVX/SSE)或 GPU 并行(CUDA、OpenCL)做向量化。具体到细节,要关注访存模式——合并内存访问、减少 bank conflict、利用 shared memory 做数据复用。Winograd 和 Im2col + GEMM 是卷积的两种经典实现,Winograd 在 3x3 卷积上能显著减少乘法次数。量化感知训练(QAT)后做 INT8/INT4 定点推理,利用 INT8 DP4A 指令,也是算子层的重点。
模型层面:结构上可以做剪枝(结构化 / 非结构化剪枝)、蒸馏(用大模型教小模型)、神经架构搜索(NAS)。通道剪枝去掉不重要的卷积核,对硬件友好,能直接减小模型大小和计算量。量化方面,PTQ(Post-Training Quantization)成本低,但精度可能下降;QAT 精度保持好但需要重新训练。对于 Transformer 类模型,KV Cache、FlashAttention、GQA/MQA 这些是当下比较热的优化方向。
框架层面:推理引擎的选择很重要——NVIDIA 生态用 TensorRT,移动端用 NCNN / MNN / TFLite,服务端也可以用 ONNX Runtime。框架层面可以做图优化(常量折叠、死代码消除、算子替换),内存规划(显存池复用、layer 间 in-place 计算),以及运行时调度(多 stream 并行、batch 动态组装)。还有个重要决策是模型格式——ONNX 作为中间表示能跨框架,但算子兼容性需要验证;TorchScript 或直接导出 C++ trace 可以减少依赖。
工程上常见的组合拳:先做图优化和算子融合,再上 INT8 量化,最后针对瓶颈算子手写 kernel。每一步用 benchmark 数据驱动,不要凭感觉优化。
问题:"你参与过的最有挑战性的 C++ 项目是什么?你在其中承担了什么角色,遇到了哪些技术难点?"
参考答案:
这道题没有标准答案,考察的是你对自己做过的事情的复盘深度。面试官想听的其实不是项目本身多厉害,而是你面对困难时怎么思考、怎么拆解问题、最后怎么解决的。回答时建议按这个结构来:
- 项目背景一句话带过
- 你的角色说清楚"我负责了哪部分,从设计到实现还是做某个模块的优化"
- 挑 1-2 个技术难点展开讲
- 遇到了什么现象(比如"线上服务 p99 延迟突然飙到 200ms")
- 怎么定位的("用 perf + flamegraph 发现是内存分配器在锁竞争")
- 为什么那个方案是最优的("考虑过方案 A 和方案 B,A 的问题是 XX,B 的问题是对现有代码侵入太大,最终选择了方案 C")
- 结果是什么("p99 降到了 50ms,吞吐提升了 60%")
如果你做过和 AI 推理部署相关的事情,优先讲那个。如果没有,挑一个最能体现你 C++ 功底的项目——最好是涉及性能优化、多线程、系统编程的,而不是纯业务逻辑的 CRUD。
一个常见坑:不要说"我就是按 leader 要求写的",面试官想看到你对技术决策有自己的判断。哪怕当时确实是 leader 定的方案,你至少要能说清楚你为什么认同、你觉得有没有更好的做法。
问题:"假设我们有一个基于 Stable Diffusion 的图像生成模型,目前在 A100 服务器上推理延迟是 2 秒。现在要把它部署到移动端(手机),要求端侧推理延迟控制在 1 秒以内。你会从哪些方面入手?"
参考答案:
这是一个从服务端到移动端的"下放"场景,A100 到手机的算力差距可能在 10-100 倍,所以不能只靠"优化一下",需要系统性地重新考虑整个 pipeline。
第一步:评估可行性。 Stable Diffusion 在 A100 上跑 2 秒不是什么好事——这说明模型可能比较大,或者目前还没有做太多优化。先确认基线:当前用的是 SD 1.5 还是 SDXL?多少步采样?什么 scheduler?分辨率多大?有些参数一调就能砍掉一半时间(比如把采样步数从 50 降到 20-25,换 DPM-Solver 而不是 DDIM)。
第二步:模型侧的大刀。 SD 1.5 的 UNet 有 860M 参数,手机跑全精度是不可能的。需要组合拳:
- 剪枝和蒸馏:用 LCM(Latent Consistency Model)或 SD-Turbo 这类蒸馏方案,可以把采样步数降到 1-4 步,延迟直接按比例下降
- 量化:FP16 是基础,W8A8 甚至 W4A8 量化配合 QAT 能把模型缩小 4-8 倍。移动端 GPU 对 FP16 和部分 INT8 有硬件加速
- 换更小的 backbone:如果业务允许,可以考虑 Tiny AutoEncoder 替代原版 VAE,或者用轻量 UNet 架构
第三步:推理框架和算子优化。
- 移动端首选 NCNN 或 MNN,这两个框架对高通 Adreno、ARM Mali GPU 都有针对性优化
- 关键算子的手工调优:Attention 模块在移动端 GPU 上用 Winograd 或手工 tiling,Softmax 用 online safe 算法减少数值溢出
- 内存管理:手机显存通常是 2-4GB 与系统共享,要仔细规划中间 tensor 的生命周期,用内存池避免碎片
第四步:工程上的取舍。
- 如果 1 秒实在达不到,考虑异步生成——先出低分辨率预览(比如用 LCM 1 步),后台继续跑高清
- 热点模型放常驻内存,冷启动延迟和内存占用之间做权衡
- 利用手机的 NPU/DSP 做部分算子的卸载,虽然编程复杂度高,但能效比 GPU 好很多
现实预期: SD 在手机上跑进 1 秒,目前即使做大量优化也很有挑战。实际面试中,你能把上面这些方向有条理地说出来,体现出对不同优化手段的 tradeoff 有理解,就已经是很好的回答了。如果能补充一句"我会先用 perf 和 benchmark 找到当前瓶颈,量化出每个优化项的预期收益,按投入产出比排序来推进",说明你还有工程化思维。
🎯应聘面试准备
问:想应聘上述岗位,需要做哪些准备?
答:
简历优化
1.核心信息前置
- 学历背景: 本科及以上在读,计算机、人工智能、数学、电子等相关专业
- 工作经验: 有 C++ 相关项目或实习经验优先(包括课程项目、GitHub 项目、竞赛经历)
- 技术栈: C/C++、性能优化、深度学习推理框架、计算机图形学(可选)
- 意向岗位:
2.匹配岗位关键词
- 技术栈: C++17/20、Python、CMake、Linux 系统编程、多线程编程
- 工程能力: 性能 Profiling(perf/VTune)、内存管理、SIMD 优化、Debug(GDB/LLDB)
- 工具与平台: Git、Docker、CUDA/OpenCL、NCNN/MNN/TensorRT 等推理框架
- 能力标签: AI Native(善用大模型辅助编码)、底层好奇心、跨团队沟通
技能梳理
根据岗位方向,建议从以下几个维度梳理和查漏补缺:
C++ 核心功底(必考):
- C++11/14/17 新特性:智能指针、移动语义、lambda、constexpr、std::optional/variant
- 虚函数表原理、对象内存布局、编译器优化(RVO/NRVO)
- STL 容器的内部实现和复杂度:vector 扩容策略、unordered_map 的 hash 冲突处理
- 多线程:std::thread、mutex、condition_variable、atomic、内存序(memory order)
计算机基础:
- 操作系统:虚拟内存、页表、TLB、上下文切换开销、文件系统
- 计算机组成:CPU cache 层级、分支预测、流水线、SIMD 向量化
- 网络:TCP/UDP 基础,epoll/io_uring,gRPC 或 REST API 的基本理解
性能优化相关(岗位核心卖点):
- 熟练使用 perf 或 VTune 做 CPU profiling
- 了解内存分配器(jemalloc/tcmalloc)的差异
- 理解 false sharing、cache line 对齐、lock-free 数据结构
- 能写出 cache-friendly 的代码——按行遍历二维数组这种基本面试题要能脱口而出
- 有实际 benchmark 驱动优化的经验,哪怕是自己写的小 demo
AI 推理相关(加分但非必须):
- 了解至少一个推理框架的基本用法:NCNN(移动端)或 TensorRT(NVIDIA)
- 量化基本原理:INT8 对称/非对称量化,PTQ vs QAT 的 tradeoff
- 了解常见模型结构(ResNet、Transformer)的计算图长什么样
图形学(AR 方向加分):
- OpenGL ES 基础:shader(GLSL)、VBO/VAO、纹理、帧缓冲
- 如果有 Vulkan/Metal 经验更好,但实习生不要求
- 了解渲染管线和基本图形学概念(投影、光照、深度测试)
面试准备
经典问题
- 手写 shared_ptr 或简化版 vector——考察 C++ 基础扎实程度
- 实现一个线程安全的生产者-消费者队列——考察多线程和锁的理解
- 给定一个内存泄漏场景,描述排查思路——考察 Debug 方法论
- "C++ 和 Python 各适合什么场景?什么情况下你会选 Python 而不是 C++?"——考察工程判断力
系统设计
- 设计一个支持 1000 QPS 的在线模型推理服务,要求 p99 < 100ms。需要考虑负载均衡、模型加载策略、batch 策略和降级兜底
- 设计一个移动端图片处理 pipeline,从相机输入到模型推理到渲染输出。重点在内存管理、多线程调度和 GPU 利用率
- 给定一个现有推理服务的性能瓶颈(比如 GPU 利用率低),如何定位和优化。考察 profiling 思路、算子分析和并行度调优
项目经验准备
- 特别提醒: 这个岗位非常看重你"从底层理解"的能力——你做过的项目不要只讲"用了什么",还要能讲"为什么是这样工作的"。比如你用了 PyTorch 训练模型,那你知道 autograd 的 backward graph 是怎么构建的吗?你用了 NCNN 推理,那你看过它的内存分配是怎么做的吗?