在多线程数据平面开发套件(DPDK)应用程序中优化内存使用

目录

介绍

参考申请

环与堆叠

从环形内存池转移到堆栈内存池所需的代码更改

测试方法

结果

结论

关于作者

资源资源

尾注


 

介绍

高速数据包处理应用程序可能会占用大量资源。软件工程师和架构师可以提高其应用程序性能的一种方法是,将其数据包处理管道划分为多个线程。但是,这可能导致对缓存和内存资源的压力增加。因此,保持应用程序的内存占用量相对于数据平面流量尽可能小是制作高性能应用程序的关键。本文提供了一种优化多线程数据包处理应用程序的内存使用的技术,该技术应提高内存绑定应用程序的性能。如果应用程序不受内存限制,则应降低其内存需求。

参考申请

本文基于使用英特尔公司虚拟化电缆调制解调器终端系统的Reference Dataplane v18.10.0进行的研究。vCMTS应用程序是基于DOCSIS 3.1规范和数据平面开发套件(DPDK)数据包处理框架的电缆数据服务接口规范(DOCSIS)媒体访问控制(MAC)数据平面管道。该应用程序的目的是提供一种工具,用于表征vCMTS数据平面数据包处理性能和英特尔®至强®平台上的功耗。从接入网数据平面下载vCMTS参考数据平面网站为01.org。vCMTS的该版本基于DPDK 18.08,但是本文中介绍的方法背后的理论可以应用于使用DPDK早期版本或其他数据包处理库(例如Cisco *矢量数据包处理(VPP)框架)的应用程序。本文中使用的功能最初已添加到DPDK 16.07.2。

该应用程序的下游部分使用多线程管道设计。管道分为两个部分:上层MAC和下层MAC,它们在单独的线程上运行。这些必须在同级超线程(两个线程在一个物理内核上运行)上运行,否则L2缓存效率将丢失。请参考图1中的vCMTS下游数据包处理管道。在本文撰写之时,vCMTS的上游部分未使用多线程模型;而是使用多线程模型。本文将重点放在下游部分作为参考应用。

图1:vCMTS下游上下MAC

环与堆叠

DPDK使用称为mbufs的消息缓冲区来存储数据包数据。这些mbuf存储在称为mempool的内存池中。默认情况下,内存池设置为环,这会创建一个池,其配置类似于先进先出(FIFO)系统。该模型可以很好地适用于线程跨越多个核心的多线程应用程序。但是,对于线程在同一内核上的应用程序,可能会导致不必要的内存带宽,并且某些超线程效率可能会丢失。在线程在同一内核上运行的应用程序中,环形内存池最终将循环遍历所有mbuf。当mbuf数量很大时,例如在vCMTS的情况下,这会导致几乎所有分配的mbuf上的CPU缓存丢失。

DPDK还允许用户在堆栈配置中设置其内存池,这会创建使用后进先出(LIFO)配置的内存池。

内存池还具有内存池缓存,该内存池允许回收“热”缓冲区,从而在同一线程上分配和释放缓冲区的情况下提高了缓存效率。内存池缓存始终使用LIFO配置来设置,以提高内存池缓存的性能。DPDK应用程序使用的每个线程对于每个内存池都有其自己的内存池缓存。由于数据包是由一个线程接收并由另一个线程传输的,因此,mbuf永远不会在同一线程上被释放和分配,这使得内存池缓存系统在这种情况下变得多余。

环形内存池模型中的缓冲区移动(如图2所示):

  • 应用程序尝试填充线程0的内存池高速缓存中的网络接口卡(NIC)接收(RX)空闲列表。
  • 永远不会在线程0上释放Mbuf,因此将直接从内存池中补充内存池缓存。
  • 然后,应用程序从内存池缓存中分配mbuf。
  • 当mbuf从传输(TX)NIC中释放时,它们将保留在线程1的内存池缓存中。
  • 当线程1的内存 你                                                                                                                       用程序将开始将mbufs返回到内存池,因为mbuf从未在线程1上分配。

对于此应用程序来说,这是一个较差的模型,因为线程0的内存池缓存始终包含“最冷”的mbuf,而线程1的内存池缓存始终已满。如果内存池很大,CPU将无法将整个内存池保留在CPU缓存中,并将其推入内存。在这种模型中,应用程序将快速循环遍历整个内存池,从而随着mbuf掉入CPU缓存而导致较大的内存带宽。

堆栈内存池模型中的缓冲区移动(如图3所示):

  • 应用程序将从内存池将mbufs分配给NIC RX空闲列表。
  • 当mbuf从传输NIC中释放时,它们被释放回内存池。

此模型更好,因为它更加简化1。内存池已更改为作为堆栈执行,并且冗余内存池缓存已被禁用。mbuf从内存池直接分配给NIC,然后直接释放回内存池,而不是保留在线程1中。请注意,从堆栈内存池释放和分配可能会稍微贵一些,因为需要进行锁定内存池,因为有多线程访问。由于将锁限制在一个芯子上,因此可以将锁的罚款降到最低。(如果线程位于单独的内核上,则锁定成本会更大。)重复使用“热” mbufs的总体收益将超过额外的锁定成本。

 

尝试的另一种方法是减少内存池的内存占用,同时仍使用环形配置,这是减少内存池中的mbuf数量(内存池大小)。分析表明,在正常运行期间,vCMTS大约有750 mbuf处于运行状态-但有时该应用程序可能需要20,000 mbufs。这意味着减少内存池的大小并禁用内存池的缓存不是一种选择,因为有时应用程序可能需要的mbuf数量比正常操作期间所需的数量大几个数量级。如果这些mbuf不可用,则该应用程序可能无法正常运行。

当应用程序使用堆栈模型时,CPU应该能够将应用程序所需的大多数mbuf“热”保留在CPU高速缓存中,并且应将较少的mbuf从CPU高速缓存中逐出到内存中。这是因为在正常操作期间,将有几个mbuf从内存池中一次又一次地重复使用,这将大大减少内存带宽。

从环形内存池转移到堆栈内存池所需的代码更改

将现有的使用环形内存池的DPDK应用程序更改为使用堆栈内存池的应用程序所需的代码修改相对较小,因为繁重的工作是由DPDK库完成的。

  1. 更改DPDK通用基本配置(dpdk / config / common_base)。
    1. 需要将CONFIG_RTE_DRIVER_MEMPOOL_STACK设置为等于“ y ”(“ CONFIG_RTE_DRIVER_MEMPOOL_STACK = y ”),这可以确保DPDK堆栈内存池驱动程序得到编译。堆栈内存池驱动程序默认情况下已编译,但是值得验证它是否已打开,尤其是在修改现有应用程序时。
    2. 需要将“ CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS ”设置为“” stack ”(“ CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS =“ stack” ”)。这会将默认内存池类型设置为堆栈。
    3. 这些更改后,请完全重建DPDK。
  2. 接下来,修改DPDK应用程序本身。尽管默认的内存池类型已设置为堆栈,但仍建议您在应用程序中将内存池类型设置为堆栈,以确保内存池的行为符合预期。尽管本文提供了在三种不同情况下修改代码的指导,但是执行代码更改的详细说明超出了其范围。
    1. “ rte_mempool_create_empty”:本文的参考应用程序使用“ rte_mempool_create_empty ”函数创建其内存池。这意味着“ rte_mempool_set_ops_byname ”功能可用于设置这些内存池的选项。此函数只能在未填充的内存池上使用,即在使用create empty函数创建内存池之后。创建内存池后,请确保已禁用内存池缓存(即,等于零)。在图4中查看此场景的示例代码片段。
    2. “ rte_mempool_create”:如果您的应用程序使用“ rte_mempool_create ”创建其内存,则可能必须更改为“ rte_mempool_create_empty ”。在撰写本文时,不存在用于更改内存池类型的过程,此过程是此功能的一部分。没有类似的功能可以设置内存池的类型。
    3. “ rte_pktmbuf_pool_create”:如果您的应用程序使用“ rte_pktmbuf_pool_create ”,请将此函数更改为“ rte_pktmbuf_pool_create_by_ops ”,该函数几乎相同,但具有用于设置内存池类型的附加参数。
//Create Mempool using variables from the application, cache size should be 0
rte_mempool_create_empty(p->name, p->pool_size, p->buffer_size, p->cache_size, sizeof(struct rte_pktmbuf_pool_private), p->cpu_socket_id, 0);
//Set options for the mempool (Change to stack)
ret = rte_mempool_set_ops_byname(app->mempool[i], "stack", NULL);
//If set options unsuccessful send panic signal
if (ret)
rte_panic("%s mempool set ops error\n", p->name);

图4:堆栈mempool创建示例代码片段(rte_mempool_create_empty)

在vCMTS中,配置选项已添加到应用程序中,以使其更容易在堆栈和环之间更改内存池类型,以进行验证和基准测试。

  1. 要修改的文件是$ MYHOME / vcmts / vcmtsd / docker / docker-image-vcmtsd / config / vcmts-ds.cfg(下游)和$ MYHOME / vcmts / vcmtsd / docker / docker-image-vcmtsd / config / vcmts- us.cfg(上游)。
  2. 这些文件在“ [MEMPOOLX] ”下具有每个内存池的配置选项,其中X是内存池ID。在下游配置两个内存池,在上游配置四个。
  3. 在每个内存池下都有一个“类型”选项,可以是“ type = stack ”以设置堆栈配置中的mempool ,也可以是“ type = ring ”以以环形配置设置mempool。vCMTS的默认内存池类型是堆栈。
  4. 使用环形内存池时,必须使用内存池缓存。为此,请将“ cache_size”选项设置为等于“ 256”。使用堆栈内存池时,通过将“ cache_size”选项设置为等于“ 0”来禁用内存池缓存。
  5. 更改这些配置选项后,必须使用“ build_docker_vcmtsd_feat ”或“ build_docker_vcmtsd_perf ”命令重建用于运行vCMTS下游实例的Docker *映像。

查看图5和6中完整的堆栈和环形配置示例。

[MEMPOOL0]
cpu = 0
type = stack
cache_size = 0

图5:vCMTS堆栈内存池配置

[MEMPOOL0]
cpu = 0
type = ring
cache_size = 256

图6:vCMTS堆栈内存池配置

测试方法

测试是在具有以下配置的系统上运行的:

  1. 双Intel®Xeon®Gold 6148处理器,每个处理器具有20个内核,总共40个内核,运行于2.4GHz
  2. 十二个8GB DDR4内存双列直插式内存模块(DIMM)
  3. 四个英特尔®以太网融合网络适配器(英特尔®以太网CNA)X710-DA4 10GbE
  4. 系统操作系统:Ubuntu * 18.04
  5. 流量生成器在单独的系统上运行

参考应用程序vCMTS支持此系统配置。对于此测试,仅使用双套接字系统中的单个套接字,并且仅运行下游实例。在系统运行vCMTS下游应用程序的1、2、4、8或12个实例时测量内存带宽。测试使用有线互联网混合(iMix)数据包捕获(pcap)进行,以模拟实际的网络流量。vCMTS软件包(ds_cable-imix2_300cms_4ofdm.pcap)中提供了用于这些测试的pcap。该电缆iMix的数据包大小分布为3%68字节,1%932字节和96%1520字节。内存带宽是使用Intel®Performance Counter Monitor(Intel®PCM)内存工具测量的。该应用程序以恒定的流量运行了几分钟,并在稳定后记录了内存带宽。

结果

本文使用vCMTS进行了四组测试:

  1. 关闭循环冗余校验(CRC),在每个实例上以5Gbps的流量打开英特尔®高级加密标准新指令(英特尔®AES-NI)软件加密
  2. CRC启用,每个实例以5Gbps的流量速率启用Intel AES-NI软件加密
  3. 关闭CRC,在每个实例上以6.3Gbps的通信速率启用Intel AES-NI软件加密
  4. CRC开启,每个实例上5.5Gbps流量速率的Intel AES-NI软件加密

所有测试都是在Intel AES-NI软件加密打开的情况下进行的。该加密是使用DPDK Cryptodev API完成的,该API基于用于IPSec库的英特尔®多缓冲区加密。vCMTS还支持使用加密加速器,例如英特尔®QuickAssist技术(英特尔®QAT)。CRC类似于数据包的校验和,并需要额外的处理来生成和检查,因此在有或没有它的情况下都进行了测试,因为应用程序可能会决定使用还是不使用它。

前两个测试以每秒5吉比特的速率进行,以便可以对CRC的开和关进行比较。最后两个测试是以该测试设置可以达到的最高零数据包丢失(ZPL)流量速率进行的。在所有测试中,内存带宽都从每秒的千兆字节转换为每秒的千兆字节,并且系统的总体吞吐量也作为结果的一部分进行了图形化处理。

图7显示了第一个测试,其中CRC处于关闭状态,Intel AES-NI软件加密处于打开状态,并且以每秒5 Gigabits的传输速率运行。堆栈配置(以黄色和橙色显示)与环形配置(以蓝色显示)相比,内存带宽显着下降。所有vCMTS实例的总流量吞吐量以绿色显示为参考点,是通过将流量速率乘以实例计数得出的。在单个实例中,内存带宽下降最大(95%)。随着实例数量的增加,内存带宽的下降不太明显。两个实例显示下降了88%,四个实例下降了81%,八个实例下降了63%,还有12个实例下降了42%。随着实例数量的增加,CPU有限的L3共享缓存资源承受了更大的压力。因此,更多的mbuf将从CPU缓存中掉落到内存中,从而降低丢弃百分比。内存带宽的平均下降为74%,这是非常重要的改进。在该图上,最小降幅为33%。这是针对12个实例测试的读取内存带宽下降,而针对单实例测试的最大写入内存带宽下降是97%。上面讨论的丢包编号引用的是读取和写入内存带宽相结合的内存带宽的下降。所有测试中图表上注明的最小和最大墨滴数是指分别与读写数关联的墨滴。导致较低的掉落百分比。内存带宽的平均下降为74%,这是非常重要的改进。在该图上,最小降幅为33%。这是针对12个实例测试的读取内存带宽下降,而针对单实例测试的最大写入内存带宽下降是97%。上面讨论的丢包编号引用的是读取和写入内存带宽相结合的内存带宽的下降。所有测试中图表上注明的最小和最大墨滴数是指分别与读写数关联的墨滴。导致较低的掉落百分比。内存带宽的平均下降为74%,这是非常重要的改进。在该图上,最小降幅为33%。这是针对12个实例测试的读取内存带宽下降,而针对单实例测试的最大写入内存带宽下降是97%。上面讨论的丢包编号引用的是读取和写入内存带宽相结合的内存带宽的下降。所有测试中图表上注明的最小和最大墨滴数是指分别与读写数关联的墨滴。单实例测试的写入内存带宽下降最大为97%。上面讨论的丢包编号引用的是读取和写入内存带宽相结合的内存带宽的下降。所有测试中图表上注明的最小和最大墨滴数是指分别与读写数关联的墨滴。单实例测试的写入内存带宽下降最大为97%。上面讨论的丢包编号引用的是读取和写入内存带宽相结合的内存带宽的下降。所有测试中图表上注明的最小和最大墨滴数是指分别与读写数关联的墨滴。

图7:CRC关闭时的振铃与堆栈测试结果

图8显示了第二个测试,除了打开CRC之外,它具有与第一个测试相同的条件。显然,堆栈配置(以黄色和橙色显示)与环形配置(以蓝色显示)相比,内存带宽显着下降。1个实例下降了96%,2个实例下降了91%,4个实例下降了87%,8个实例下降了65%,12个实例下降了44%。内存带宽的平均下降为77%,这是另一个非常重要的改进。

图8:CRC开启时的振铃与堆栈测试结果

图9显示了第三项测试,该测试关闭了CRC,打开了Intel AES-NI加密,并且以每秒6.3吉比特的最高零数据包丢失流量运行。显然,堆栈配置(以黄色和橙色显示)与环形配置(以蓝色显示)相比,内存带宽显着下降。1个实例下降了94%,2个实例下降了87%,4个实例下降了78%,8个实例下降了61%,12个实例下降了46%。内存带宽的平均下降为73%,这是另一个非常重要的改进。

图9:振铃与堆栈测试结果–零数据包丢失,CRC关闭

图10显示了第四项测试,该条件与第三项测试具有所有相同的条件,但CRC已打开。结果,零丢包流量速率较低,因此此测试是使用5.5吉比特/秒的流量速率进行的。显然,与环形配置(蓝色)相比,堆栈配置(黄色和橙色)再次显着降低了内存带宽。1个实例下降了95%,2个实例下降了91%,4个实例下降了84%,8个实例下降了76%,12个实例下降了48%。内存带宽的平均下降为79%,这是另一个非常重要的改进。

图10:环形与堆栈测试结果–零数据包丢失,CRC开启

对于环形模型,总体上需要更多处理才能处理数据包,因为必须触摸两次mbuf的总内容。这意味着每单位时间更多的数据包访问,这可能会增加此模型的内存带宽。

选择这四个测试是因为它们创建了不同的处理情况和不同的内存带宽量。作为这些测试的一部分,还有许多其他变量可能已更改,但本文未包括这些变量。

使用环形和堆栈进行了其他测试,以测量vCMTS的最高零数据包丢失流量速率,但是发现这两个速率相同。结论是,vCMTS不受测试系统中可用内存带宽量的限制,但是其他具有较少内存通道的部署模型的行为可能有所不同。如果应用程序受内存限制,则从环形内存池更改为堆栈内存池可能使其性能更高。还进行了测试,以使用带环网和堆栈的vCMTS来测量系统的电源使用情况。这些测试发现功耗没有显着差异。

结论

显然,在所有四个测试中,由于从环形更改为堆栈而导致的内存带宽下降是显着的。四个测试的平均下降为76%。这样做的主要好处是,当运行高流量时,数据平面核心不太可能达到内存带宽饱和,这可能会降低性能。在这种情况下,将堆栈内存池配置用于双线程数据包处理应用程序可以获得直接的性能优势,因为它降低了内存带宽利用率并提高了内存带宽饱和时的流量速率。此更改的另一个好处是可以为在同一套接字上运行的其他应用程序提供更多的内存带宽。这种修改应该只需要对应用程序进行最少的代码更改,因此付出的努力值得这种更改。2。

关于作者

Conor Walsh是位于英特尔Shannon(爱尔兰)的英特尔网络平台集团(NPG)架构团队的软件工程实习生。

资源资源

在英特尔®至强®处理器上最大化DOCSIS 3 0/3 1处理的性能

尾注

1对于同级双线程超线程情况,此模型更加简化;在线程跨越多个核心的情况下未对其进行测试。

2当将vCMTS更改为堆叠状态时,由于减少了内存带宽,因此系统不必安装尽可能多的DIMM内存,这将导致每个DIMM节省大约7瓦的电源。该声明尚未在本文中得到验证,但是它可能是从切换到堆栈配置节省功率的一种方法。

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页