一种用于流式数据并行处理的内存管理方法技术领域
本发明涉及流式数据并行处理技术领域,具体地说,本发明涉及一种
用于流式数据并行处理的内存管理方法。
背景技术
流式数据处理包括:网络数据包处理(下文中简称为数据包处理)、
视频流处理、文本处理、消息处理等。随着网络基础设施和互联网产业的
蓬勃发展,网络规模和服务复杂度都在不断增长。传统的流式数据处理技
术已经无法满足高速网络带来的性能需求,基于多核处理器的流式数据并
行处理技术已经成为了新的趋势。以数据包处理为例,图1示出了一个通
用的数据包多核并行处理构架。参考图1,在这个数据包多核并行处理构
架中,每个处理器核上都绑定了一个处理线程,所有处理线程共同构成了
一个并行流水线布局。输入的数据包从流水线第一级开始,经过层层处理
和中间调度,最终抵达流水线最后一级。在实际应用中,系统的流水线级
数和每级流水线的线程个数都是可调整的。
另一方面,在并行处理技术领域,如何减少多个处理线程对共享资源
的同时竞争,始终是一个核心问题,以数据包并行处理为代表的流式数据
并行处理也不例外。在数据包并行处理中,最常见的共享资源就是内存。
每个待处理的数据包都会被分配一块内存以存储相关数据,处理完毕后这
块内存将被释放。在多核并行处理环境下,多个线程需要同时进行内存申
请和释放,很容易造成对内存资源的持续竞争。并且,处理线程数目越多
意味着竞争越激烈,造成的竞争开销也越大。对于高吞吐率的数据包处理
系统而言,其每秒都需要处理上百万的数据包,从而带来上百万次的内存
操作。因此,高效的内存管理方法对于提高数据包多核并行处理系统的性
能来说至关重要。
现有技术中,通用内存管理方法主要有全局存储共享类型内存管理方
法和线程局部缓存类型内存管理方法两大类,下面分别介绍。
1、全局存储共享类型内存管理方法
早期的内存管理器都是全局存储共享类型的,例如早期版本Glibc库
中由DougLea实现的内存管理器。在全局存储共享类型内存管理方法下,
内存管理器首先从操作系统管理的堆内存中预申请一块全局存储区域,并
使用某种数据结构进行组织,例如分隔空闲列表。当同时有多个线程执行
内存申请和释放操作时,为了避免发生资源冲突(例如返回了同一个内存
块给不同线程)或破坏内部数据结构(例如内存块链表节点指针错乱),
通常需要使用互斥锁机制来对每个操作进行同步,从而带来内存资源竞争
开销。而数据包多核并行处理中,线程数目通常较多,容易造成激烈的资
源竞争,带来非常大的内存竞争开销。因此,全局共享类型的内存管理器
显然不适合用于以数据包多核并行处理为代表的流式数据并行处理应用
场景。
2、线程局部缓存类型内存管理方法
为了减少全局存储共享类型内存管理方法在多线程并发环境下的竞
争开销,以线程局部缓存为基础的针对多线程优化的内存分配器逐渐成为
了主流,如Hoard内存管理器和开源的TCMalloc内存管理器,以及较新版
本的Glibc库中的内存管理模块ptmalloc等。这些内存管理方案的基本原
理都是加入线程局部缓存,即内存管理器除了维护一块全局堆内存以外,
还会为应用程序的每个线程单独维护一块相关联的线程局部缓存。
当应用程序申请内存时,内存管理器会首先检查申请内存的线程相关
联的局部缓存,若不为空则从局部缓存中直接获取,否则才从全局堆内存
中获取。由于是从自己的局部缓存中取,因此通常情况下都不需要进行加
锁。在这种设计下,只要大部分的内存申请都是通过局部缓存,则内存分
配性能就可以非常高。
当应用程序释放内存时,同样地,内存管理器需要尝试将该内存块释
放到线程局部缓存,以通过局部化写入来避免加锁,同时也能够通过填充
局部缓存来为后续的内存申请做好准备。与内存申请不同的是,内存释放
可以有两种不同的方案,第一种是释放回该内存块原始申请时所属的线程
缓存,第二种是直接释放到本线程自己的局部缓存。
线程局部缓存类型的内存管理在一般的多线程环境下能够有效地提
升性能,但在实践中发现,对于以数据包多核并行处理为代表的流式数据
并行处理应用场景,传统的线程局部缓存方案仍然有很大提升空间。
发明内容
因此,本发明的任务是提供一种特别适合于流式数据并行处理的内存
管理解决方案。
根据本发明的一个方面,提供了一种用于流式数据并行处理的内存管
理方法,包括下列步骤:
1)根据流式数据并行处理流水线的构架为生产者和消费者建立一一
对应的对等关系,为建立了对等关系的每个生产者建立私有的局部缓存池,
为建立了对等关系的每个消费者建立私有的局部缓存池;
2)在进行流式数据并行处理过程中,每个生产者向其私有的局部缓
存池申请内存,并且在该生产者的局部缓存池为空时,触发交换操作以获
得内存块,所述交换操作是将该生产者的局部缓存池和与其对等的消费者
的局部缓存池交换;
3)在进行流式数据并行处理过程中,每个消费者向该消费者的局部
缓存池释放内存。需要说明的是,上述步骤2)和步骤3)的执行顺序可
以交换,二者不分先后。
其中,所述步骤2)中,所述交换操作通过交换对等的生产者和消费
者的局部缓存池的指针或者对象引用实现。
其中,所述步骤1)还包括:建立全局内存池。
其中,所述步骤2)还包括:触发交换操作后,若此时对等消费者缓
存池的内存块数目少于第一阈值,停止所述交换操作,生产者直接从所述
全局内存池获取内存块,若此时对等消费者缓存池的内存块数目达到所述
第一阈值,则继续执行所述交换操作。
其中,所述步骤3)还包括:消费者向该消费者的线程局部缓存池释
放内存时,若此时该消费者的局部缓存池的内存块数目超过第二阈值,则
该消费者停止向其局部缓存池释放内存,改为向所述全局内存池释放内存,
若此时该消费者的局部缓存池的内存块数目未超过所述第二阈值,则该消
费者继续向其局部缓存池释放内存,所述第二阈值大于所述第一阈值。
其中,所述步骤2)包括下列子步骤:
21)生产者开始对流式数据进行流水线第一级数据处理时,触发为本
批次流式数据的内存申请;
22)该生产者判断本地局部缓存池是否为空,如果是,则继续执行步
骤23),如果否,则执行步骤25);
23)判断对等端的消费者的局部缓存池的容量是否大于所述第一阈值,
如果是,则执行步骤24)如果否,则执行步骤26);
24)将该生产者的线程局部缓存池与其对等消费者的线程局部缓存池
交换;
25)该生产者从局部缓存池中获取所需的内存块;
26)该生产者从全局内存池获取内存块。
其中,所述步骤3)包括下列子步骤:
31)消费者在完成一批次流式数据的最后一级数据处理时,触发本批
次流式数据的内存释放。
32)该消费者判断本地的局部缓存池存量是否小于所述第二阈值,如
果是,执行步骤33),否则,执行步骤34);
33)将内存释放到本地的局部缓存池;
34)将内存直接释放到全局内存池。
其中,所述步骤2)还包括:每个生产者动态调整自身的所述第一阈
值。
其中,所述步骤2)中,动态调整所述第一阈值的方法如下:每个生
产者记录交换操作触发频度F1和直接访问全局内存池的频度F2,当(C1
*F1)>(C2*F2)时,提高该生产者的所述第一阈值,当(C1*F1)<
(C2*F2)时,降低该生产者的所述第一阈值,其中C1表示单次交换操
作的开销,C2表示单次访问全局内存池的开销。
其中,所述步骤3)还包括:消费者动态调整所述第二阈值。
其中,动态调整所述第二阈值的方法如下:当全局内存池的内存块超
出预设的全局内存阈值时,提高所述第二阈值。
与现有技术相比,本发明具有下列技术效果:
1、本发明能够有效地降低流式数据并行处理的内存操作开销。
2、本发明能够提高流式数据并行处理的内存的利用率。
3、本发明特别适合于流水线级数较多,并行度较高,流水线结构复
杂的应用场合。
附图说明
以下,结合附图来详细说明本发明的实施例,其中:
图1示出了一个通用的数据包多核并行处理构架;
图2示出了采用传统的线程局部缓存方案且内存释放到本地缓存的方
案下流水线的内存单向循环流动示意图;
图3示出了本实施例中的多点缓存和对等交换的内存管理构架;
图4示出了本发明一个优选实施例的用于流式数据并行处理的内存管
理方法的步骤2的子流程示意图;
图5示出了本发明一个优选实施例的用于流式数据并行处理的内存管
理方法的步骤3的子流程示意图。
具体实施方式
发明人对数据包多核并行处理进行了深入研究,发现直接将现有的通
用线程局部缓存类型的内存管理方法应用于基于流水线构架的数据包多
核并行处理中,容易造成资源浪费及竞争开销等问题。
一方面,在流水线中,往往由第一级线程申请内存,最后一级线程释
放内存,中间级线程只是使用第一级线程所申请的内存,例如从第一级线
程所申请的内存拷贝原始数据包、读写头部字段等,并不需要额外地为每
一个中间级线程申请内存,这就是数据包处理中的“生产者-消费者”内存
使用模式。然而在传统的线程局部缓存方案中,仍然会为每个线程单独维
护一块相关联的线程局部缓存,这就导致资源的浪费。
另一方面,在流水线的“生产者-消费者”内存使用模式下,传统的线
程局部缓存方案中那些用于减少竞争开销的措施可能会失效。如前文所述,
线程局部缓存类型的内存管理方法的内存申请流程都是一致的,但释放缓
存时有两种不同方案,为了全面地评估该方法在数据包多核并行处理下的
竞争开销,下面分别进行分析。
1)若内存释放时采用释放回原始缓存的方案,则每个线程缓存区都可
能被其关联线程和其它持有从属于该缓存区的内存块的一个或多个线程
同时访问,因此每次申请和释放操作都需要加锁。这会导致竞争开销显著
增加。
2)若内存释放时采用释放到本地缓存的方案,则只有特殊的申请和释
放才需要加锁。然而,在“生产者-消费者”模式下,该方法会造成局部缓
存失效。在内存释放到本地缓存的方案下,内存总是从生产者缓存传递到
消费者缓存,再由消费者缓存释放到全局存储区,从而形成生产者缓存到
消费者缓存再到全局存储区的单向流动,这将导致生产者线程的缓存被抽
干,在抽干生产者线程的缓存和填满消费者线程的缓存后,所有内存申请
和释放请求都要通过访问全局存储区完成,最终形成在全局存储区、生产
者缓存、消费者缓存三者之间单向循环流动,图2示出了采用传统的线程
局部缓存方案且内存释放到本地缓存的方案下流水线的内存单向循环流
动示意图。在这种状态下,所有内存申请和释放请求都要通过访问全局存
储区完成,这就增加了对全局存储区的加锁访问,导致竞争开销显著增加。
基于上述分析,根据本发明的一个实施例,提供了一种用于流式数据
并行处理的内存管理方法,该方法可以应用于数据包并行处理、视频流并
行处理、文本并行处理、消息并行处理等各种流式数据并行处理的场景中。
本实施例中,用于流式数据并行处理的内存管理方法包括下列步骤:
步骤1:根据流式数据并行处理流水线的构架,建立基于多点缓存和
对等交换的内存管理构架。
本实施例中,流水线采用常用的“生产者-消费者”内存使用模式,该
模式下,将第一级流水线的线程称为“生产者”,最后一级流水线的线程
称为“消费者”。在多点缓存和对等交换的内存管理体系中,将相同数量
的生产者和消费者建立一一映射,相互之间称为“对等”关系。例如,与
某个生产者对应的消费者可以称为该生产者的对等消费者,与某个消费者
对应的生产者可以称为该消费者的对等生产者。
图3示出了本实施例中的多点缓存和对等交换的内存管理构架,在该
构架中,为流水线建立全局共享的全局内存池,为每个存在“对等”关系
的生产者和消费者都建立一个私有的局部缓存池。采用自建内存池的方式
来管理全局内存池、生产者线程局部缓存池和消费者线程局部缓存池,以
自行管理内存的组织和使用方式。初始时,全局内存池和生产者的缓存池
都会被放入一定量从操作系统预先申请的内存块,而消费者的缓存池可以
保持为空。
需要说明的是,当流水线构架中第一级线程(即生产者)的数目与最
后一级线程(即消费者)数目相同时,可以直接为生产者和消费者建立一
一映射,而当流水线构架中的生产者数目与消费者数目不同时,则选取数
目相同的生产者和消费者建立一一映射,剩余的生产者或消费者直接面向
全局内存池申请或释放内存,不再执行后续的步骤2、3、4。
内存管理通常有两种实现方式,一种是动态内存分配,即直接使用系
统库函数进行内存申请和释放(如C语言库中malloc函数和free函数);
另一种就是自建内存池。动态内存分配方式虽然实现简单(应用程序自身
无需关心),但由于系统库函数需要考虑足够的通用性,因此性能和效率
往往不高。数据包处理系统对性能要求非常高,并且由于网络协议的限制,
原始数据包所需要的内存块大小通常也比较固定,因此本实施例中采用自
建内存池的方式。一方面,内存池的操作比通用内存分配操作简单许多,
因此操作开销很小;另一方面,由于内存池都是分配固定大小的内存块,
因此不会产生外部碎片,内存利用率相对更高。
步骤2:在进行流式数据并行处理过程中,每个生产者向该生产者的
线程局部缓存池申请内存。如果此时该生产者的线程局部缓存池为空,则
触发交换操作以获得内存块。交换操作是将该生产者的线程局部缓存池与
其对等消费者的线程局部缓存池交换。交换操作能够让生产者和消费者继
续在各自的缓存池上操作,而不用去访问全局内存池,在不经过全局内存
池的前提下实现了缓存资源的循环利用。在实现上,交换操作只需要交换
各自局部缓存池的指针(或对象引用),不需要加锁,因此操作代价非常
小。此外,由于只有生产者的线程局部缓存池为空时才会触发交换,因此
均摊到每次内存操作的代价很小。
步骤3:在进行流式数据并行处理过程中,每个消费者向该消费者的
线程局部缓存池释放内存。
上述通过多点缓存申请和释放内存的设计,能够让绝大部分内存申请
和释放操作都只需要访问线程私有的局部缓存,不需要加锁,因此性能非
常高;另一方面,相比于传统的线程局部缓存方案,该方法不需要为无关
线程(如中间流水线线程、管理线程等)建立缓存池,且消费者的缓存池
初始也不需要填充,因此能够进一步提升内存利用率。
进一步地,在实现流式数据并行处理时,有的线程流水线可能存在负
载均衡调度机制,这可能导致生产者申请内存的速率与相应的对等消费者
释放内存的速率不一致,这样,可能出现部分生产者-消费者之间每次交换
的内存块数量逐步减少的现象,这又反过来促使交换操作更加频繁,甚至
出现生产者和对等消费者的缓存池都为空,导致无法交换的情况。为了避
免这种情况的发生,本发明的另一个实施例的内存管理方法中,加入了一
套均衡调节机制,该均衡调节机制包括:a、当生产者缓存池为空时,若
此时对等消费者缓存池少于第一阈值(也可称为低阈值),则认为对等端
内存块的存量不够,交换操作不划算,因此不再进行对等交换操作,而是
让生产者直接从全局池获取内存块。b、当消费者释放内存时,若局部缓
存池的存量大于第二阈值(也可称为高阈值),则认为局部缓存池在当前
占用了过多内存资源,因此不再释放内存到局部缓存池,而是直接释放到
全局内存池。
图4示出了本发明一个优选实施例的用于流式数据并行处理的内存管
理方法的步骤2的子流程示意图,参考图4,本优选实施例的用于流式数
据并行处理的内存管理方法中,步骤2包括下列子步骤:
步骤21:生产者开始对流式数据进行流水线第一级数据处理时,触发
为本批次流式数据的内存申请。
步骤22:该生产者判断本地局部缓存池(即该生产者的局部缓存池)
是否为空,如果是,则继续执行步骤23,如果否,则执行步骤25。
步骤23:判断对等端的消费者的局部缓存池的容量是否大于第一阈值,
如果是,则执行步骤24,如果否,则执行步骤26。
步骤24:执行交换操作,即将该生产者的线程局部缓存池与其对等消
费者的线程局部缓存池交换。所述交换操作通过交换各自局部缓存池的指
针(或对象引用)完成。
步骤25:该生产者从局部缓存池中获取所需的内存块,然后转到步骤
27。
步骤26:该生产者从全局内存池获取内存块,然后转到步骤27。
步骤27:本次内存申请完成,返回。
图5示出了本发明一个优选实施例的用于流式数据并行处理的内存管
理方法的步骤3的子流程示意图,参考图5,本优选实施例的用于流式数
据并行处理的内存管理方法中,步骤3包括下列子步骤:
步骤31:消费者在完成一批次流式数据的最后一级数据处理时,触发
本批次流式数据的内存释放。
步骤32:该消费者判断本地的局部缓存池(即该消费者的局部缓存池)
存量是否小于第二阈值,如果是,执行步骤33,否则,执行步骤34。
步骤33:将内存释放到本地的局部缓存池,然后转到步骤35。
步骤34:将内存直接释放到全局内存池,然后转到步骤35。
步骤35:本次内存释放完成,返回。
更进一步地,在一个优选实施例中,为了进一步减少内存操作开销,
还对所述第一阈值和第二阈值进行动态调整。
如前文所述,第一阈值是一个低阈值,这个阈值越小,则交换操作次
数越多(这是因为交换条件更容易满足),而均衡调节触发次数则越少。
反之,第一阈值越大,则交换操作次数越少,均衡调节触发次数则越多。
类似地,第二阈值是一个高阈值,这个阈值越小,则交换操作次数越多(这
是因为平均每次交换量减少,导致生产者的局部缓存池更容易用尽),而
内存资源利用率则越高(这是因为内存更少地会堆积在局部缓存池);反
之,第二阈值越大,则交换操作次数越少,内存资源利用率则越低。
交换操作和均衡调节都会产生一定的开销,在不同的场景和运行状态
下,第一、第二阈值的大小对这两类开销的影响是不同的。因此,根据历
史反馈信息动态地调整阈值,能够不断迭代降低当前占比较大的操作开销,
进而使全局开销最小化,并进一步提高内存的使用效率。
基于以上分析,本实施例提供了一种对第一阈值和第二阈值进行动态
调整的方法,包括下列步骤:
41)事先评估或测量单次交换操作的开销(记为C1)和均衡调节时
访问全局缓存池的开销(记为C2);
42)每个线程单独维护自己的阈值(生产者维护低阈值,消费者维护
高阈值),以根据线程自身实际运行状况做个性化调整;
43)每个线程额外维护两个动态值:交换操作触发频度(记为F1)、
均衡调节触发频度(记为F2),作为运行时的历史反馈信息;
44)当(C1*F1)>(C2*F2)时,说明交换操作的开销目前占比
较大,因此可以提高低阈值,以减少交换操作开销;
45)当(C1*F1)<(C2*F2)时,说明均衡调节的开销目前占比
较大,因此可以降低低阈值,减少均衡条件开销;
46)当全局内存池的内存资源足够(可以根据预设的全局内存阈值判
断)时,可以提高高阈值,以减少交换操作开销,即通过内存来换取性能。
实验表明,本发明能够有效地降低流式数据并行处理的内存操作开销,
提高内存的利用率。并且,本发明尤其适用于流水线级数较多,流水线结
构复杂的应用场合。
以上所述仅为本发明示意性的具体实施方式,并非用以限定本发明的
范围。任何本领域的技术人员,在不脱离本发明的构思和原则的前提下所
作的等同变化、修改与结合,均应属于本发明保护的范围。