RK3588 推理优化:多线程、零拷贝与内存管理

RK3588 推理优化:多线程、零拷贝与内存管理
RK3588 推理优化多线程、零拷贝与内存管理模型转好了、量化也做了还能不能更快答案是能。这篇讲 RK3588 上的推理优化技巧NPU 核心调度、零拷贝、多线程流水线、内存管理、CPU/NPU 协同——从单帧推理优化到完整的视频流处理流水线。大家好我是黒漂技术佬。很多人把 RKNN 模型跑起来就完事了但其实还有不少优化空间。同样的模型优化得好和不好吞吐量能差两三倍。这篇讲 RK3588 上推理优化的各种手段NPU 三核调度、零拷贝、预处理加速、多线程流水线、内存优化。一、RK3588 NPU 架构先了解一下硬件才能针对性优化。RK3588 NPU 规格三核 NPU三个独立的 NPU 核心NPU0、NPU1、NPU2总算力6 TOPSINT8支持INT8/INT16/FP16 混合精度内存和 CPU 共享内存通过 DMA 传输三核模式RK3588 的 NPU 有三种运行模式模式说明适用场景单核心只用一个核低负载、单路轻量模型联合模式默认三核一起算一个模型单模型最大性能独立模式三个核各跑各的多路并发、多模型默认是联合模式三个核合力跑一个模型单模型速度最快。如果要同时跑多路或者多个模型可以切到独立模式每个核独立跑总吞吐量更高。二、NPU 调度优化1. 核心模式选择单路推理用默认联合模式三核合力单帧最快。多路并发推理用独立模式每个核跑一路总吞吐更高。# 设置 NPU 核心掩码rknn.init_runtime(core_maskRKNN_NPU_CORE_0_1_2)# 三核联合默认# 或者rknn.init_runtime(core_maskRKNN_NPU_CORE_0)# 只用核02. 多路并发方案比如 3 路摄像头同时推理摄像头1 → NPU0 摄像头2 → NPU1 摄像头3 → NPU2三个模型分别绑定到三个核并行推理总吞吐 单路 × 3。比三个模型抢一个联合模式的 NPU 效率高很多。3. 异步推理RKNN 支持异步推理提交任务后不用等可以去做预处理结果好了再取。// 异步提交rknn_run_async(ctx,NULL);// 做别的事情预处理下一帧// ...// 等结果rknn_wait(ctx);rknn_outputs_get(ctx,...);配合流水线使用隐藏推理延迟。三、零拷贝优化数据拷贝是推理 pipeline 里的隐形开销很多人忽略了。传统流程有拷贝摄像头数据 → CPU内存 → 预处理 → 拷到NPU输入 → NPU推理 → 拷回CPU内存中间几次内存拷贝加起来开销不小。零拷贝思路用 RKNN 的零拷贝接口输入输出直接用物理内存少拷贝几次。RKNN 零拷贝 API// 分配 NPU 可用的物理连续内存void*bufrknn_create_mem(ctx,size);// 直接把数据写进这块内存// ...// 设置输入零拷贝rknn_input inputs[1];inputs[0].index0;inputs[0].bufbuf;inputs[0].sizesize;inputs[0].pass_through1;// 直通模式不做内部拷贝rknn_inputs_set(ctx,1,inputs);pass_through1就是告诉 RKNN 不用再拷一份直接用这块内存。适用场景大输入尺寸1080p 以上高帧率推理多路并发内存带宽紧张能省 10-30% 的端到端时间具体看输入大小。注意零拷贝内存是物理连续的分配有上限别太多用完要释放rknn_destroy_mem(ctx, buf)内存对齐要满足 NPU 要求四、预处理优化预处理缩放、颜色转换有时候比推理本身还慢。方案 1RGA 硬件加速RK3588 有 RGARaster Graphic Acceleration硬件模块专门做图像缩放、裁剪、格式转换。CPU 软件缩放 → RGA 硬件缩放 速度提升 3-5 倍librga 库可以直接调用// RGA 缩放 格式转换rga_info_tsrc,dst;src.fd-1;src.virAddryuv_data;src.width1920;src.height1080;src.formatRK_FORMAT_YCbCr_420_SP;dst.fd-1;dst.virAddrrgb_data;dst.width640;dst.height640;dst.formatRK_FORMAT_RGB_888;c_RkRgaBlit(src,dst,NULL);方案 2把预处理融合进模型归一化、通道转换这些简单操作可以在转 RKNN 时通过 mean/std 配置融合进去推理时自动算不用 CPU 做。rknn.config(mean_values[[0,0,0]],std_values[[255,255,255]],//除以255的归一化融合了 quant_img_RGB2BGRFalse,)方案 3YUV 直接输入如果摄像头输出是 NV12/YUV420可以直接喂给 RKNN不用转 RGBrknn.config(quant_img_RGB2BGRFalse)# 输入直接传 YUV 数据少一次颜色空间转换省时间。预处理时间占比参考方案1920→640 缩放 格式转换CPU 软件OpenCV~15msRGA 硬件~2ms融合进模型只有归一化0ms推理顺带做了差一个数量级。预处理优化的收益可能比优化推理本身还大。五、多线程流水线单帧推理再快端到端也有极限。用流水线把各阶段并行起来吞吐量翻倍。典型的三阶段流水线┌──────────┐ ┌──────────┐ ┌──────────┐ │ 预处理 │ → │ NPU推理 │ → │ 后处理 │ │ (CPU) │ │ (NPU) │ │ (CPU) │ └──────────┘ └──────────┘ └──────────┘三个阶段各占一个线程用队列串起来。没有流水线串行帧1: 预处理→推理→后处理 → 帧2: 预处理→推理→后处理 → ... 总时间 N × (预处理 推理 后处理)有流水线帧1: 预处理 → 推理 → 后处理 帧2: 预处理 → 推理 → 后处理 帧3: 预处理 → 推理 → 后处理 总时间 ≈ 预处理 推理 后处理 (N-1) × max(各阶段时间)理想情况下吞吐量由最慢的阶段决定。实际收益售货柜项目里串行一帧 15ms预处理5 推理7 后处理3流水线稳定 7ms 一帧由推理阶段决定吞吐量提升一倍多队列长度控制队列不能无限长不然延迟会累积。设置最大长度满了就丢旧帧。if(queue.size()MAX_QUEUE_SIZE){// 丢最旧的一帧autooldqueue.front();queue.pop();release(old);}实时视频流处理保证低延迟比保证每帧都处理更重要。六、内存管理优化1. 预分配内存循环复用不要每帧 malloc/free提前分配好循环用// 初始化时分配uint8_t*input_bufrknn_create_mem(ctx,input_size);uint8_t*output_bufrknn_create_mem(ctx,output_size);// 循环里复用while(running){// 写数据到 input_buf// 推理// 读 output_buf// 不用释放下一帧接着用}// 退出时释放rknn_destroy_mem(ctx,input_buf);2. 控制模型数量每个 RKNN 模型实例都占内存。多路同型号的话能不能共享模型看具体 API。一般每个线程一个 context多路就多几个 context内存线性增长。控制好并发路数。3. 及时释放中间结果后处理完就释放输出内存别堆着。4. 大模型考虑内存峰值多模型并发时注意峰值内存会不会爆。RK3588 内存和 CPU 共享系统还要用别把 NPU 内存占满了。七、CPU 亲和性与调度RK3588 是 8 核 CPU4 个 A76 大核 4 个 A55 小核。推理线程绑大核把推理和预处理的线程绑到 A76 大核上性能更稳定// 绑 CPU 核心 4-7A76 大核cpu_set_t cpuset;CPU_ZERO(cpuset);CPU_SET(4,cpuset);CPU_SET(5,cpuset);pthread_setaffinity_np(pthread_self(),sizeof(cpu_set_t),cpuset);不同任务分不同核预处理、后处理大核拉流、IO小核就行NPU 推理不占 CPU 核随便哪个线程发合理分配避免大核被 IO 任务占了。调频策略性能模式下 CPU 和 NPU 频率最高速度最快但功耗大。# 设置性能模式echoperformance/sys/devices/system/cpu/cpu4/cpufreq/scaling_governor功耗敏感的场景可以调省电模式看具体需求。八、性能测量与瓶颈分析优化之前先测瓶颈在哪。各阶段打点计时autot0std::chrono::high_resolution_clock::now();preprocess();autot1std::chrono::high_resolution_clock::now();inference();autot2std::chrono::high_resolution_clock::now();postprocess();autot3std::chrono::high_resolution_clock::now();printf(预处理: %.2fms, 推理: %.2fms, 后处理: %.2fms\n,ms(t1-t0),ms(t2-t1),ms(t3-t2));找到最慢的阶段针对性优化。常见瓶颈预处理最慢上 RGA 硬件加速推理最慢优化模型、量化、NPU 调度后处理最慢优化 NMS 算法、多线程用 rknn 自带的性能统计rknn.init_runtime(targetrk3588,perf_debugTrue)开启性能调试能看到每层的耗时找最慢的层。九、优化效果总结RK3588 上单路 YOLOv8n 640 输入各优化手段的效果阶段端到端耗时说明基础版CPU预处理同步推理~22ms刚跑起来的水平RGA 预处理加速~14ms预处理从 12ms 降到 2ms零拷贝输入~11ms少了两次拷贝三阶段流水线~7ms吞吐量由推理决定NPU 调频性能模式~6ms再快一点从 22ms 优化到 6ms快了 3 倍多。大部分优化都是工程层面的模型本身没变。十、常见坑坑 1RGA 和 NPU 内存不通用RGA 分配的内存不能直接给 NPU 用格式不一样。需要注意内存类型该拷的还是得拷。坑 2多线程共用一个 rknn contextRKNN context 不是线程安全的多线程要每个线程一个 context或者加锁。坑 3零拷贝内存分配失败物理连续内存有限分配太大或太多会失败。提前分配好复用。坑 4流水线队列爆了处理不过来队列越来越长延迟越来越大。一定要有长度限制满了丢帧。坑 5CPU 降频温度高了 CPU/NPU 降频速度忽快忽慢。散热做好或者设置固定频率。十一、本篇小结RK3588 NPU 三核联合模式单模型最快独立模式多路并发吞吐高预处理优化收益很大RGA 硬件缩放比 CPU 快几倍零拷贝减少内存拷贝开销适合大输入高帧率三阶段流水线预处理→推理→后处理吞吐量翻倍内存预分配复用避免每帧分配释放CPU 绑核、调频保证性能稳定先测瓶颈再优化别瞎调下一篇讲多路视频推理架构多个摄像头、多个模型怎么组织多进程还是多线程资源怎么分配。我是黒漂技术佬。