眼谷科技嵌入式软件开发 二面 总结
1.自我介绍
面试官好,我换个角度介绍一下自己。一面主要聊了技术细节,这次我想说说我对嵌入式开发的理解和职业规划。
我选择嵌入式这个方向,是因为喜欢软硬结合、能看到实际产品的感觉。相比纯软件开发,嵌入式更贴近硬件,需要考虑资源限制、实时性、功耗等问题,这种约束反而让我觉得有挑战性。特别是嵌入式AI这个领域,如何在有限的算力和内存下实现智能功能,这个问题很吸引我。
技术上,我主要用C++做开发,熟悉Linux系统和交叉编译,有模型部署的实际经验。在项目中遇到过性能瓶颈、内存不足等问题,通过优化算法、使用硬件加速等方式解决。这些经历让我理解了嵌入式开发的特点:不只是写代码,还要理解硬件、优化性能、权衡资源。
我希望在嵌入式AI方向深耕,眼谷科技在智能视觉领域有很好的技术积累,这是我选择这里的原因。我相信在这个平台上能够快速成长,做出有价值的产品。
2.说说你做过的最有挑战的项目
我做过的最有挑战的项目是在RK3588平台上部署多模型并行推理系统。项目需求是同时运行人脸检测、人脸识别、行为分析三个模型,要求总延迟不超过100ms,而且功耗要控制在合理范围内。
最大的挑战是资源调度。RK3588的NPU有三个核心,但三个模型同时跑会互相竞争资源,导致性能下降。我首先分析了每个模型的计算量和内存占用,发现人脸检测最重,人脸识别次之,行为分析最轻。然后设计了一个调度策略:人脸检测独占一个NPU核心,另外两个模型共享其他核心,通过流水线方式处理。
第二个挑战是内存管理。三个模型加载后内存占用很大,接近系统上限。我做了几个优化:一是使用模型共享权重,减少重复加载;二是实现了动态加载机制,不用的模型及时卸载;三是优化了中间结果的缓存策略,复用内存空间。
第三个挑战是功耗控制。持续高负载运行会导致发热和功耗过高。我实现了动态调频机制,根据负载情况调整NPU频率,在性能和功耗之间平衡。还优化了数据流,减少CPU和NPU之间的数据搬运,降低总线功耗。
最终实现了三个模型并行运行,总延迟85ms,功耗控制在5W以内,满足了产品要求。这个项目让我深刻理解了嵌入式系统的资源约束,也学会了如何在多个目标之间权衡和优化。
3.你对嵌入式AI的理解是什么?未来发展方向如何?
嵌入式AI就是在资源受限的设备上实现智能功能,核心挑战是如何在有限的算力、内存、功耗下达到可用的精度和性能。
技术层面,嵌入式AI涉及几个关键技术。模型压缩包括剪枝、量化、知识蒸馏等,目标是减小模型大小和计算量。硬件加速利用NPU、DSP等专用硬件提升性能。软硬件协同优化,比如算子融合、内存复用,充分发挥硬件能力。
应用层面,嵌入式AI已经在很多领域落地,比如智能摄像头的人脸识别、智能音箱的语音识别、自动驾驶的感知系统、工业检测的缺陷识别等。相比云端AI,嵌入式AI的优势是低延迟、隐私保护、离线可用,但挑战是算力和精度的限制。
未来发展方向,我觉得有几个趋势。一是专用芯片会越来越强,NPU算力持续提升,功耗持续降低,这会让更复杂的模型能够在端侧运行。二是模型会越来越轻量化,像MobileNet、EfficientNet这些专为移动端设计的模型会更普及。三是端云协同会成为主流,简单任务在端侧处理,复杂任务上云,平衡性能和成本。四是多模态融合,结合视觉、语音、传感器等多种数据,实现更智能的应用。
我认为嵌入式AI是AI落地的重要方向,也是我想要深入的领域。眼谷科技在智能视觉方面的积累,正好契合这个方向。
4.如何在嵌入式系统中进行内存优化?
嵌入式系统内存有限,内存优化很关键。我的经验主要有几个方面。
首先是减少内存分配。能用栈就不用堆,栈分配速度快且自动释放。如果必须用堆,使用内存池预分配,避免频繁的malloc/free导致碎片化。对于固定大小的对象,可以用对象池复用。
// 内存池示例
class MemoryPool {
char* pool;
size_t block_size;
std::vector<void*> free_list;
public:
void* allocate() {
if (free_list.empty()) return nullptr;
void* ptr = free_list.back();
free_list.pop_back();
return ptr;
}
void deallocate(void* ptr) {
free_list.push_back(ptr);
}
};
其次是复用内存。AI模型推理时,中间结果的内存可以复用。比如卷积层的输出可以作为下一层的输入,不需要额外分配。使用in-place操作,直接在输入上修改,避免额外内存。
第三是延迟加载和动态卸载。不是所有模块都需要常驻内存,可以按需加载。比如某些功能只在特定场景使用,用的时候加载,不用的时候卸载。
第四是数据压缩。对于大块数据,可以压缩存储,使用时解压。比如图片可以用JPEG压缩,模型权重可以量化为INT8。虽然增加了计算开销,但在内存紧张时是值得的。
第五是优化数据结构。选择内存占用小的数据结构,比如用位域(bitfield)存储布尔值,用数组代替链表(链表有指针开销)。注意内存对齐,避免浪费。
第六是检测内存泄漏。使用Valgrind等工具定期检查,确保所有分配的内存都被释放。在嵌入式系统中,即使很小的泄漏,长时间运行也会导致问题。
实际项目中,我会先用工具分析内存占用,找到大头,针对性优化。比如发现模型权重占了80%内存,就重点优化模型;发现中间结果占用多,就优化内存复用。
5.多线程在嵌入式开发中如何使用?有什么注意事项?
嵌入式系统的多线程使用要特别谨慎,因为资源有限,线程开销相对较大。
首先是线程数量控制。不是线程越多越好,要根据CPU核心数合理分配。一般线程数不超过核心数的2倍,避免过多的上下文切换。在单核系统中,多线程主要用于并发而非并行,要注意不要让CPU密集型任务阻塞IO任务。
// 根据CPU核心数创建线程池 int num_threads = std::thread::hardware_concurrency(); ThreadPool pool(num_threads);
其次是线程同步。使用互斥锁保护共享数据,但要注意锁的粒度,锁太粗会降低并发度,锁太细会增加开销。优先使用无锁数据结构(如原子操作),避免锁竞争。
// 使用原子操作避免锁
std::atomic<int> counter(0);
counter.fetch_add(1); // 原子递增
// 必须用锁时,减小锁粒度
{
std::lock_guard<std::mutex> lock(mtx);
// 只在临界区内操作共享数据
}
第三是避免死锁。统一加锁顺序,使用std::lock同时锁多个互斥量,使用超时锁避免永久等待。在嵌入式实
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

查看14道真题和解析