日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET 中密封类的性能优势

發布時間:2023/12/4 asp.net 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET 中密封类的性能优势 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

.NET 中密封類的性能優勢

Intro

最近看到一篇文章 Performance benefits of sealed class in .NET,覺得寫得不錯,翻譯一下,分享給大家。

目前看到的一些類庫中其實很多并沒有考慮使用密封類,如果你的類型是不希望被繼承的,或者不需要被重寫的,那么就應該考慮聲明為密封類,尤其是對于類庫項目的作者來說,這其實是非常值得考慮的一件事情,很多優秀的類庫都會考慮這樣的問題,尤其是 .NET 框架里的一些代碼,大家看開源項目源碼的時候也可以留意一下。

Preface

默認情況下,類是不密封的。這意味著你可以從它們那里繼承。我認為這并不是正確的默認行為。事實上,除非一個類被設計成可以繼承,否則它應該被密封。如果有需要,你仍然可以在以后刪除 sealed 修飾符。除了不是最好的默認值之外,它還會影響性能。

事實上,當一個類被密封時,JIT可以進行一些優化,并稍微提升應用程序的性能。

在 .NET 7 中應該會有一個新的分析器來檢測可以被密封的類。在這篇文章中,我將展示這個 issue https://github.com/dotnet/runtime/issues/49944 中提到的密封類的一些性能優勢。

性能優勢

虛方法調用

當調用虛方法時,實際的方法是在運行時根據對象的實際類型找到的。每個類型都有一個虛擬方法表(vtable),其中包含所有虛擬方法的地址。這些指針在運行時被用來調用適當的方法實現(動態執行)。

如果JIT知道對象的實際類型,它可以跳過vtable,直接調用正確的方法以提高性能。使用密封類型有助于JIT,因為它知道不能有任何派生類。

public?class?SealedBenchmark {readonly?NonSealedType?nonSealedType?=?new();readonly?SealedType?sealedType?=?new();[Benchmark(Baseline?=?true)]public?void?NonSealed(){//?The?JIT?cannot?know?the?actual?type?of?nonSealedType.?Indeed,//?it?could?have?been?set?to?a?derived?class?by?another?method.//?So,?it?must?use?a?virtual?call?to?be?safe.nonSealedType.Method();}[Benchmark]public?void?Sealed(){//?The?JIT?is?sure?sealedType?is?a?SealedType.?As?the?class?is?sealed,//?it?cannot?be?an?instance?from?a?derived?type.//?So?it?can?use?a?direct?call?which?is?faster.sealedType.Method();} }internal?class?BaseType {public?virtual?void?Method()?{?} } internal?class?NonSealedType?:?BaseType {public?override?void?Method()?{?} } internal?sealed?class?SealedType?:?BaseType {public?override?void?Method()?{?} }方法算術平均值誤差方差中位數比率代碼大小
NonSealed0.4465 ns0.0276 ns0.0258 ns0.4437 ns1.0018 B
Sealed0.0107 ns0.0160 ns0.0150 ns0.0000 ns0.027 B

請注意,當 JIT 可以確定實際類型時,即使類型沒有密封,它也可以使用直接調用。例如,以下兩個片段之間沒有區別:

void?NonSealed() {var?instance?=?new?NonSealedType();instance.Method();?//?The?JIT?knows?`instance`?is?NonSealedType?because?it?is?set//?in?the?method?and?never?modified,?so?it?uses?a?direct?call }void?Sealed() {var?instance?=?new?SealedType();instance.Method();?//?The?JIT?knows?instance?is?SealedType,?so?it?uses?a?direct?call }

對象類型轉換 (is / as)

當對象類型轉換時,CLR 必須在運行時檢查對象的類型。當轉換到一個非密封的類型時,運行時必須檢查層次結構中的所有類型。然而,當轉換到一個密封的類型時,運行時必須只檢查對象的類型,所以它的速度更快。

public?class?SealedBenchmark {readonly?BaseType?baseType?=?new();[Benchmark(Baseline?=?true)]public?bool?Is_Sealed()?=>?baseType?is?SealedType;[Benchmark]public?bool?Is_NonSealed()?=>?baseType?is?NonSealedType; }internal?class?BaseType?{} internal?class?NonSealedType?:?BaseType?{} internal?sealed?class?SealedType?:?BaseType?{}方法平均值誤差方差中位數
Is_NonSealed1.6560 ns0.0223 ns0.0208 ns1.00
Is_Sealed0.1505 ns0.0221 ns0.0207 ns0.09

數組 Arrays

.NET中的數組是支持協變的。這意味著,BaseType[] value = new DerivedType[1] 是有效的。而其他集合則不是這樣的。例如,List<BaseType> value = new List<DerivedType>(); 是無效的。

協變會帶來性能上的損失。事實上,JIT在將一個項目分配到數組之前必須檢查對象的類型。當使用密封類型時,JIT可以取消檢查。你可以查看 Jon Skeet 的文章 https://codeblog.jonskeet.uk/2013/06/22/array-covariance-not-just-ugly-but-slow-too/ 來獲得更多關于性能損失的細節。

public?class?SealedBenchmark {SealedType[]?sealedTypeArray?=?new?SealedType[100];NonSealedType[]?nonSealedTypeArray?=?new?NonSealedType[100];[Benchmark(Baseline?=?true)]public?void?NonSealed(){nonSealedTypeArray[0]?=?new?NonSealedType();}[Benchmark]public?void?Sealed(){sealedTypeArray[0]?=?new?SealedType();}}internal?class?BaseType?{?} internal?class?NonSealedType?:?BaseType?{?} internal?sealed?class?SealedType?:?BaseType?{?}方法平均值誤差方差中位數比率
NonSealed3.420 ns0.0897 ns0.0881 ns1.0044 B
Sealed2.951 ns0.0781 ns0.0802 ns0.8658 B

數組轉換成 Span

你可以將數組轉換為 Span<T> 或 ReadOnlySpan<T>。出于與前面部分相同的原因,JIT在將數組轉換為 Span<T> 之前必須檢查對象的類型。當使用一個密封的類型時,可以避免檢查并稍微提高性能。

public?class?SealedBenchmark {SealedType[]?sealedTypeArray?=?new?SealedType[100];NonSealedType[]?nonSealedTypeArray?=?new?NonSealedType[100];[Benchmark(Baseline?=?true)]public?Span<NonSealedType>?NonSealed()?=>?nonSealedTypeArray;[Benchmark]public?Span<SealedType>?Sealed()?=>?sealedTypeArray; }public?class?BaseType?{} public?class?NonSealedType?:?BaseType?{?} public?sealed?class?SealedType?:?BaseType?{?}方法平均值誤差方差中位數比率
NonSealed0.0668 ns0.0156 ns0.0138 ns1.0064 B
Sealed0.0307 ns0.0209 ns0.0185 ns0.5035 B

檢測不可達的代碼

當使用密封類型時,編譯器知道一些轉換是無效的。所以,它可以報告警告和錯誤。這可能會減少你的應用程序中的錯誤,同時也會刪除不可到達的代碼。

class?Sample {public?void?Foo(NonSealedType?obj){_?=?obj?as?IMyInterface;?//?ok?because?a?derived?class?can?implement?the?interface}public?void?Foo(SealedType?obj){_?=?obj?is?IMyInterface;?//????Warning?CS0184_?=?obj?as?IMyInterface;?//???Error?CS0039} }public?class?NonSealedType?{?} public?sealed?class?SealedType?{?} public?interface?IMyInterface?{?}

尋找可以被密封的類型

Meziantou.Analyzer 包含一個規則,可以檢查可能被密封的類型。

dotnet?add?package?Meziantou.Analyzer

它應該使用 ?MA0053 報告任何可以被密封的internal 類型:

你也可以通過編輯 .editorconfig文件指示分析器報告 public類型。

[*.cs] dotnet_diagnostic.MA0053.severity = suggestion# Report public classes without inheritors (default: false) MA0053.public_class_should_be_sealed = true# Report class without inheritors even if there is virtual members (default: false) MA0053.class_with_virtual_member_shoud_be_sealed = true

你可以使用像 dotnet format 這樣的工具來解決這個問題。

dotnet?format?analyzers?--severity?info

注意:在.NET 7中,這應該是 CA1851 的標準靜態分析的一部分 https://github.com/dotnet/roslyn-analyzers/pull/5594

補充說明

所有的基準都是使用以下配置運行的:

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000 AMD Ryzen 7 5800X, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.100-preview.2.22153.17[Host] : .NET 6.0.3 (6.0.322.12309), X64 RyuJITDefaultJob : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT

其他資源

  • Why Are So Many Of The Framework Classes Sealed?

  • Analyzer Proposal: Seal internal/private types

More

從上面的解釋和基準測試中我們可以看到一些密封類為我們帶來的好處,我們在設計一個類型的時候就應該去考慮這個類型是不是允許被繼承,如果不允許被繼承,則應該考慮將其聲明為 sealed,如果你有嘗試過 Sonar Cloud 這樣的靜態代碼分析工具,你也會發現,有一些 private 的類型如果沒有聲明為 sealed 就會被報告為 Code Smell 一個代碼中的壞味道

除了性能上的好處,首先將一個類型聲明為 sealed 可以實現更好的 API 兼容性,如果從密封類變成一個非密封類不是一個破壞性的變更,但是從一個非密封類變成一個密封類是一個破壞性的變更

希望大家在自己的類庫項目中新建類型的時候會思考一下是否該將其聲明為 sealed,除此之外可以不 public 的類型可以聲明為 internal,不 public 不必要的類型,希望有越來越多更好更高質量的開源項目

原文地址:https://www.meziantou.net/performance-benefits-of-sealed-class.htm

閱讀原文,查看作者原文

總結

以上是生活随笔為你收集整理的.NET 中密封类的性能优势的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。