技术文档丨Cyber RT车载系统CPU资源调度和数据与协程任务驱动模型

近日,随着Apollo5.0的升级,Cyber RT再次进行了更新,主要包括三方面的新支持:ARM架构的处理器支持、Python API的全面支持、Cyber RT API文档站的支持。

 

Apollo Cyber RT计算框架的核心功能是计算任务调度和提供底层通信服务,解决了此前ROS框架存在的难题,更加适用于自动驾驶领域。

 

在当前的无人车载运行环境中,为了提高任务并行性,算法模块会创建大量的异步任务,如果任由异步任务抢占CPU资源,会导致如下问题:

 

  1. 传统异步任务采用OS Thread实现,由OS Kernel进行调度,用户空间无法对这些任务上核顺序及执行周期性进行控制,无人车载计算任务实时性无法得到保证。

  2. OS Kernel采用时间分片算法对这些异步任务进行调度,当异步任务增多时,Context Switch开销会被放大。

  3. 大量不相关的异步任务相互抢占CPU资源,造成Cache MissCache Bouncing开销

 

  以下,ENJOY  

 

Cyber RT框架

 

在Cyber RT框架中,调度器将算法异步任务从OS Thread转为Cyber RT Task(协程)在用户空间进行CPU资源调度,同时封装了OS Kernel CPU亲和性及调度策略配置接口,控制OS Thread及OS Process在OS Kernel中的CPU资源调度。形成完备的无人车载系统CPU计算资源管理方案,软件结构如下:

 

▲Cyber RT框架架构图

 

通过架构图可以清晰看到,在Cyber RT框架下:

 

  1. Algorithm Layer共有三种异步任务,分别为OS Process(操作系统进程)OS Thread(操作系统线程)Cyber RT Task(用户空间协程)

  2. Cyber RT Scheduler介于OS&&Hardware与Algorithm Layer之间,负责Cyber RT Task CPU资源调度,以及通过配置OS Process、OS Thread CPU亲和性及Kernel调度策略,来控制它们在OS Kernel中的CPU资源调度。

  3. 大量异步任务由OS Thread转为Cyber RT Task,即协程,由Cyber RT调度器在用户空间统一调度,由于协程上下文切换完全由Cyber RT调度器控制,Cyber RT Task执行顺序、切换完全由Cyber RT调度器决定,无需经过OS Kernel,算法层异步任务实时性周期性得到保证。同时,OS Thread 数量减少,无人车系统中的Context Switch开销大大降低。

  4. 不同OS Process间通过Cyber RT Scheduler控制其CPU亲和性,在CPU资源上相互隔离,大大减少了Cache Miss及Cache Bouncing开销。

  5. OS Thread继承OS Process的CPU亲和性,不相关的OS Thread在CPU资源上天然相互隔离,大大减少了Cache Miss及Cache Bouncing开销。下面,我们就基于车载任务拓扑样例,来阐述如何实施车载CPU资源控制方案。

 

车载CPU资源配置实施方案

 

车载任务拓扑样例

 

▲模拟任务关系图

 

我们仿照车载计算任务,做了如上模拟任务关系图,它相当于我们架构图中的算法层,是我们的调度目标,简单阐述如下:

 

  1. DriverPerceptionPNC分别对应于无人车的驱动、感知、规划控制模块,不同模块之间通过消息传递链接

  2. Driver与Perception模块运行在OS Process1,PNC运行在OS Process2 。

  3. 不同模块均包含Cyber RT Task(用橙框标注),并创建了异步OS Thread(用蓝框标注)。

 

硬件配置

 

CPU结构:4物理核UMA构架。

 

内存:16G。

 

在接下来的配置中,期望达成如下结果:

 

  1. OS  Process1与OS  Prcess2分别各自占用一半CPU资源。

  2. 两个进程中的Cyber RT调度器分别配置CPU亲和性以及Kernel调度策略。

  3. Cyber RT Task根据关联关系分别设置优先级调度策略

  4. 给出OS Thread绑核样例。

 

配置文件命名

 

Cyber RT调度器在程序启动时,会默认按照process_name字段,查找加载调度器配置。在这里,我们命名OS Process1为"compute3d_sched",命名OS Process2为"control_planning_sched",下面论述中,我们以相应进程名来表示两个进程,以便区分。

 

调度器配置文件创建规则为"进程名+.conf",样例如下:

 

1$touch compute3d_sched.conf
2$touch control_planning_sched.conf

注:process_name字段,需要在launch文件中配置,然后由mainboard加载dag文件时,通过-p参数传入。譬如,我们有一个planning.dag,我们可以使  用"mainboard -d planning.dag -p control_planning_sched"来运行dag,同时指定process_name。

 

OS Process配置

 

我们将前两个物理核分配给compute3d_sched,后两个物理核分配给control_planning_sched,配置如下:

 

compute3d_sched.conf:

 

1scheduler_conf {
2process_level_cpuset: "0-1, 4-5" #OS ProcessCPU0415
3}

 

control_planning_sched.conf:

 

1scheduler_conf {
2process_level_cpuset: "2-3, 6-7" #OS ProcessCPU2637
3}

 

调度器配置

 

Cyber RT 调度器有两种调度策略,分别为Choreo与Classic,Choreo策略是Cyber RT下无人车专属调度,它强调任务执行周期性以及实时性。在我们的样例中,我们以Choreo策略配置为例讲解。

 

Classic作为经典调度,不在本篇文章中做过多介绍,读者可以在apollo/cyber/conf的样例配置中找到Classic配置样本。

 

使用样例

 

compute3d_sched.conf:
 
 1scheduler_conf {
 2policy: "choreography"
 3process_level_cpuset: "0-1, 4-5"
 4choreography_conf {
 5choreography_processor_num: 4 #Choreo
 6choreography_affinity: "1to1" #CPU1to1, CPUrangeCPU
 7choreography_cpuset: "0-14-5" #cpu
 8choreography_processor_policy: "SCHED_OTHER" #SCHED_FIFOSCHED_RR
 9SCHED_OTHERThreadkernel
10choreography_processor_prio: -10 #ThreadkernelLinux
11pool_processor_num: 8 #pooltaskpool
12}
13}

 

control_planning_sched.conf:

 

 1scheduler_conf {
 2policy: "choreography"
 3process_level_cpuset: "2-3, 6-7"
 4choreography_conf {
 5choreography_processor_num: 4 #Choreo
 6choreography_affinity: "1to1" #CPU1to1, CPUrangeCPU
 7choreography_cpuset: "2-3, 6-7" #cpu
 8choreography_processor_policy: "SCHED_OTHER" #SCHED_FIFOSCHED_RR
 9SCHED_OTHERThreadkernel
10choreography_processor_prio: -10 #ThreadkernelLinux
11pool_processor_num: 8 #pooltaskpool
12}
13}

 

Cyber RT Task配置

 

在Cyber RT下,我们建议按照如下原则设定Task调度配置: 

 

  1. 相同链路Task尽量绑定到相同Processor上,提高Cache locality

  2. 相同链路Task,子节点Task优先级高于父节点Task,比如感知模块中Recognition优先级要高于Segmentation。

  3. 处于同Processor上Task们,总处理时延最大值不能高于链路处理周期,保证传感器消息处理实时性。在本案例中,为了简洁性,暂不考虑该场景。

  4. 暂时不能进行绑定Processor的Task可以设定优先级然后放入到pool中 。

 

这种配置策略下,越是对整车运行状态影响大的任务往往会更早得到CPU资源,同时,同链路上任务周期性也得到有效保证。这在百度无人车系统上已得到充分验证了。当然,读者可以按照自己对无人车任务的理解去配置任务优先级。

 

使用样例

 

compute3d_sched.conf:

 

 1scheduler_conf {
 2policy: "choreography"
 3process_level_cpuset: "2-3, 6-7"
 4choreography_conf {
 5choreography_processor_num: 4 #Choreo
 6choreography_affinity: "1to1" #CPU1to1, CPUrangeCPU
 7choreography_cpuset: "0-14-5" #cpu
 8choreography_processor_policy: "SCHED_OTHER" #SCHED_FIFOSCHED_RR
 9SCHED_OTHERThreadkernel
10choreography_processor_prio: -10 #ThreadkernelLinux
11pool_processor_num: 8 #poolprocessortask
12tasks: [
13{
14name: "Segmentation" #Task
15prior: 4 #TaskDAGTask
16processor: 0 #TaskProcessorTaskATask
17processor
18}, {
19name: "Recognition"
20prior: 3
21processor: 0
22}, {
23name: "Localization" 
24processor: 1
25}, {
26name: "Gnss" 
27prior: 3 
28}, {
29name: "Lidar"
30prior: 1
31}, {
32name: "Camera"
33prior: 2
34}
35]
36}
37}
control_planning_sched.conf:
 
1scheduler_conf {
 2policy: "choreography"
 3process_level_cpuset: "2-3, 6-7"
 4choreography_conf {
 5choreography_processor_num: 4 #Choreo
 6choreography_affinity: "1to1" #CPU1to1, CPUrangeCPU
 7choreography_cpuset: "2-36-7" #cpu
 8choreography_processor_policy: "SCHED_OTHER" #SCHED_FIFOSCHED_RR
 9SCHED_OTHERThreadkernel
10choreography_processor_prio: -10 #ThreadkernelLinux
11pool_processor_num: 8 #poolprocessortaskpool
12tasks: [
13{
14name: "Canbus" #Task
15prior: 4 #TaskDAGTask
16processor: 0 #TaskProcessorTaskATask
17processor
18}, {
19name: "Control"
20prior: 3
21processor: 0
22}, {
23name: "Planning" 
24processor: 0
25}
26]
27}
28}

OS Thread配置

 

原则上讲,Cyber RT期望算法模块中所有的异步任务均使用Cyber RT Task(协程),这样Cyber RT调度器就可以在用户空间对计算任务进行统一调度, 但因为当前无人车仍处于快速迭代的阶段,算法模块的实现或引入第三方库过程中,总是会使用OS Thread。

 

因此,Cyber RT调度器在架构设计上,提供了OS Thread的CPU亲和性及Kernel调度策略配置入口,用来兼容当前现状。

 

使用样例

 

我们以Driver模块中创建1个线程为例进行讲解,OS Thread绑定过程需要如下两个步骤:

 

第一步,在调度器配置中,加入Thread名称以及CPU亲和性配置。

 

 1scheduler_conf {
 2policy: "choreography"
 3threads: [
 4{
 5name: "ThreadA" #
 6cpuset: "0, 4" #cpu
 7policy: "SCHED_OTHER" #SCHED_FIFOSCHED_RRSCHED_OTHERThreadkernel
 8prio: -10 #ThreadOS kernelLinux
 9}
10]
11}

 

 

第二步,将Thread句柄以及Thread Name传入到Cyber Scheduler,完成线程优先级配置。

 

1std::thread thread = std::thread(&ClassA::Run, this);
2std::string name = "ThreadA";
3auto sched = scheduler::Instance();
4sched->SerInnerThreadAttr(thread, name);

 

Cyber RT数据与协程任务驱动模型

 

▲Cyber RT数据与协程任务驱动模型流程图

 

总结

 

本篇文章,我们介绍了Cyber RT框架在用户空间对算法层异步任务OS ProcessOS ThreadCyberRT Task的CPU计算资源的整体统一控制以及数据与协程任务驱动模型的操作流程。相关详细的示例配置以及实车的调度器配置,我们都可以在apollo/cyber/conf目录下找到。需要进一步说明的是,Cyber RT对车载环境中的系统中断、 kwork的CPU计算资源控制也做了考虑,但这部分需要Root权限,被放到了我们定制的Base OS中,故而没有在当前文章中做出介绍。