APP下载

仿真建模工具内存分配优化

2024-08-03乔仕岭刘晨王学松孙林丁光亮

科技创新与应用 2024年22期

摘 要:在一体化仿真建模工具的性能优化时,内存分配优化方面提出内存池式分配方式,该分配方式特采用全局内存池基础实现并加上并行线程内存管理的方法来较好地匹配仿真工具的系统多临时对象和多动态分配内存的应用特点,性能测试表明,该内存池具有良好的空间特性和效率,比传统的操作系统直接分配内存节省约50.7%的分配时间。

关键词:内存池;仿真建模工具;分配优化;管理模块;模块设计

中图分类号:TP391 文献标志码:A 文章编号:2095-2945(2024)22-0046-04

Abstract: When optimizing the performance of the integrated simulation modeling tool, the memory pool allocation method is put forward in the aspect of memory allocation optimization. This allocation method is implemented on the basis of global memory pool and the method of parallel thread memory management is used to better match the application characteristics of multi-temporary objects and multi-dynamic memory allocation of the simulation tool. The performance test shows that the memory pool has good space characteristics and efficiency. It saves about 50.7% of the allocation time than the traditional operating system to allocate memory directly.

Keywords: memory pool; simulation modeling tool; allocation optimization; management module; module design

当前的仿真建模工具是桌面式低代码拖拽式多专业一体化的,系统的可操作性、流畅性、实时性都是系统应用要考虑的因素。在这样架构的仿真建模工具中,存在着大量对象的频繁创建和销毁,这里的运行效率显然已经成为瓶颈。为了保证系统实时性的同时提升工具的综合性能,内存分配管理尤为关键。完全依赖系统内存管理方式难以应对频繁操作对象所带来的内存碎片化问题和效率问题,将影响系统的性能和稳定性。因此设计一个高效的内存管理模块尤为必要。内存池式分配模块能够优化内存管理过程,通过池式的管理能够有效管理临时对象的生命周期,减少操作系统层面带来的内存管理影响。这种方法不仅能减少内存碎片化,提高内存利用率,还能降低操作系统开销,显著提升仿真建模工具的执行效率和实时性。

本论文旨在深入探讨在多专业一体化桌面式低代码仿真建模环境下,设计内存池模块的设计与实现。通过对内存池式管理的设计理念、实现机制以及内存分配性能优化进行研究,旨在为实时或超实时仿真环境系统性能提升提供可行的解决方案。同时,此研究也将探索内存池式管理在减少内存碎片化、提升系统稳定性及执行效率方面的价值。

1 内存布局

进程内存布局分为Code Segment、Data Segment、BSS(Block started by symbol)、Heap和Stack等区,Code Segment是存放进程的机器码,Data Segment是存放已初始化的全局或静态变量和常量数据,BSS是存放未初始化的全局和静态变量,Heap是在程序中可动态操作的内存区域,Stack 是由编译器自动管理,用于函数的形参,局部变量和函数的返回变量等,如图1所示;在程序运行时需要频繁地分配和释放内存,从而引起内存的内部外部碎片化,内部碎片是由于进程访问内存为提高效率引入对齐机制以及系统分配内存大于实际内存而未被有效利用,外部碎片主要是由于对象的生命周期不确定,频繁的分配和释放操作,以及系统管理内存的算法不完善等原因,逐渐会把内存变成大大小小的空洞和裂隙而不可用。

仿真建模工具当构建的系统对象比较少的时候,软件的性能没有明显的变化,当构建的仿真模型操作着几十万个系统对象时,系统的操作性能和计算性能有着很显示的下降,这是因为仿真软件在构建一个仿真系统或计算运行一个仿真系统时,各自存在着不同的生命周期,有些对象还需要频繁的构建和销毁,从而造成仿真软件需要不断的与操作系统交互操作内存需求,因而内存分配效率低下,同时也使得内存极度碎片化,而仿真系统在执行业务时既有操作流畅性要求又有计算实时性需求,在规模建模仿真时,内存的碎片化影响系统可操作性和实时性,成为仿真软件大规模仿真的瓶颈,因此在系统构建一个合理的内存池,接管系统内存分配和对象管理,改善仿真软件的运行效率是非常必要的。

2 内存池设计

当前的开源的内存池库在一定程度上可以解决内存的分配效率,但开源的内存池为了使其具有通用性,在一些方面是做了牺牲,像tcmalloc 设计过于复杂,会引起额外的依赖性和复杂性,使其与仿真工具良好的集成和协作变得困难,仿真工具将变得臃肿而无法发挥内存池的优势提升性能,jemalloc在内存的管理模式上并不是完全符合仿真工具的需求,比如一些特定的对象的释放方式可以通过标记模式重复利用来提高系统性能而非真正的释放对象。因此应结合自研仿真工具的特性,设计一套适合自身需求的内存池是必要的。

2.1 内存池架构设计

内存池采用分页管理模式,按照64 KB每页的大小分配内存页空间,内存池将在每页上连续分配相应的池空间,规避一定的跨页性调度。整体内存池通过哈希桶进行管理,按照系统内存评估所需要的哈希桶数量,键有概率被映射到相同的哈希值,形成哈希冲突。哈希桶可以允许在一个桶中存储多组数据。通过映射函数定位到哈希桶,可以快速定位存储数据位置,从而利用哈希通实现高效的数据访问操作。

哈希桶的索引设计根据64位内存地址字段来确认,将地址分为哈希桶索引段、内存页索引段、内存池索引号三部分,通过移位及与运算来确认映射函数,实现哈希桶的访问,如图2所示。

哈希桶的数据结构关系是通过GlobalHashBucket数组建立全局哈希数组,数组内有指向内存池PoolInfo索引数组指针,PoolInfo内存池数组有指向实际的池地址指针,而实际的内存地址则用MemBlock进行管理,用于实际的地址分配,如图3所示。

为了便于直接高效管理分配内存池,将内存池按16 B的长度对齐分类,内存池中的块大小为Size=[24~215],然后通过一个分配AllocSize映射表,通过查表的O(1)的操作,通够快速找到将所需分配的内存映射到PoolTypeArray数组中的,AllocSize中存放的是所申请内存大小所对应的PoolTypeArray的索引,索引的计算如下:

SizeIndex = ((Size +15) >>4);

PoolTypeArrayIndex= AllocSize [SizeIndex];

PoolTypeArray中存放的是当前不同Size内存池的分配信息,CurActiveArray是当前所能分配的内存,内存申请时直接从当前的CurActiveArray中分配内存;FullPoolArray是已经用完的池,在回收的时候优化从内存空间中回收,如图4所示。

2.2 内存线程池设计

由于仿真系统中存在并行计算模块,在内存池的设计上需要响应并行架构,因此在全局内存池的基础上进行多线程并行管理,每个线程都有自己所管理的内存池单元以此减少内存池的竞争。线程池也是按照点内存池的块类型进行管理,块的大小与PoolTypeArray的相同,通过这种方式每个线程都管理着全局类型的内存池,ThreadBlockArray是线程独立的,通过设计Tls来实现变量分离,ThreadBlockArray中指向MemBlockArray地址,其中Activelist 是可以分配的内存块BlockList,BlockList又指向实际可以分配的内存块,以及线程可分配的内存块数,MemBlockArray中的FullList则指向本线程已经占用的内存块,回收内存时,本线程内优化回收并重复在本线程内利用,如图5所示。

3 内存池的实现

3.1 管理资源初始化

根据操作系统的MEMORYSTATUSEX和SYSTEM_INFO 2个结构体数据,确定进程可以使用的内存空间及分配的内存页面大小,评估所需要的GlobalHashBucket数组大小,并初始GlobalHashBucket中的哈希映射索引函数。通过Tls方法来初步化ThreadBlockArray的独立存储空间,确定线程独立内存池空间,如图6所示。

3.2 内存池初步化与内存申请实现

内存池不主动去开辟池空间,当有内存申请时通过申请驱动内存池空间的初始化。发起内存申请需求时进入内存线程池,按照16 B对齐确认PoolType,查询当前线程内存池下是否有可以分配的内存,如有可以分配的内存,则直接从本线程所管理的内存空间中分配内存给申请者,如没有可以分配的内存则向总的PoolTypeArray中查找当前可以使用的内存池,如有则从池中分配一定量的内存MemBlock到本线程的BlockList所指向的BlockNode下面,用于下次分配使用,同时返回一个MemBlock给申请者,如当前的CurActiveArray中没有可以用的内存池,则先分配一个页面的MemBlock 并通过哈希映射函数将这个Memblock指向Poolinfo,把Poolinfo放入GlobalHashBucket中,然后再回到内存池的内存分配流程,绑定内存到线程的ThreadBlockArray下,分配内存块给申请者。经过以上的流程就实现了内存池初始化、装配、线程内存池内存独占和内存分配给申请者的全部过程,如图7所示。

3.3 释放内存

当需要回收内存时进入内存线程池,通过当前内存块的地址获取当前MemBlock的信息判断回收类型,如是池回收则需MemBlock放入到CurActiveArray所指向的BlockList头部,如是彻底回收判断MemBlock所在的内存池中是否全部内存是无效的,如无效则从ThreadBlockArray中卸载这个PoolInfo指向内存池,然后从PoolTypeArray中卸载PoolInfo,从GlobalHashBucket中卸载 PoolInfo,然后调用系统指令回收内存到操作系统。如这个内存池中的内存有效,则进入池回收流程,只将此块内存回收到当前内存线程池中就结束流程,如图8所示。

除此回收流程外,当系统退出的时候,内存池配合系统对象回收机制,检查是否有内存泄露对象,提升程序可靠性同时内存池在系统退出前将扫描内存池的全部内存状态,调用操作系统指令回收全部内存。

4 成果与性能提升

在配置环境为:【操作系统为64位Win10操作系统,CPU为Intel Core i7-8700 K,内存为16 G*2 DDR】的环境上测试,进行50次内存分配后统计见表1,内存池的分配耗时为系统直接分配的50.7%,比系统直接分配约节省了一半时间,开发内存池式分配系统来代替直接的系统内存分配操作是有意义的。

5 结束语

系统采用内存池时分配技术,优化了仿真工作的操作性能和计算性能,提升了仿真工具的内存健壮性,后续可以再结合内存池增加仿真工具的内池使用统计分析,结合分析数据来开发对象池用于系统进一步提升仿真系统性能,同时也由于系统底层的此类完善架构,也使仿真工具系统具备了良性的迭代能力。

参考文献:

[1] 夏立斌,刘晓宇,姜晓巍,等.基于分布式数据集的并行计算框架内存优化方法[J].计算机工程,2023,49(4):43-51.

[2] 梁月月.CBase数据库内存管理技术研究与实现[D].西安:西安电子科技大学,2021.

[3] 蔺佳敏.基于C~(++)的高并发网络编程的研究[J].电子技术,2020,49(8):36-37.

[4] 杨帆,高国静,张怡锋.并行计算框架的内存优化算法研究[J].信息技术,2020,44(8):132-135,140.

[5] 陈婷婷.面向分布式异构内存池的资源抽象和数据放置策略设计与实现[D].武汉:华中科技大学,2020.

[6] 吴雪松,李志.机载数传设备嵌入式应用软件的内存池设计[J].单片机与嵌入式系统应用,2020,20(3):12-15.

[7] 段宗曜.应用内存池Socket池构建高性能的IOCP服务器[J].中国新通信,2019,21(12):172-173.

[8] 薛楠,李斌,王晓华,等.一种基于机载嵌入式系统内存动态管理方式[J].电脑知识与技术,2019,15(15):281-282.

[9] 张鑫.面向异构内存池的RDMA传输优化机制[D].武汉:华中科技大学,2019.

[10] 许健,于鸿洋.一种Linux多线程应用下内存池的设计与实现[J].电子技术应用,2012,38(11):146-149.

[11] 郭丙轩,张京莉,张志超.基于内存池的空间数据调度算法[J].计算机工程,2008(6):63-64.

第一作者简介:乔仕岭(1982-),男,助理工程师。研究方向为计算机科学与技术。