.NET7是如何優化Guid.Equals性能的?

簡介

在之前的文章中,我們多次提到 Vector - SIMD 技術,也答應大家在后面分享更多.NET7 中優化的例子,今天就帶來一個使用 SIMD 優化Guid.Equals()方法性能的例子。

為什么 Guid 能使用 SIMD 優化?

首先就需要介紹一些背景知識,那就是Guid它是什么,在我們人類眼中,Guid就是一串字符串,如下方所示的那樣。

"D313CD46-2724-7359-84A0-9E73C861CCD2"

而在定義中,全局唯一標識符(GUID,Globally Unique Identifier)是一種由算法生成的二進制長度為128 位的數字標識符。GUID 主要用于在擁有多個節點、多臺計算機的網絡或系統中。在理想情況下,任何計算機和計算機集群都不會生成兩個相同的 GUID。GUID 的總數達到了 2^128(3.4×10^38)個,所以隨機生成兩個相同 GUID 的可能性非常小,但并不為 0。GUID 一詞有時也專指微軟對 UUID 標準的實現。

大家可以看到我著重標記了它的位數是128 位,128 位意味著什么?就是如果比較兩個 Guid 是否相等的話,不管是 64 位 CPU 還是 32 位的 CPU 需要多條指令比較多次。如果我們用上了 Vector?是不是會有更好的性能呢?

首先我們來看看 Guid 是如何定義的,看看能不能直接讀取 128 位數據,從而用上 Vector。Guid 它是值類型的,是一個結構體。代碼如下所示,我省略了部分信息。

public?readonly?partial?struct?Guid{...private?readonly?int?_a;???//?Do?not?rename?(binary?serialization)private?readonly?short?_b;?//?Do?not?rename?(binary?serialization)private?readonly?short?_c;?//?Do?not?rename?(binary?serialization)private?readonly?byte?_d;??//?Do?not?rename?(binary?serialization)private?readonly?byte?_e;??//?Do?not?rename?(binary?serialization)private?readonly?byte?_f;??//?Do?not?rename?(binary?serialization)private?readonly?byte?_g;??//?Do?not?rename?(binary?serialization)private?readonly?byte?_h;??//?Do?not?rename?(binary?serialization)private?readonly?byte?_i;??//?Do?not?rename?(binary?serialization)private?readonly?byte?_j;??//?Do?not?rename?(binary?serialization)private?readonly?byte?_k;??//?Do?not?rename?(binary?serialization)...}

可以看到它由 1 個 32 位 int,2 個 16 位的 short 和 8 個 8 位的 byte 組成,至于為什么需要這樣組成,其實是一個標準化的東西,為了在生成和序列化時更快。

我們使用ObjectLayoutInspector可以打印出 Guid 的數據結構,數據結果如下圖所示,和我們源碼里面看到的一致:

914623c687af4fed282bf86da6ac8a1a.png

那么 Guid 是否能使用 SIMD 優化的結論顯而易見:

  • Guid 有 128 位,現在 CPU 都是 64 位或者 32 位,還存在提升空間

  • Guid 是結構體類型,結構體類型在內存中是連續存儲,我們可以直接讀取內存來訪問整個結構體

SIMD 優化代碼

根據我們前面文章中,Min 和 Max 方法在.NET7 被優化的經驗,我們可以直接寫下面這樣的代碼。

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private?static?bool?EqualsCore(in?Guid?left,?in?Guid?right)
{//?檢測硬件是否支持Vector128if?(Vector128.IsHardwareAccelerated){//?支持Vector128就好辦了,直接加載比較return?Vector128.LoadUnsafe(ref?Unsafe.As<Guid,?byte>(ref?Unsafe.AsRef(in?left)))?==?Vector128.LoadUnsafe(ref?Unsafe.As<Guid,?byte>(ref?Unsafe.AsRef(in?right)));}//?如果不支持,那么從Guid頭部讀取內存//?32位比較四次ref?int?rA?=?ref?Unsafe.AsRef(in?left._a);ref?int?rB?=?ref?Unsafe.AsRef(in?right._a);return?rA?==?rB&&?Unsafe.Add(ref?rA,?1)?==?Unsafe.Add(ref?rB,?1)&&?Unsafe.Add(ref?rA,?2)?==?Unsafe.Add(ref?rB,?2)&&?Unsafe.Add(ref?rA,?3)?==?Unsafe.Add(ref?rB,?3);
}

在上面的代碼中,我們可以看到不僅提供了 Vector 加速的方案,還有不支持回退的場景。不過那段 Vector 代碼是不是不太好理解?我們逐個部分來解析一下。我們首先看左右的部分,右邊也是同樣的意思Vector128.LoadUnsafe(ref Unsafe.As<Guid, byte>(ref Unsafe.AsRef(in left)))

  • ref Unsafe.AsRef(in left) 是獲取 left Guid 它的首地址指針,此時返回的其實是Guid*

  • ref Unsafe.As<Guid, byte>(...)Guid*指針轉換為byte*指針

  • Vector128.LoadUnsafe(...) 由于 Guid 已經變為 Byte 指針,所以就能直接 LoadUnsafe 了

最后 right Guid 也使用相同的方式加載,最后使用==比較兩個Vector是否相等就好了。其實==還使用了CompareEqualMoveMask兩個指令,只是在.NET7 中 JIT 會把兩個向量的比較給優化。看下方圖片中紅色框標記的部分,就是這兩個指令。

5c08807d34276774207a0379e1043db6.png

那么.NET6 下==沒有優化,那該怎么辦呢?根據這里的匯編指令,Meziantou[1]大佬給出了.NET6 下同樣功效的優化代碼:

static?class?GuidExtensions
{public?static?bool?OptimizedGuidEquals(in?Guid?left,?in?Guid?right){if?(Sse2.IsSupported){Vector128<byte>?leftVector?=?Unsafe.ReadUnaligned<Vector128<byte>>(ref?Unsafe.As<Guid,?byte>(ref?Unsafe.AsRef(in?left)));Vector128<byte>?rightVector?=?Unsafe.ReadUnaligned<Vector128<byte>>(ref?Unsafe.As<Guid,?byte>(ref?Unsafe.AsRef(in?right)));//?使用Sse2.CompareEqual()比較是否相等,它的返回值是一個128位向量,如果相等,該位置返回0xffff,否則返回0x0//?CompareEqual的結果是128位的,我們可以通過Sse2.MoveMask()來重新排列成16位,最終看是否等于0xffff就好var?equals?=?Sse2.CompareEqual(leftVector,?rightVector);var?result?=?Sse2.MoveMask(equals);return?(result?&?0xFFFF)?==?0xFFFF;}return?left?==?right;}
}

從下圖的匯編代碼中,可以看到是一樣的效果:2831d55beb1ea1a7033bc6b7a8d031cf.png

總結

最終這一波操作下來,我們可以看到Guid.Equals的性能提升了 30%。如果你的程序中使用 Guid 作為數據庫、對象主鍵的,只需要升級.NET7 或者用上面的GuidExtensions就能獲得這樣的性能提升。b9adb5f45517840ab37b1379bd9f88a9.png

參考資料

[1]

Meziantou: https://www.meziantou.net/faster-guid-comparisons-using-vectors-simd-in-dotnet.htm

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281464.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281464.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281464.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

go語言載入json的一個坑

問題簡介 go語言標準庫匯總內置了對 json 文件的處理&#xff0c;非常方便&#xff0c;最近在寫一個應用的時候&#xff0c;需要從 json 文件中載入 配置&#xff0c;由于是 go 新手&#xff0c;忽略一個細節&#xff0c;導致載入內容始終為空 代碼演示 代碼是最好的說明載體 p…

el表達式 if 和 if else 的寫法

javaScript的if else大家都不會陌生&#xff0c;但可能很多小伙伴并不知道在jsp文件里&#xff0c;el表達式的if else是怎么寫的&#xff0c;下面安利給各位小伙伴。 el表達式 if 代碼示例示例1<c:if test"${jsonObject.transportTypeName eq 火車}"><li>…

Effective_STL 學習筆記(二十七) 用 distance 和 advance 把 const_iterator 轉化成 iterator...

并不存在從 const_iterator 到 iterator 之間的隱式轉換 一種安全的、可移植的方法獲取他所對應的 iterator&#xff1a; 1   typedef deque<int> IntDeque;     // 方便的typedef 2   typedef IntDeque::iterator Iter; 3   typedef IntDeque::const_iterato…

分布式唯一Id

支持.Net/.Net Core/.Net Framework&#xff0c;可以部署在Docker, Windows, Linux, Mac。分布式唯一Id&#xff0c;顧名思義&#xff0c;是指在全世界任何一臺計算機上都不會重復的唯一Id。在單機/單服務器/單數據庫的小型應用中&#xff0c;不需要用到這類東西。但在高并發、…

缺氧游戲黑科技計算機,《缺氧》游戲內參數修改圖文詳解

很多玩家都很喜歡缺氧這款游戲&#xff0c;有時候因為一些不可告人的秘密我們需要修改游戲中的內容來達到簡化我們的生存難度&#xff0c;這樣就需要修改游戲的腳本&#xff0c;所幸《缺氧 》對于這個問題很寬容&#xff0c;完全沒有加密地圖的生成腳本&#xff0c;讓我們可以完…

Python 項目實踐三(Web應用程序)第四篇

接著上節繼續學習&#xff0c;本章將建立用戶賬戶 Web應用程序的核心是讓任何用戶都能夠注冊賬戶并能夠使用它&#xff0c;不管用戶身處何方。在本章中&#xff0c;你將創建一些表單&#xff0c;讓用戶能夠添加主題和條目&#xff0c;以及編輯既有的條目。你還將學習Django如何…

新手想買二手車 先看看買車后這五個步驟吧

買二手車你該知道 很多人因為資金短缺又或者是想要一輛便宜車“練手”而選擇去買一輛價格低廉&#xff0c;有著一定車齡的二手車。很多人看中二手車正正是因為便宜&#xff0c;以為是購買以后基本不需要再投入新的花費&#xff0c;殊不知這是非常錯誤的想法&#xff0c;因為以下…

yii---where or該如何使用

今天調試YII項目的時候&#xff0c;遇到一個奇葩的事兒&#xff0c;在調試 where or 查詢的時候&#xff1a;調試語句是這樣&#xff1a; $str static::find()->where([or,username > $username,mobile > $account]); echo "<br>"; echo $st…

十六進制編輯器--ImHex

十六進制編輯器是用于編輯單個字節數據的軟件應用程序&#xff0c;主要由程序員或系統管理員使用。常規文本編輯器和十六進制編輯器之間的區別在于常規編輯器表示文件的邏輯內容&#xff0c;而十六進制編輯器表示文件的物理內容。十六進制編輯器可以讓你以十六進制的形式查看或…

計算機第一課 教案 紀律,信息技術開學第一課-紀律

《信息技術開學第一課-紀律》由會員分享&#xff0c;可在線閱讀&#xff0c;更多相關《信息技術開學第一課-紀律(3頁珍藏版)》請在人人文庫網上搜索。1、信息技術開學第一課教學目標&#xff1a;1、 了解學生的基礎情況&#xff0c;選出課代表2、 分好學習小組&#xff0c;選出…

奧迪堅SVRM(Screen-Voice Recording Manager)錄屏軟件正式發布

奧迪堅SVRM(Screen-Voice Recording Manager)能夠對座席通話同步錄音的同時進行座席操作錄屏 實時監控座席屏幕操作&#xff0c;及時糾正操作問題。 座席質檢可以邊聽邊看&#xff0c;為KPI考核提供依據。 利用優秀座席操作記錄對座席進行培訓。 監控坐席人員對敏感信息訪問次數…

小米:開源不僅要站在巨人的肩膀上,還要為巨人指方向

今天上午&#xff0c;第一屆小米開源技術峰會在北京舉行&#xff0c;會上&#xff0c;小米人工智能與云平臺副總裁崔寶秋致開場詞&#xff0c;并發表了《小米開源之路》的演講。 崔寶秋強調小米一直在推動開源&#xff0c;也是開源的倡導者。他告訴我們雷軍創立小米的其中一個重…

容器基本操作

docker run --namefang -it image_id /bin/bash 執行的shell docker ps -a 查看所有容器&#xff0c; -l 查看最近運行容器 docker inspect 查看容器的詳細信息 docker start [-i] container_id 或者名字也可以 docker rm 刪除容器docker ps 查看正在運行的容器do…

計算機及相關設備制造業2020,在現實生活中,接近完全壟斷市場類型的行業包括()。A.計算機及相關設備制造業B.稀有...

在現實生活中&#xff0c;接近完全壟斷市場類型的行業包括()。A&#xff0e;計算機及相關設備制造業B&#xff0e;稀有更多相關問題【簡答題】請解釋以下名詞: 制動、緩解、實施制動作用、實施緩解作用、制動距離。【簡答題】請解釋以下名詞: 制動、緩解、實施制動作用、實施緩…

CAS原理

像synchronized這種獨占鎖屬于悲觀鎖&#xff0c;它是在假設一定會發生沖突的&#xff0c;那么加鎖恰好有用&#xff0c;除此之外&#xff0c;還有樂觀鎖&#xff0c;樂觀鎖的含義就是假設沒有發生沖突&#xff0c;那么我正好可以進行某項操作&#xff0c;如果要是發生沖突呢&a…

數據分析師的職業規劃之路

“數據分析師作為一個出現時間不長的工種&#xff0c;大數據時代下&#xff0c;成為螺絲釘還是成為龍頭&#xff0c;需要嘗試新的可能。” 數據分析師手中擁有一座寶藏。作為滴滴出行數據分析團隊的負責人&#xff0c;劉普成發現了數據分析師通往卓越的秘訣&#xff1a;視野。數…

《設計模式》3.結構型模式

點擊進入我的博客 3.1 適配器模式 適配器模式把一個類的接口變換成客戶端所期待的另一種接口&#xff0c;使得原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。 3.1.1 類的適配器結構 目標&#xff08;Target&#xff09;角色&#xff1a;這就是所期待得到的接口&…

最快的計算機操作,世界十大最快的超級計算機

最近&#xff0c;《聯邦儲備技術》雜志對全球超級計算機進行了排名&#xff0c;并從中選出了十個最快的超級計算機. 其中&#xff0c;中國有兩臺超級計算機進入了榜單&#xff0c;而“天河2號”則依靠雙精度浮點算術峰. 速度達到了每秒5490億次&#xff0c;占據了王位.這也是兩…

蘋果iOS 10.3.1修復博通Wi-Fi芯片重大安全漏洞

如果你還沒有將設備升級到 iOS 10.3.1 的話&#xff0c;那么現在是個機會了。因為不久前發布的 iOS 10.3.1&#xff0c;修復了 iPhone 中博通 Wi-Fi 芯片的一個重大安全漏洞&#xff0c;該安全漏洞可能會使在 Wi-Fi 范圍內的攻擊者在智能手機上注入并運行代碼。 Google Project…

計算機開機跳過硬盤檢查,如何設置開機跳過檢測硬盤

如何設置開機跳過檢測硬盤導讀&#xff1a;很多win用戶都想設置開機跳過檢測硬盤&#xff0c;能夠更加快速的進入系統&#xff0c;今天百分網小編給大家整理出了方法。1、系統的問題系統解決在Windows界面下&#xff0c;通過“磁盤碎片整理”程序來完成。首先啟動“CMD”鍵入“…