.Net7矢量化的性能优化

news/2024/2/28 17:31:06

前言

矢量化是性能优化的重要技术,也是寄托硬件层面的优化技术。本篇来看下。文章来源:微软官方博客

概括

一:矢量化支持的问题:
矢量化的System.Runtime.Intrinsics.X86.Sse2.MoveMask
函数和矢量化的Vector128.Create().ExtractMostSignificantBits()
函数返回的结果是一样的。但是前者只能在支持SSE2的128位矢量化平台上工作,而后者可以在任何支持128位矢量化平台上工作,包括Risc-V,Arm64,WASM等平台。这里以一段代码看下:

private static void Main()
{Vector128<byte> v = Vector128.Create((byte)123);while (true){WithIntrinsics(v);WithVector(v);break;}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static int WithIntrinsics(Vector128<byte> v) => Sse2.MoveMask(v);
[MethodImpl(MethodImplOptions.NoInlining)]
private static uint WithVector(Vector128<byte> v) => v.ExtractMostSignificantBits();

看下它的ASM代码:

WithIntrinsics:
G_M000_IG01:                ;; offset=0000H55                   push     rbpC5F877               vzeroupper488BEC               mov      rbp, rsp48894D10             mov      bword ptr [rbp+10H], rcxG_M000_IG02:                ;; offset=000BH488B4510             mov      rax, bword ptr [rbp+10H]C5F91000             vmovupd  xmm0, xmmword ptr [rax]C5F9D7C0             vpmovmskb eax, xmm0G_M000_IG03:                ;; offset=0017H5D                   pop      rbpC3                   retWithVector
G_M000_IG01:                ;; offset=0000H55                   push     rbpC5F877               vzeroupper488BEC               mov      rbp, rsp48894D10             mov      bword ptr [rbp+10H], rcxG_M000_IG02:                ;; offset=000BH488B4510             mov      rax, bword ptr [rbp+10H]C5F91000             vmovupd  xmm0, xmmword ptr [rax]C5F9D7C0             vpmovmskb eax, xmm0G_M000_IG03:                ;; offset=0017H5D                   pop      rbpC3                   ret

可以看到这两个函数生成的ASM几乎一模一样。

2.矢量化的一个例子
由于以上代码体现的SSE2的局限性,所以需要把一些代码矢量化,以便在任何平台上运行,这里看一个例子。

static bool Contains(ReadOnlySpan<byte> haystack, byte needle)
{for (int i = 0; i < haystack.Length; i++){if (haystack[i] == needle){return true;}}return false;
}

查找元素,找到了然后返回。怎么对这例子进行矢量化呢?首先需要判断你代码运行的硬件是否支持矢量化,可以通过Vector.IsHardwareAccelerated的返回值来判断。其次,传入的变量长度(haystack.length)必须的大于一个向量的长度(Vector .Count,win11加VS2022这个值是32)。那么改造之后如下:

static bool Contains(ReadOnlySpan<byte> haystack, byte needle)
{if (Vector.IsHardwareAccelerated && haystack.Length >= Vector<byte>.Count){// ...}else{for (int i = 0; i < haystack.Length; i++){if (haystack[i] == needle){return true;}}}return false;
}

如果以上if的两个判断均为true的话,那么我们进入矢量化阶段。代码如下:

static unsafe bool Contains(ReadOnlySpan<byte> haystack, byte needle)
{if (Vector.IsHardwareAccelerated && haystack.Length >= Vector<byte>.Count)//判断当前运行的硬件是否符合矢量化以及变量的长度不能小于矢量化里面一个向量的长度。{fixed (byte* haystackPtr = &MemoryMarshal.GetReference(haystack))//获取变量的头指针{Vector<byte> target = new Vector<byte>(needle);//向量化需要查找的变量needlebyte* current = haystackPtr;//变量haystack的头指针,以便于后面循环byte* endMinusOneVector = haystackPtr + haystack.Length - Vector<byte>.Count;//头指针+变量的长度减去一个向量的长度。同头指针current开始到endMinusOneVector在这个里面遍历循环,查找需要查找的变量target也就是向量化的needle,这里为什么要进去Vector<byte>.Count因为向量是从0开始查找的。do{if (Vector.EqualsAny(target, *(Vector<byte>*)current))//判断当前的指针是否与需要查找的变量相等{return true;//相等就返回true}current += Vector<byte>.Count;//不相等指针就位移到下一个向量,继续遍历循环。}while (current < endMinusOneVector);//这里判断是否达到循环终点。}}else{for (int i = 0; i < haystack.Length; i++){if (haystack[i] == needle){return true;}}}return false;
}

以上代码几乎完成了90%,但是依然有点点问题。那就是最后一个向量endMinusOneVector没有被查找。所以还需要加上它的查找。最后的点如下,第一个Contains是不矢量化的,第二个Contains_Vector是矢量化之后的。

static bool Contains(ReadOnlySpan<byte> haystack, byte needle)
{for (int i = 0; i < haystack.Length; i++){if (haystack[i] == needle){return true;}}return false;
}
static unsafe bool Contains_Vector(ReadOnlySpan<byte> haystack, byte needle)
{if (Vector.IsHardwareAccelerated && haystack.Length >= Vector<byte>.Count){fixed (byte* haystackPtr = &MemoryMarshal.GetReference(haystack)){Vector<byte> target = new Vector<byte>(needle);byte* current = haystackPtr;byte* endMinusOneVector = haystackPtr + haystack.Length - Vector<byte>.Count;do{if (Vector.EqualsAny(target, *(Vector<byte>*)current)){return true;}current += Vector<byte>.Count;}while (current < endMinusOneVector);if (Vector.EqualsAny(target, *(Vector<byte>*)endMinusOneVector)){return true;}}}else{for (int i = 0; i < haystack.Length; i++){if (haystack[i] == needle){return true;}}}return false;
}

上面的代码几乎是完美的,测试下基准

private byte[] _data = Enumerable.Repeat((byte)123, 999).Append((byte)42).ToArray();//Enumerable.Repeat表示999个123的byte,放在数组,最后又加了一个42数值到数组[Benchmark(Baseline = true)]
[Arguments((byte)42)]
public bool Find(byte value) => Contains(_data, value); // just the fallback path in its own method[Benchmark]
[Arguments((byte)42)]
public bool FindVectorized(byte value) => Contains_Vectorized(_data, value); // the implementation we just wrote|         Method | value |      Mean |    Error |   StdDev | Ratio | Code Size |
|--------------- |------ |----------:|---------:|---------:|------:|----------:|
|           Find |    42 | 508.42 ns | 2.336 ns | 2.185 ns |  1.00 |     110 B |
| FindVectorized |    42 |  21.57 ns | 0.342 ns | 0.303 ns |  0.04 |     253 B |

可以看到矢量化之后的性能,进行了夸张的25倍的增长。这段代码几乎完美,但是并不完美。这里是用的1000个元素测试,如果是小于30个元素呢?有两个方法,第一个是退回到没有矢量化的代码也就是Contains函数,第二个是把Vector切换到128位来操作。代码如下,几乎没变更:

static unsafe bool Contains128(ReadOnlySpan<byte> haystack, byte needle)
{if (Vector128.IsHardwareAccelerated && haystack.Length >= Vector128<byte>.Count){ref byte current = ref MemoryMarshal.GetReference(haystack);Vector128<byte> target = Vector128.Create(needle);ref byte endMinusOneVector = ref Unsafe.Add(ref current, haystack.Length - Vector128<byte>.Count);do{if (Vector128.EqualsAny(target, Vector128.LoadUnsafe(ref current))){return true;}current = ref Unsafe.Add(ref current, Vector128<byte>.Count);}while (Unsafe.IsAddressLessThan(ref current, ref endMinusOneVector));if (Vector128.EqualsAny(target, Vector128.LoadUnsafe(ref endMinusOneVector))){return true;}}else{for (int i = 0; i < haystack.Length; i++){if (haystack[i] == needle){return true;}}}return false;
}

来进行一个基准测试:

private byte[] _data = Enumerable.Repeat((byte)123, 29).Append((byte)42).ToArray();[Benchmark(Baseline = true)]
[Arguments((byte)42)]
public bool Find(byte value) => Contains(_data, value);[Benchmark]
[Arguments((byte)42)]
public bool FindVectorized(byte value) => Contains_Vectorized(_data, value);|         Method | value |      Mean |     Error |    StdDev | Ratio | Code Size |
|--------------- |------ |----------:|----------:|----------:|------:|----------:|
|           Find |    42 | 16.363 ns | 0.1833 ns | 0.1530 ns |  1.00 |     110 B |
| FindVectorized |    42 |  1.799 ns | 0.0320 ns | 0.0299 ns |  0.11 |     191 B |

同样的性能进行了16倍的提速。

结尾

作者:江湖评谈
欢迎关注公众号:jianghupt。文章首发。image


http://www.ppmy.cn/news/360012.html

相关文章

Java 实战介绍 Cookie 和 Session 的区别

HTTP 是一种不保存状态的协议&#xff0c;即无状态协议&#xff0c;HTTP 协议不会保存请求和响应之间的通信状态&#xff0c;协议对于发送过的请求和响应都不会做持久化处理。 无状态协议减少了对服务压力&#xff0c;如果一个服务器需要处理百万级用户的请求状态&#xff0c;对…

手替厨师透露行业秘密:给演员做菜用的冰箱是什么样的?

你知道那些影视剧里美味的大餐都是怎么做出来的吗&#xff1f;爱豆们真的多才多艺可以做出那么美味的大餐吗&#xff1f;其实不少出自手替之手。尤其是那些“美食剧”剧组&#xff0c;除了要有专业手替&#xff0c;还需要配备一台高品质冰箱&#xff0c;而且马上双十一了&#…

美的工业技术永磁电机等四项关键技术获得“国际领先”鉴定 | 美通社头条

美通社消息&#xff1a;5月27日&#xff0c;由美的工业技术主导的“高功率密度永磁电机及压缩机关键技术研究与应用”、“高能效R290转子压缩机关键技术研究及应用”、“绿色高品质薄型化永磁电机关键技术研究及应用”以及“准两级往复式压缩机及制冷系统关键技术研究与应用”&…

PS 工具获取:Photoshop CS6超级免安装精简版来临!不到200M!

工具获取&#xff1a; 小蓝枣的资源仓库&#xff0c;提取码&#xff1a;kmji 解压后才 170 多M&#xff01;&#xff01;&#xff01; 使用方法&#xff1a; 解压后&#xff0c;直接点击里面的 Photoshop.exe 就可以运行了&#xff0c;嫌麻烦的话可以添加一个快捷方式在桌面上…

Photoshop下载附免费资源

简介 Adobe Photoshop&#xff0c;简称“PS”&#xff0c;是由Adobe Systems开发和发行的图像处理软件。Photoshop主要处理以像素所构成的数字图像。使用其众多的编修与绘图工具&#xff0c;可以有效地进行图片编辑和创造工作。PS 有很多功能&#xff0c;在图像、图形、文字、…

全家桶大礼包Adobe Photoshop免费自取免费

下载安装视频教程 链接&#xff1a;https://pan.baidu.com/s/1w5p-PAgILe_GM35Kx6BFNA 提取码&#xff1a;EC21 教程视频 教程视频&#xff1a; 链接: https://pan.baidu.com/s/1vcJ_9S6EjTUjKYwGBs3Aow 提取码: vvfs 复制这段内容后打开百度网盘手机App&#xff0c;操作更方…

adobe Photoshop CS6 MAC中文版

安装教程 1、首先下载好安装包 2、安装开始前请断网&#xff0c;不然需要申请一个adobe id 3、双击开始安装&#xff0c;然后选择试用&#xff0c;一直到安装完成。 4、依旧是断网状态&#xff0c;打开ps软件&#xff0c;&#xff08;先联网还是会让输入ADOBE 的id和密码的&am…

水清冷冷:Photoshop CC 2020/PSCC 2020安装教程及学习技巧(附工具)

工具地址在底部↓↓ Adobe Photoshop cc 2020&#xff0c;简称“PSCC 2020”&#xff0c;是一款实用的强大图像处理工具,。它是实用的修图做图工具&#xff0c;利用丰富的绘图工具&#xff0c;可以有效地进行图片编辑工作。PSCC 2020在AI方面更加强大&#xff0c;让你图像处理更…

【文末下载地址】Adobe Photoshop 2022原版免注册免登录来啦

WIN系统版本 了解 Photoshop 桌面版 2021 年 10 月版&#xff08;版本 23.0&#xff09;中的新增功能和增强功能。 adobe PS 2022启动画面 以下是关于新增功能介绍&#xff0c;信息来源自adobe官网信息。 悬停时自动选择 “对象选择”是常见的 Photoshop 工作流程中不可或缺…

工业4G路由器 小体积4G LTE通信模块转有线转WiFi充电桩视频安防监控物联网路由器上网CPE

4G LTE代表第四代长期演进&#xff0c;这是一种用于通过蜂窝网络提供高速数据传输的无线通信技术。它是移动网络技术的最新标准&#xff0c;提供比其前身3G更快的数据传输速度。它广泛用于移动设备、物联网设备和机器对机器通信。 近年来&#xff0c;随着物联网技术的快速发展…

基于Hexo和Butterfly创建个人技术博客,(3) 创建博客文章及文章模板配置

Hexo官司网查看 这里 笔者个人站查看 这里 特别说明&#xff1a; hexo博客站点发布的文件全是静态文件&#xff0c;没有任何后台服务。博文的发布过程是&#xff1a;1、在本地用hexo new命令创建.md文件----2、经hexo g命令生成.html文件-----3、再通过hexo d命令发布到远程主机…

Linux内核中内存管理相关配置项的详细解析11

接前一篇文章&#xff1a;Linux内核中内存管理相关配置项的详细解析10 二十一、Track memory changes 对应配置变量为&#xff1a;CONFIG_MEM_SOFT_DIRTY。 此项只有选中和不选中两种状态&#xff0c;默认为选中。 内核源码详细解释为&#xff1a; This option enables memo…

探索现代软件架构:揭秘单体、SOA和微服务的进化的之路

1、单体服务、SOA、微服务区别 单体服务 是指一个应用程序中所有的功能都集成在一个单一的代码库中。这种设计模式简单易用&#xff0c;开发人员可以快速地开发和维护应用程序&#xff0c;但是也存在一些问题。例如&#xff0c;当应用程序需要添加新功能时&#xff0c;需要对整…

原木奶油风设计|打造恬静温暖的生活空间

目光所及皆是温柔的现代极简原木风打造自然恬静的自然美~ 以现代简约的设计勾勒出空间的格调&#xff0c;餐客厅一体化让空间更加的整洁 奶油白主调与自然原木色营造出的温暖柔和的自然美以通透自然舒适的理念让生活回归静谧、柔和、简洁、自然 这个家像奶油棉花糖般的柔软…

防排烟风管的防火材料有哪些?耐火极限是多少?

在建筑中&#xff0c;通风管道需要具备一定的耐火性能&#xff0c;因为一旦发生火灾&#xff0c;如果通风管道没有耐火保护&#xff0c;将会造成火势的蔓延扩大。因此&#xff0c;在防排烟风管中使用耐火材料是非常重要的。 防排烟风管的耐火极限取决于建筑设计规范和生产标准等…

江水平装修队:一个装修队的企业文化

装修界有个习惯&#xff0c;把一些客户看的懂的报价搞的很低&#xff0c;比如贴瓷砖&#xff0c;却把一些客户看不懂的报价搞成天价&#xff0c;比如水电&#xff0c;比如木工背景墙吊顶&#xff1b;签下合约后再由项目经理上下其手&#xff0c;这个工种给那个工种补。 但江水平…

怎么选择一款安心的墙布产品?-江南爱窗帘十大品牌

随着经济条件的改善&#xff0c;人们对生活品质的追求越来越高&#xff0c;对家装的要求也越来越高。但是&#xff0c;在墙面装饰行业&#xff0c;各种墙布良莠不齐&#xff0c;或甲醛含量超标&#xff0c;或成品工艺瑕疵给消费者带来了严重的困扰。那如何选择一款好的墙布产品…

当下流行的五大别墅庭院照明亮化设计

别墅景观设计是高档住宅项目的市场需要&#xff0c;别墅景观照明亮化怎么才能符合业主的品味呢&#xff1f;集丰照明设计景观亮化&#xff0c;按照多年从业经验&#xff0c;从专业的角度为朋友们分享&#xff1a;当下较为流行的五大庭院照明亮化发展趋势。 1&#xff0e;别墅庭…

【奶油侘寂风格装修案例】营造柔和的高颜值空间!

火爆全网的奶油系风格&#xff0c;既有着现代极简的空间感&#xff0c;也有着法式浪漫雅致空灵的情调&#xff01; 奶油系家装风格&#xff0c;顾名思义就是选取奶油一样轻盈温柔的颜色&#xff0c;进行色彩的搭配。 清清淡淡之间呈现出软糯的温柔质感&#xff0c;透出优雅恬…

QT实战之智能浴霸

QT实战之智能浴霸 Linux/Windows均可用,更改随机值部分可自行连接传感器,sqlite数据库可自行更改为其他数据库。 界面设计 控件及界面美化 QLabel QLineEdit QPushButton QRadioButton QTextBrowser QCheckBox QMessageBox QgroupBox QProgressBar 主窗口及子窗口各一张背…
最新文章