姜 悦,褚厚斌,张丽晔,蔡斐华,李 智
(中国运载火箭技术研究院,北京 100076)
随着计算机多核处理器的迅速普及,软件架构已经逐渐从传统的串行模式转变为并行模式,以充分发挥多核处理器的效率和潜能。多核计算机拥有强大的处理能力,在并行应用领域有着广阔的应用前景,多线程编程技术已成为当前重要的软件技术之一。
在航天运载领域,匹配试验结束后,试验数据后处理解析软件需要将保存下来的dat 格式的遥测原码,通过挑帧、十进制转换、公式转换、写入文件等处理方法,转换成物理量值供各系统进行判读评估,并进行受控。普通的单线程数据后处理解析能够按照帧协议的格式将遥测原码解析,并进行文件写入,但数据文件写入迟滞,导致主线程阻塞,效率不高。如何快速、高效、可靠且准确地写入数据文件是后处理解析软件的核心任务。
多线程是现代操作系统中非常重要的概念。多线程即允许同时执行多个线程,对完成并行任务和提升用户体验非常重要。在传统的操作系统中,进程通常指代正在运行的程序的实例,每个进程都有自己的地址空间和一个执行线程,该线程通常叫做主线程[1]。在同一进程环境中可以执行多个线程,一般而言,运行在同一个进程中的多个线程具有相同的地址空间。线程是进程中的实际运作单位,是操作系统分配处理器时间的基础单元,它们彼此合作、交流,共享该进程中的全部系统资源,如处理器时间、内存和变量等[2]。
多线程设计中最重要的一个环节就是识别软件中的可并行部位,并对该部位进行问题分解,即将软件划分为多个独立的任务,并确定这些任务之间的相互依赖关系。
问题分解的方式主要有三种[3]:
(1)任务分解:将不同的行为分配给不同的线程,需要移除任务间的依赖性或使用复制来分离依赖性。此种方式需打破原有程序的逻辑关系,消除任务间的依赖性,适用于任务多且并行度高的问题。
(2)数据流分解:将计算分割成一系列阶段,每个线程在不同的阶段同时工作。此种方式不破坏原有程序的逻辑关系,适用于逻辑性强的问题,但数据流影响程序并行执行能力,易出现线程空闲。
(3)数据分解:分割数据集,多个线程对不同的数据集执行同样的操作。此种方式不破坏原有程序的逻辑关系,适用于数据多且格式规范的问题。
试验数据后处理解析软件通过读取试验过程中存储的遥测原码,根据XML 文件中的协议、帧结构、参数和公式的配置信息,完成数据的解析,并以文本形式存储。后处理解析执行过程如图1 所示,包括读取数据原码、类型判断、获取主帧、帧解析、获取子帧、公式转换、格式处理及文件写入7 个主要模块。
图1 帧数据处理过程图Fig.1 Flow diagram of data processing
(1)读取数据原码
软件首先对文件进行检测,确认文件格式,识别文件类型,然后逐帧读取遥测原码文件。
(2)类型判断
软件对读取的一帧原码帧进行帧头识别和帧类型判断,以确定合理的解析方式。
(3)获取主帧
获取主帧主要从数据流中完成整帧的提取,采用判断帧头和帧长度的方式获取整帧,然后再对帧中的参数进行解析。
(4)帧解析
帧解析功能主要是根据参数的波道位置和占字节数从帧缓存区将参数存储的十六进制数据取出,并根据数据类型转换成十进制。
(5)获取子帧
挑包功能应用于主帧中有嵌入子帧(异步帧)的协议,在完成主帧解析后,继续获取主帧中的嵌入子帧,获取嵌入帧采用判别帧头、帧长度和帧尾的方式获取整帧,对没有帧尾的子帧,采用判别帧头、帧长度和下一帧头的方式获取整帧,该种判别子帧的方式可以过滤掉半帧的情况。
(6)公式转换
遍历帧参数结构中的所有参数,根据参数代号从公式转换库中查询该参数的物理量转换公式,将参数的十进制数转换为物理量值,重新存入参数结构中。在完成公式转换后,将得到由参数物理量值组成的不同解析帧。
(7)格式处理及文件写入
在格式处理及文件写入阶段,软件按照数据处理格式要求,对解析帧完成写入数据的格式转换,以及时间和物理量值的拼接,然后将结果写入参数文件。
在单线程架构下对图1 所示的后处理过程中各模块进行时间插桩,采集各模块的运行时间,分析结果如图2 所示。可以看出,模块①~⑥运行的总耗时与模块⑦运行的总耗时比例约为1 ∶13,同时考虑到模块①~⑥逻辑关系强,并行度低,模块⑦并行度较高,因此将关键因素定位为“格式处理及文件写入”模块,进行多线程算法设计。
图2 单线程架构分析图Fig.2 Analysis of single-thread architecture
考虑到后处理解析具有逻辑性较强,数据较多且格式规范等特点,采用基于数据分解的问题分解方式,对本问题的模型适用度更高,且应用至不同型号时无须重新进行逻辑分析,可扩展性强。因此,本设计采用基于数据分解的后处理多线程设计,即将每个解析帧结构按CPU 核数进行分割,将分割后的数据块分配至不同的CPU 核进行处理,如图3 和图4 所示。
图3 系统设计框图Fig.3 Design diagram of system
图4 多线程后处理解析方法示意图Fig.4 Diagram of multi-thread method for test data post-processing parsing
多线程的执行通常存在着无序性,而试验数据后处理解析过程对参数文件的写入要求严格有序,保持原有的时间序列。因此,在任务分配时需要考虑不能有两个及以上的线程同时向同一参数文件进行写入操作。在任务分配方式设计时采用基于定向和闲置混合的方式,即当分配当前时刻的帧结构片段时,如果上一时刻的该帧结构片段仍在被某个线程进行处理,则采用定向分配原则;否则,采用闲置分配原则。这样既可以保证参数写入的准确度,又可以达到各线程负载均衡。线程调度数据结构如表1 所示。
表1 线程调度数据结构Tab.1 Data structure of thread scheduling
具体规则为:
(1)识别跨包参数:如果同一个参数出现在不同包帧协议中,应予以识别并标识,不能采用多线程方式解析;
按参数个数将一帧解析帧(解析帧数据结构如表2 所示)拆分为独立的等长度的帧结构片段,分配给不同核上的线程,每个线程处理不同的帧结构片段,能够达到将多线程负载尽可能均衡地分配在不同CPU 内核上的目的。
表2 解析帧数据结构Tab.2 Data structure of parsing frame
具体规则为:对于跨解析帧参数所在的解析帧,不进行拆分;对于不含跨解析帧参数的每一类解析帧,将其按参数个数等分为n-1 段,其中n为CPU 核数。采用下取整对参数个数进行拆分,相同解析帧类型只拆分一次,后续不再拆分。
在主线程和子线程之间建立缓存队列,可以实现主线程和子线程并行工作,有效提高主线程和子线程的工作效率。缓存队列采用环形队列结构,数据结构和示意图分别如表3 和图5 所示。相较于普通队列,环形队列能够有效利用资源,避免假满队列的情况出现,空间利用率更高。其方法为:
表3 缓存数据结构Tab.3 Data structure of cache region
图5 环形数组示意图Fig.5 Diagram of annular array
(1)确定环形队列在算法中的使用部位:用于主线程和各子线程之间的共享数据缓存,即各帧及帧片段的解析结果。
(2)完成环形队列数据结构设计,利用head 作为环形数组的头部标识,tail 作为环形数组的尾部标识。
(a)入队:先判断tail 节点的下一个位置是否是head,如果是,就表示队列已满,无法入队;如果不是,就可以入队,即将元素放入tail 的下一个位置,最后将tail 加1(为了能够在tail 增加到数组尾部时能够转回到首部,这里需要将tail 加1 与队列的大小取余再赋值给tail);
(b)出队:先判断tail 和head 是否相等,如果相等,就表示队列已空,无元素可出,不等则表示非空。此时需要取出head 对应位置的元素,然后将head 加1(为了能够在head 增加到数组尾部时能够转回到首部,这里需要将head 加1 与队列的大小取余再赋值给head)。
同时,多线程在设计时需要保证共享数据操作的完整性,即保证在任一时刻,只能有一个线程访问该对象。通过对资源池进行分析,将需要上锁的资源定位于环形队列,在锁定与解锁资源时遵循以下原则:
(1)当更新对象域的时候总是锁定;
(2)当访问可能更新的对象域的时候总是锁定;
(3)当调用其它对象方法的时候总是锁定;
(4)资源使用完毕后及时解锁,释放资源。
在多线程技术中,用户需要根据任务或程序的需要创建线程。创建解析线程分为以下几步:
(1)创建主线程,流程如图6(a)所示;
(2)在程序中为每个核创建一个线程;
(3)主线程和子线程同步执行,子线程听从主线程分配任务,流程如图6(b)所示。
选取某型号试验数据后处理解析软件,分别形成单线程参考版本和多线程测试版本,在单CPU 八核电脑上对多线程功能进行测试,基于单线程运行结果,对多线程性能及结果进行评估,多线程性能测试环境如表4 所示。
表4 多线程性能测试环境Tab.4 Muti-thread performance test environment
经测试,多线程的加速比和CPU 利用率效果对比如表5 所示。可以看出,通过在不同数据源下对多线程模块进行测试,在单CPU 八核电脑上算法加速比不低于49%,CPU 利用率提升不低于260%。
表5 多线程测试结果Tab.5 Muti-thread test results
通过对多线程的遥测原码后处理解析的关键技术进行研究,采用多线程技术优化后处理软件,可得到较显著的性能优化。经多线程设计改进后,该软件具有较高的执行效率,在多核处理器上能够充分发挥计算机性能,达到显著提升试验数据处理效率的目的。