20 CPU占用与内存占用

20.1 概述

本文说明 ag_driver 对 CPU 和内存的使用。

请先阅读《ag_driver的线程模型与接口设计》,因为本文引用了其中的概念。

../../_images/20_01_components_and_threads.png

20.2 CPU占用

20.2.1 CPU占用的来源

ag_driver 的 CPU 占用主要来自两个方面:

  • MSOP/DIFOP Packet 的处理线程 process_thread 解析 MSOP/DIFOP Packet,构建点云。

  • process_thread 等待 MSOP/DIFOP Packet 时,反复被 唤醒 -> 睡眠 -> 唤醒。这个过程 CPU 占用率较高,原因是 Packet 是频率高但不连续的,process_thread 切换状态的次数太多了。

20.2.2 降低 CPU 占用率的办法

CMake 编译宏 ENABLE_WAIT_IF_QUEUE_EMPTY 可以让 ag_driver 的 CPU 占用率降低一点,但是弊端是点云延迟会加大。

它的原理是让 process_thread 每次睡眠的时间长一点,这样每次唤醒时处理的包数就多一些,反复 唤醒 -> 睡眠 -> 唤醒 的次数就少一些。

option(ENABLE_WAIT_IF_QUEUE_EMPTY "Enable waiting for a while in handle thread if the queue is empty" OFF)

打开这个宏后,Packet 处理线程会调用 usleep(),而不是等待条件变量通知。

在某平台的测试结果表明,这个宏可以将 CPU 占用率从 25% 降低到 22%。

#ifdef ENABLE_WAIT_IF_QUEUE_EMPTY
  
    ...

    std::this_thread::sleep_for(std::chrono::microseconds(1000));
    return value;
#else

    ...
    
    std::unique_lock<std::mutex> ul(mtx_);
    cv_.wait_for(ul, std::chrono::microseconds(usec), [this] { return (!queue_.empty()); });

    ...
    
    return value;
#endif

这里也有必要说明这样做的弊端。

对于 usleep() 的等待时间是不确定的。如果等待跨过了点云的分帧点,比如

  • MEMS 雷达,比如 A0 的 MSOP Packet 序列号跨过了 1344。

这样分帧就会拖后,ag_driver 的使用者“看”到这一帧的时间点就会推迟。

所以请您根据自己的场景,权衡 CPU 占用和点云延迟之间的利弊,再决定是否使能这个编译宏。

20.3 内存占用

20.3.1 MSOP/DIFOP Packet 队列

ag_driver 的内存占用主要来自 MSOP/DIFOP Packet 队列 free_pkt_queuestuffed_pkt_queue

  • 这两个队列中的 Packet 实例是按需分配的,分配的数量与雷达类型、使用场景相关。

  • ag_driver 的实现中,它不会释放这些实例。如果有偶然的意外发生,导致 Packet 实例的数量异常增多,那么后面它们所占用的内存也不会降低。

20.3.2 点云队列

ag_driver 使用的点云实例是调用者分配、管理的,所以这里不讨论点云占用的内存。

20.3.3 查表方式计算三角函数值

  • 关于 MEMS 雷达,

    • A0 不需要计算三角函数值,也就不需要占用这部分内存。

  • 如果连接多个雷达,ag_driver 的多个实例各有自己的 Trigon 实例。也就是每个实例都占用 432,000 字节。

20.3.4 Socket 接收缓存

一个比较隐蔽的内存占用,是接收 MSOP/DIFOP Packet 的 Socket 的接收缓存。

MSOP Packet 和 DIFOP Packet 一般各有自己接收的 Socket。在丢包的情况下,可能需要增加Socket接收缓存的大小。