查看原文
其他

开发者说|如何优化自动驾驶系统的性能

王方浩 Apollo开发者社区 2022-07-29


下面是由社区开发者—王方浩提供的文章,本文主要介绍如何优化自动驾驶系统的性能。


    ENJOY THE FOLLOWING  


主要从以下几个方面来优化自动驾驶系统,使得它更加稳定,同时又能够能保证任务的实时性。以下几个技术都是目前Apollo Cyber中采用的技术:
  • 线程调度

  • Cgroups

  • CPU亲和性

  • 中断绑定

  • Linux性能优化

  • perf安装

  • 火焰图



由于Linux操作系统提供了控制线程的API接口,Cyber通过系统提供的API对进程的优先级和使用的资源进行调节。首先Cyber将要求比较高的进程设置为实时进程,Linux操作系统中实时进程的优先级最高,实时进程可以抢占其它线程,好处是能够保证实时进程在一定的时间内返回结果,这在自动驾驶控制系统中非常关键,试想一下如果需要发送一条指令给汽车,系统没有在规定的时间内响应,或者响应有延迟,就有可能导致车祸。

Linux对实时进程的调度有2种方式:

  • SCHED_FIFO - 先到的进程优先执行,后到的进程需要等之前的进程执行完成之后再开始执行;

  • SCHED_RR - 基于时间片轮转,先到的进程执行完成之后放到队列尾部,在队列中循环执行。

基于FIFO方式的平均等待时间和进程的顺序有关系,如果先到的进程执行时间很长,那么后到的进程等待时间就会变长;如果先到进程的执行时间很短,那么后到进程的等待时间就会变短。当然基于时间片轮转的方式就没有这个缺点,但是先到进程的执行时间会变长,因为基于轮转的,需要循环队列执行,那么先到进程需要等待其它进程的执行。所以需要根据不同的场景来选择不同的调度策略。


Cgroups,名称源自控制组群(Control Groups)的简写,是Linux内核的一个功能,用来限制、控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。


Cgroups的一个设计目标是为不同的应用情况提供统一的接口,从控制单一进程(像Nice)到操作系统层虚拟化(像OpenVZ、Linux-VServer、LXC)。


Cgroups提供:

  • 资源限制:组可以被设置不超过设定的内存限制;这也包括虚拟内存;

  • 优先级:一些组可能会得到大量的CPU或磁盘IO吞吐量;

  • 结算:用来衡量系统确实把多少资源用到适合的目的上;

  • 控制:冻结组或检查点和重启动。

利用Cgroups技术,可以设置这一组进程的优先级,并且根据重要程度和进程类型分配不同的资源。例如给重要的进程组分配更多的CPU和内存,限制其他进程组的CPU和内存防止其影响系统性能等。



CPU亲和性又叫Processor affinity或CPU pinning。现在的CPU都是多核心的,比如Apollo推荐的计算单元就是4核8线程,多核心CPU的好处是可以同时执行多个任务。现在假设多核CPU有以下场景,一个核上的任务很多,而另外的核心都是空闲状态,那么就会出现一个核累死,而其他的核都在等待的状态,这时候操作系统就想到了一种技术来解决这个问题,即CPU的负载均衡,当一个CPU核心上的任务很多,而其他CPU是空闲状态的时候,操作系统会把这个核上的任务迁移到其他核心,这样多核CPU的利用率就上来了。


对整个系统来说,CPU负载均衡是一个好技术,但是对单个线程来说,就不是那么好了。线程迁移会导致额外的开销,比如当前的CACHE需要重新刷新,而且把重要的任务绑定到单独的核心上,可以保证这个任务的高效执行而不被打断。


Linux操作系统中通过"sched_setaffinity" API来设置线程的CPU亲和性,通过"sched_getaffinity"来获取线程的CPU亲和性。

#define _GNU_SOURCE /* See feature_test_macros(7) */#include <sched.h>
int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);


中断绑定又叫smp_affinity,通过"cat /proc/interrupts"可以列出系统中每个 I/O 设备中每个 CPU 的中断数、处理的中断数、中断类型,以及注册为接收中断的驱动程序列表。系统通过"smp_affinity"可以指定多核CPU是否会响应这个中断,这在频繁有中断的系统中相当有用,比如CAN总线会频繁通过中断来传递传感器消息,如果没有绑定中断,那么系统中每个核心都可能被打断,如果这个核心上有任务在运行,那么CPU就会打断当前任务的执行,而去处理中断程序,从而带来中断上下文切换开销。如果我们把中断绑定到一个单独的核心上,让这个CPU核心去处理中断,而其它CPU核心则不会被频繁打断。


smp_affinity 的默认值为 f,即可为系统中任意 CPU 提供 IRQ。将这个值设定为 1,如下,即表示只有 CPU 0 可以提供这个中断:

# echo 1 >/proc/irq/32/smp_affinity# cat /proc/irq/32/smp_affinity1


Linux操作系统的"perf"命令可以采样一段时间内的系统调用,保存成文件之后再结合火焰图,可以查看当前系统各个进程对CPU的使用情况,火焰图中的横轴代表了CPU占用时间的比例,宽度越宽,代表该进程越耗时。火焰图的横轴是当前进程的调用栈,可以逐级查看每个调用栈和具体的耗时。



ubuntu下执行如下命令安装perf:

apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`


安装成功之后可以执行"perf"命令来采样系统进程调用:

// 先找到需要统计的apollo进程sudo ps -ef | grep apollo
// 查看到进行号之后,用进程号替换下面的PID,进行采样,采样频率为99HZ,采样时间为120秒sudo perf record -F 99 -p PID -g -- sleep 120
// 输出perf文件sudo perf script > out.perf

上述步骤就完成了对Apollo进程的采样,并且输出了采样文件,下面通过生成火焰图来分析进程的调用状况。火焰图采用开源工具"FlameGraph",执行如下命令:

// 下载FlameGraph项目git clone --depth 1 brendangregg/FlameGraph
// 折叠调用栈FlameGraph/stackcollapse-perf.pl out.perf > out.folded
// 生成火焰图FlameGraph/flamegraph.pl out.folded > out.svg

最后把生成的"out.svg"文件在浏览器中打开,就可以点击并且查看对应的调用时间和调用栈,用以分析系统耗时。火焰图如下,源文件在github上下载,链接如下:

https://github.com/daohu527/Dig-into-Apollo/blob/main/performance/Gregg4.svg


图片引用自阮一峰《如何读懂火焰图?》



*自动驾驶性能优化

https://zhuanlan.zhihu.com/p/63125847


以上是"如何优化自动驾驶系统的性能"的全部内容,更多话题讨论、技术交流可以扫描下方二维码添加『Apollo小哥哥』为好友,进开发者交流群。
 


©️著作权归作者所有,如需转载,请注明出处,否则将追究法律责任。




您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存