首页/文章/ 详情

自动驾驶仿真测试的多并发测试-进程池

1年前浏览496

大家好,我是李慢慢。

之前的文章,在《自动驾驶仿真|如何基于esmini做规控SIL测试?》文末,还留有挺多问题还未解决,其中一个就是如何进行大规模测试,经过独自摸索,感觉有以下方向可以尝试:

1、硬件方向:使用服务器,更多的CPU算力支持;

2、软件方向:串行+多进程+容器技术;


硬件方向,我这边无法尝试。

软件方向,以下简单以本人目前的修为水平谈谈看法。


所谓行,就是连续性测试,即测试完一个场景紧接着就测试下一个场景。但仅仅是串行测试效率特别地下,比如一个场景测试花费60s,那么10000个场景就要花费60*10000/60/60/24=7天。进行一轮测试要花费7天是肯定满足不了软件的迭代速度的。因此,可以考虑多并发,即多个场景同时开始测试。假如我们的硬件设备的算力水平允许我们同时跑10个场景,那么之前的7天瞬间就能缩短到1天内算完。要实现实现多并发,多线程多进程是可以考虑的技术手段。这种靠叠加线程或者进程的方法,对于esmini这种轻量级的仿真软件应该是很适合的,但对于carla这种比较重的软件,可能同一台设备并不支持启动多次,就要考虑用容器(docker)技术,将软件环境分离开来,用多容器来作为多并发手段了。

后续我将会尝试,使用python的进程池,启动多个进程,基于esmini来多并发的完成规控的SIL测试。但是现在,我需要先学习下python的multiprocessing库提供的池子-Pool,做个简单的demo程序,了解下进程池的基本原理。


正巧此前已经学习过了一些线程和进程的知识:

Python线程threading模块告诉你什么是“多并发”?

python的Thread(线程)和multiprocessing(进程)有什么区别?


此篇“进程池”也正好算是进阶篇了。以下开搞。


1、进程池和多个子进程的区别

当需要创建的⼦进程数量不多时, 可以直接利⽤multiprocessing.Process动态生成多个进程, 但如果要创建很多进程时,⼿动创建的话⼯作量会非常大,此时就可以⽤到multiprocessing模块提供的Pool去创建一个进程池。用进程池管理大量子进程,非常方便。


2、进程池定义

进程池:可以提供指定数量的进程给用户使用,即当有新的请求提交到进程池中时,如果池未满,则会创建一个新的进程用来执行该请求;反之,如果池中的进程数已经达到规定最大值,那么该请求就会等待,只要池中有进程空闲下来,该请求就能得到执行。


3、进程池的创建-异步

multiprocessing.Pool的常⽤函数:

apply_async(func, args, kwds):使⽤⾮阻塞⽅式调⽤func(任务并⾏执⾏),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
apply(func, args, kwds):使⽤阻塞⽅式调⽤func,必须等待上⼀个进程执行完任务后才能执⾏下⼀个进程,了解即可,几乎不用;
close():关闭Pool,使其不再接受新的任务;
terminate():不管任务是否完成,⽴即终⽌;
join():主进程阻塞,等待⼦进程的退出,必须在close或terminate之后使⽤;

初始化Pool时,可以指定⼀个最⼤进程数,当有新的任务提交到Pool中时,如果进程池还没有满,那么就会创建⼀个新的进程⽤来执⾏该任务,但如果进程池已满(池中的进程数已经达到指定的最⼤值),那么该任务就会等待,直到池中有进程结束才会创建新的进程来执⾏。

这是一个demo程序:




















import multiprocessing as mpimport time

def simulation(i):    print(f"simulation'{i}'执行中......, process-name:{mp.current_process().name}, process-pid:{mp.current_process().pid}")    time.sleep(2)    print(f"simulation'{i}'执行完毕......")

if __name__ == '__main__':    # 创建进程池    my_pool = mp.Pool(3)  # Pool(3) 表示创建容量为3个进程的进程池    for i in range(10):        # 使⽤异步⽅式执⾏work任务        my_pool.apply_async(simulation, (i, ))    # 进程池关闭之后不再接受新的请求    my_pool.close()    # 等待po中所有子进程结束,必须放在close()后面, 如果使⽤异步⽅式执⾏work任务,主线程不再等待⼦线程执⾏完毕再退出!    my_pool.join()


截图是上述代码输出:


由于我的进程池只允许了最多3个子进程,因此最开始只有三个进程(无序的)在运行,当有进程中的任务执行完毕后,这个进程又开始接受下一个任务,所以这个进程的name和id都没有发生变化。

后续用emini做大规模测试的话,只需要在simulation()函数中定义esmini运行的场景和同步运行算法程序即可。


4、进程池的创建-同步

另外,我也测试了以下同步进程的效果,简单改了下代码,如下:





















import multiprocessing as mpimport time

def simulation(i):    print(f"simulation'{i}'执行中......, process-name:{mp.current_process().name}, process-pid:{mp.current_process().pid}")    time.sleep(2)    print(f"simulation'{i}'执行完毕......")

if __name__ == '__main__':    # 创建进程池    my_pool = mp.Pool(3)  # Pool(3) 表示创建容量为3个进程的进程池    for i in range(10):        # 利⽤进程池同步执⾏work任务,进程池中的进程会等待上⼀个进程执行完任务后才能执⾏下⼀个进程        my_pool.apply(simulation, (i,))    # 进程池关闭之后不再接受新的请求    my_pool.close()    # 等待po中所有子进程结束,必须放在close()后面, 如果使⽤异步⽅式执⾏work任务,主线程不再等待⼦线程执⾏完毕再退出!    my_pool.join()


运行效果如下:


这种只有等一个进程完成后才能开启下一个进程的方式,感觉太鸡肋了,不就是个串行测试嘛,根本就用不着进程池。


附:测试中,也可以用“ps -a”指令来查看进程的启动情况。


今晚先肝到这里吧,瑞斯拜。

欢迎持续关注哦,“肝”货贼多。

来源:车路慢慢
python自动驾驶
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2023-06-22
最近编辑:1年前
李慢慢
硕士 自动驾驶仿真工程师一枚
获赞 11粉丝 71文章 122课程 0
点赞
收藏
未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习 福利任务 兑换礼品
下载APP
联系我们
帮助与反馈