当前位置: 首页 > news >正文

DPDK系列之三十三DPDK并行机制的底层支持

一、背景介绍

在前面介绍了DPDK中的上层对并行的支持,特别是对多核的支持。但是,大家都知道,再怎么好的设计和架构,再优秀的编码,最终都要落到硬件和固件对整个上层应用的支持。单纯的硬件好处理,一个核不够多个核,在可能的情况下把CPU的频率增加,加大缓存等等。在现有水平的范围内,这些都是可以比较容易做到的。
但是另外一个,就是在CPU上如何最终运行指令(也可以叫做固件设计),这个就需要设计人员动脑子了。一般来说,IPC(Instruction Per Clock,一个时钟周期内执行的指令数量,可不要看成进程间通信)的数量越高,CPU运行性能越高(频率和核数相同)。
现代CPU基本使用了越标量(superscalar)体系结构,通过以空间换时间的方式实行了指令级并行运算。不同的架构的处理器,可能在硬件设计本身有所不同,但在追求并行度上,原理基本相同。
在前面的多核编程中,介绍过几种指令,目前常用的基本以SIMD(单指令流多数据流)和MIMD(多指令流多数据流)为主。后者一般是多核和多CPU(当然更高层次的多计算机也算),但在分析本文中更倾向的是SIMD,毕竟一个核心能处理多少更能体现性能和效率。
SIMD其实很容易理解,可以认为是一种并行的批处理。原来只能一次取一条指令处理一条数据,这次可以一条指令处理多条数据。举个最简单的例子,加指令,需要有两次读操作数,而如果使用SIMD,则一次就可以都读进来。其后的处理周期也是如此,那么效率至少增加了一倍。
而这些指令设计和处理会形成一个指令集,它的发展也有一个过程,intel的SIMD指令集主要有MMX, SSE, AVX, AVX-512,主流就是SSE/AVX。AMD的比较复杂,有兴趣可以查找看一下。

二、DPDK中的应用

在DPDK中对SIMD的应用体现在数据的处理上,DPDK提供了一个化化的拷贝memcpy函数,它充分利用了SIMD指令集:

static __rte_always_inline void *
rte_memcpy(void *dst, const void *src, size_t n)
{if (!(((uintptr_t)dst | (uintptr_t)src) & ALIGNMENT_MASK))return rte_memcpy_aligned(dst, src, n);elsereturn rte_memcpy_generic(dst, src, n);
}
static __rte_always_inline void *
rte_memcpy_aligned(void *dst, const void *src, size_t n)
{void *ret = dst;/* Copy size < 16 bytes */if (n < 16) {return rte_mov15_or_less(dst, src, n);}/* Copy 16 <= size <= 32 bytes */if (n <= 32) {rte_mov16((uint8_t *)dst, (const uint8_t *)src);rte_mov16((uint8_t *)dst - 16 + n,(const uint8_t *)src - 16 + n);return ret;}/* Copy 32 < size <= 64 bytes */if (n <= 64) {rte_mov32((uint8_t *)dst, (const uint8_t *)src);rte_mov32((uint8_t *)dst - 32 + n,(const uint8_t *)src - 32 + n);return ret;}/* Copy 64 bytes blocks */for (; n >= 64; n -= 64) {rte_mov64((uint8_t *)dst, (const uint8_t *)src);dst = (uint8_t *)dst + 64;src = (const uint8_t *)src + 64;}/* Copy whatever left */rte_mov64((uint8_t *)dst - 64 + n,(const uint8_t *)src - 64 + n);return ret;
}
static __rte_always_inline void *
rte_memcpy_generic(void *dst, const void *src, size_t n)
{__m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8;void *ret = dst;size_t dstofss;size_t srcofs;/*** Copy less than 16 bytes*/if (n < 16) {return rte_mov15_or_less(dst, src, n);}/*** Fast way when copy size doesn't exceed 512 bytes*/if (n <= 32) {rte_mov16((uint8_t *)dst, (const uint8_t *)src);rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);return ret;}if (n <= 48) {rte_mov32((uint8_t *)dst, (const uint8_t *)src);rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);return ret;}if (n <= 64) {rte_mov32((uint8_t *)dst, (const uint8_t *)src);rte_mov16((uint8_t *)dst + 32, (const uint8_t *)src + 32);rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);return ret;}if (n <= 128) {goto COPY_BLOCK_128_BACK15;}if (n <= 512) {if (n >= 256) {n -= 256;rte_mov128((uint8_t *)dst, (const uint8_t *)src);rte_mov128((uint8_t *)dst + 128, (const uint8_t *)src + 128);src = (const uint8_t *)src + 256;dst = (uint8_t *)dst + 256;}
COPY_BLOCK_255_BACK15:if (n >= 128) {n -= 128;rte_mov128((uint8_t *)dst, (const uint8_t *)src);src = (const uint8_t *)src + 128;dst = (uint8_t *)dst + 128;}
COPY_BLOCK_128_BACK15:if (n >= 64) {n -= 64;rte_mov64((uint8_t *)dst, (const uint8_t *)src);src = (const uint8_t *)src + 64;dst = (uint8_t *)dst + 64;}
COPY_BLOCK_64_BACK15:if (n >= 32) {n -= 32;rte_mov32((uint8_t *)dst, (const uint8_t *)src);src = (const uint8_t *)src + 32;dst = (uint8_t *)dst + 32;}if (n > 16) {rte_mov16((uint8_t *)dst, (const uint8_t *)src);rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);return ret;}if (n > 0) {rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);}return ret;}/*** Make store aligned when copy size exceeds 512 bytes,* and make sure the first 15 bytes are copied, because* unaligned copy functions require up to 15 bytes* backwards access.*/dstofss = (uintptr_t)dst & 0x0F;if (dstofss > 0) {dstofss = 16 - dstofss + 16;n -= dstofss;rte_mov32((uint8_t *)dst, (const uint8_t *)src);src = (const uint8_t *)src + dstofss;dst = (uint8_t *)dst + dstofss;}srcofs = ((uintptr_t)src & 0x0F);/*** For aligned copy*/if (srcofs == 0) {/*** Copy 256-byte blocks*/for (; n >= 256; n -= 256) {rte_mov256((uint8_t *)dst, (const uint8_t *)src);dst = (uint8_t *)dst + 256;src = (const uint8_t *)src + 256;}/*** Copy whatever left*/goto COPY_BLOCK_255_BACK15;}/*** For copy with unaligned load*/MOVEUNALIGNED_LEFT47(dst, src, n, srcofs);/*** Copy whatever left*/goto COPY_BLOCK_64_BACK15;
}

更多相关的代码在rte_memcpy.h和rte_memcpy.c中,注意,它包含不同CPU架构平台的多个版本,不要搞混。
从上面的代码可以看到,影响拷贝速度的有以下几点:
1、字节对齐和数据的加载存储。
这个大家都明白,除了字节对齐速度加快外,而且DPDK中还对不同的字节对齐以及长度进行了控制,充分发挥SIMD的优势(说直白一点就是在条件允许的情况下,一次拷贝数量多【16字节:128位】,这个和平台支持有关)
2、函数和库调用开销,库函数需要调用过程,这个也浪费时间。这个库调用过程在编译选择优化的过程中,优化难度也比较大,不如在DPDK中直接调用,特别是使用
static __rte_always_inline(静态内联)时,这在网上有很多优化的比较,自己也可以试一试。
3、整体上来说,数据量越大,上面的优化越优势越大;否则优势则不明显。
上述的比较是针对库glibc以及DPDK相比而言的,至于个人优化过的则不在此范畴之内。另外,随着技术的进步,如果用高版本的glibc并开启优化后,可能效果差别也不大,这个没有进行比较。
有兴趣可以看看rte_mov256等几个函数。
需要说明的是,对于某一类函数,没有普遍最优之说。只有场景条件限制下的最合适。也就是说,DPDK的拷贝函数不代表此函数比glibc中的拷贝函数优秀,只是说明此函数在DPDK的应用场景下更合适。
最后总结一下,针对内存拷贝的优化点:
1、减少拷贝过程中的附加处理如字节对齐
2、在平台允许情况下使用最大带宽(拷贝最大数量)
3、使用平坦顺序内存并使用分支预测(减少分支跳转,如是否有范围重叠等)
4、有可能的情况下使用non-temporal访存执令
5、使用加速拷贝的一些指令(string操作指令等)。
6、处理大内存(M以上)和小内存(K以下)的不同场景(这个在一些常用框架中都会处理)

三、总结

性能和效率的提升,是一个系统工程。它可能会从一个点开始,然后不断的影响别的点,然后这些点又互相影响,最后蔓延到整个系统,形成一个量变到质变的过程。计算机应用也不外乎这样。
DPDK中通过Linux内核的一些设计(如大页),通过一种工程优化的手段来提高网络通信的效率,但反过来,内核也会借鉴DPDK的一些特点来吸收到内核中去。同样,DPDK的出现对硬件本身的设计也提出了虚拟化的相关等要求。硬件水平的提高又可以提高DPDK的性能。
国内的缺少的不是后面的一系列动作,缺少的恰恰是开始那个点,那个用于爆发的创新点。

相关文章:

DPDK系列之三十三DPDK并行机制的底层支持

一、背景介绍 在前面介绍了DPDK中的上层对并行的支持&#xff0c;特别是对多核的支持。但是&#xff0c;大家都知道&#xff0c;再怎么好的设计和架构&#xff0c;再优秀的编码&#xff0c;最终都要落到硬件和固件对整个上层应用的支持。单纯的硬件好处理&#xff0c;一个核不…...

LVGL_基础控件滚轮roller

LVGL_基础控件滚轮roller 1、创建滚轮roller控件 /* 创建一个 lv_roller 部件(对象) */ lv_obj_t * roller lv_roller_create(lv_scr_act()); // 创建一个 lv_roller 部件(对象),他的父对象是活动屏幕对象// 将部件(对象)添加到组&#xff0c;如果设置了默认组&#xff0c…...

王道考研操作系统——文件管理

磁盘的基础知识 .txt用记事本这个应用程序打开&#xff0c;文件最重要的属性就是文件名了 保护信息&#xff1a;操作系统对系统当中的各个用户进行了分组&#xff0c;不同分组的用户对文件的操作权限是不一样的 文件的逻辑结构就是文件内部的数据/记录应该被怎么组织起来&…...

商业智能系统的主要功能包括数据仓库、数据ETL、数据统计输出、分析功能

ETL服务内容包含&#xff1a; 数据迁移数据合并数据同步数据交换数据联邦数据仓库...

基于帝国主义竞争优化的BP神经网络(分类应用) - 附代码

基于帝国主义竞争优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于帝国主义竞争优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.帝国主义竞争优化BP神经网络3.1 BP神经网络参数设置3.2 帝国主义竞争算…...

将python项目部署在一台服务器上

将python项目部署在一台服务器上 1.服务器2.部署方法2.1 手动部署2.2 容器化技术部署2.3 服务器less技术部署 1.服务器 服务器一般为&#xff1a;物理服务器和云服务器。 我的是物理服务器&#xff1a;这是将服务器硬件直接放置在您自己的数据中心或机房的传统方法。这种方法需…...

【C语言】善于利用指针(二)

&#x1f497;个人主页&#x1f497; ⭐个人专栏——C语言初步学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; ​ 目录 导读&#xff1a;1. 字符指针1.1 字符串的引用方式1.2 有趣的面试题 2. 数组指针2.1 一维数组指针的定义2.2 一维数组…...

Python调用C++

https://www.cnblogs.com/renfanzi/p/10276997.html Linux使用Python调用C/C接口(一) - 代码先锋网 linux系统上使用Python调用C生成的.so动态链接库opencv_linux 下python 编译为so ,给c使用_比赛学习者的博客-CSDN博客 https://www.cnblogs.com/shuimuqingyang/p/13618105…...

自己实现扫描全盘文件的函数。

1.自己实现扫描全盘的函数 def scan_disk(dir): global count,dir_count if os.path.isdir(dir): files os.listdir(dir) for file in files: print(file) dir_count 1 if os.path.isdir(dir os.sep file): …...

JSON文件读写

1、依赖文件 #include <QFile> #include <QJsonDocument> #include <QJsonObject> #include <QDebug> #include <QStringList>2、头文件 bool ReadJsonFile(const QString& filePath""); bool WriteJsonFile(const QString&…...

VisualStudio2022环境下Release模式编译dll无法使用TLS函数问题

Debug x86环境下正常使用TLS回调函数 切换到Release发现程序没有使用tls 到C/C > 优化中将全程序优化关闭即可...

ChatGPT基础使用总结

文章目录 一、ChatGPT基础概念大型语言模型LLMs---一种能够以类似人类语言的方式“说话”的软件ChatGPT定义---OpenAI 研发的一款聊天机器人程序&#xff08;2022年GPT-3.5&#xff0c;属于大型语言模型&#xff09;ChatGPT4.0---OpenAI推出了GPT系列的最新模型ChatGPT典型使用…...

解决报错: require is not defined in ES module scope

用node启动mjs文件报错&#xff1a;require is not defined in ES module scope 现象如下&#xff1a; 原因&#xff1a; 文件后缀是mjs, 被识别为es模块&#xff0c;但是node默认是commonjs格式&#xff0c;不支持也不能识别es模块。 解决办法&#xff1a;把文件后缀从.mjs改…...

STM32 10个工程篇:1.IAP远程升级(六)

在IAP远程升级的最后一篇博客里&#xff0c;笔者想概括性地梳理总结IAP程序设计中值得注意的问题&#xff0c;诚然市面上或者工作后存在不同版本的IAP下位机和上位机软件&#xff0c;也存在不同定义的报文格式&#xff0c;甚至对于相似的知识点不同教程又有着完全不同的解读&am…...

【智能家居项目】裸机版本——字体子系统 | 显示子系统

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 今天实现上图整个项目系统中的字体子系统和显示子系统。 目录 &#x1f004;设计思路&#x1…...

PDF中跳转到参考文献后,如何回到原文

在PDF中&#xff0c;点击了参考文献的超链接可以直接跳至参考文献的位置。 如果想从当前参考文献在回到正文中对应位置时&#xff0c;可以通过 Alt \red{\text{Alt}} Alt ← \red{\leftarrow} ← 实现。...

了解基于Elasticsearch 的站内搜索,及其替代方案

对于一家公司而言&#xff0c;数据量越来越多&#xff0c;如果快速去查找这些信息是一个很难的问题&#xff0c;在计算机领域有一个专门的领域IR&#xff08;Information Retrival&#xff09;研究如何获取信息&#xff0c;做信息检索。在国内的如百度这样的搜索引擎也属于这个…...

【多模态融合】TransFusion学习笔记(2)

接上篇【多模态融合】TransFusion学习笔记(1)。 从TransFusion-L到TransFusion ok,终于可以给出论文中那个完整的框架图了&#xff0c;我第一眼看到这个图有几个疑问: Q&#xff1a;Image Guidance这条虚线引出的Query Initialization是什么意思? Q&#xff1a;图像分支中的…...

Pyhon-每日一练(1)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…...

MySQL:数据库的物理备份和恢复-冷备份(3)

介绍 物理备份&#xff1a; 直接复制数据文件进行的备份 优点&#xff1a;不需要其他的工具&#xff0c;直接复制就好&#xff0c;恢复直接复制备份文件即可 缺点&#xff1a;与存储引擎有关&#xff0c;跨平台能力较弱 逻辑备份&#xff1a; 从数据库中导出数据另存而进行的备…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...