日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
.NET中密封類(lèi)的性能優(yōu)勢(shì),你知道幾個(gè)?

Intro

目前看到的一些類(lèi)庫(kù)中其實(shí)很多并沒(méi)有考慮使用密封類(lèi),如果你的類(lèi)型是不希望被繼承的,或者不需要被重寫(xiě)的,那么就應(yīng)該考慮聲明為密封類(lèi),尤其是對(duì)于類(lèi)庫(kù)項(xiàng)目的作者來(lái)說(shuō),這其實(shí)是非常值得考慮的一件事情,很多優(yōu)秀的類(lèi)庫(kù)都會(huì)考慮這樣的問(wèn)題,尤其是 .NET 框架里的一些代碼,大家看開(kāi)源項(xiàng)目源碼的時(shí)候也可以留意一下。

Preface

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

事實(shí)上,當(dāng)一個(gè)類(lèi)被密封時(shí),JIT可以進(jìn)行一些優(yōu)化,并稍微提升應(yīng)用程序的性能。

在 .NET 7 中應(yīng)該會(huì)有一個(gè)新的分析器來(lái)檢測(cè)可以被密封的類(lèi)。在這篇文章中,我將展示這個(gè) issue https://github.com/dotnet/runtime/issues/49944 中提到的密封類(lèi)的一些性能優(yōu)勢(shì)。

性能優(yōu)勢(shì)

虛方法調(diào)用

當(dāng)調(diào)用虛方法時(shí),實(shí)際的方法是在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類(lèi)型找到的。每個(gè)類(lèi)型都有一個(gè)虛擬方法表(vtable),其中包含所有虛擬方法的地址。這些指針在運(yùn)行時(shí)被用來(lái)調(diào)用適當(dāng)?shù)姆椒▽?shí)現(xiàn)(動(dòng)態(tài)執(zhí)行)。

如果JIT知道對(duì)象的實(shí)際類(lèi)型,它可以跳過(guò)vtable,直接調(diào)用正確的方法以提高性能。使用密封類(lèi)型有助于JIT,因?yàn)樗啦荒苡腥魏闻缮?lèi)。

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() { }
}

方法

算術(shù)平均值

誤差

方差

中位數(shù)

比率

代碼大小

NonSealed

0.4465 ns

0.0276 ns

0.0258 ns

0.4437 ns

1.00

18 B

Sealed

0.0107 ns

0.0160 ns

0.0150 ns

0.0000 ns

0.02

7 B

請(qǐng)注意,當(dāng) JIT 可以確定實(shí)際類(lèi)型時(shí),即使類(lèi)型沒(méi)有密封,它也可以使用直接調(diào)用。例如,以下兩個(gè)片段之間沒(méi)有區(qū)別:

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
}

對(duì)象類(lèi)型轉(zhuǎn)換 (is / as)

當(dāng)對(duì)象類(lèi)型轉(zhuǎn)換時(shí),CLR 必須在運(yùn)行時(shí)檢查對(duì)象的類(lèi)型。當(dāng)轉(zhuǎn)換到一個(gè)非密封的類(lèi)型時(shí),運(yùn)行時(shí)必須檢查層次結(jié)構(gòu)中的所有類(lèi)型。然而,當(dāng)轉(zhuǎn)換到一個(gè)密封的類(lèi)型時(shí),運(yùn)行時(shí)必須只檢查對(duì)象的類(lèi)型,所以它的速度更快。

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 {}

方法

平均值

誤差

方差

中位數(shù)

Is_NonSealed

1.6560 ns

0.0223 ns

0.0208 ns

1.00

Is_Sealed

0.1505 ns

0.0221 ns

0.0207 ns

0.09

數(shù)組 Arrays

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

協(xié)變會(huì)帶來(lái)性能上的損失。事實(shí)上,JIT在將一個(gè)項(xiàng)目分配到數(shù)組之前必須檢查對(duì)象的類(lèi)型。當(dāng)使用密封類(lèi)型時(shí),JIT可以取消檢查。你可以查看 Jon Skeet 的文章 https://codeblog.jonskeet.uk/2013/06/22/array-covariance-not-just-ugly-but-slow-too/ 來(lái)獲得更多關(guān)于性能損失的細(xì)節(jié)。

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 { }
方法平均值誤差方

方法

平均值

誤差

方差

中位數(shù)

比率

NonSealed

3.420 ns

0.0897 ns

0.0881 ns

1.00

44 B

Sealed

2.951 ns

0.0781 ns

0.0802 ns

0.86

58 B

數(shù)組轉(zhuǎn)換成 Span

你可以將數(shù)組轉(zhuǎn)換為 Span 或 ReadOnlySpan。出于與前面部分相同的原因,JIT在將數(shù)組轉(zhuǎn)換為 Span 之前必須檢查對(duì)象的類(lèi)型。當(dāng)使用一個(gè)密封的類(lèi)型時(shí),可以避免檢查并稍微提高性能。

public class SealedBenchmark
{
SealedType[] sealedTypeArray = new SealedType[100];
NonSealedType[] nonSealedTypeArray = new NonSealedType[100];

[Benchmark(Baseline = true)]
public Span NonSealed() => nonSealedTypeArray;

[Benchmark]
public Span Sealed() => sealedTypeArray;
}

public class BaseType {}
public class NonSealedType : BaseType { }
public sealed class SealedType : BaseType { }

方法

平均值

誤差

方差

中位數(shù)

比率

NonSealed

0.0668 ns

0.0156 ns

0.0138 ns

1.00

64 B

Sealed

0.0307 ns

0.0209 ns

0.0185 ns

0.50

35 B

檢測(cè)不可達(dá)的代碼

當(dāng)使用密封類(lèi)型時(shí),編譯器知道一些轉(zhuǎn)換是無(wú)效的。所以,它可以報(bào)告警告和錯(cuò)誤。這可能會(huì)減少你的應(yīng)用程序中的錯(cuò)誤,同時(shí)也會(huì)刪除不可到達(dá)的代碼。

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 { }

尋找可以被密封的類(lèi)型

Meziantou.Analyzer 包含一個(gè)規(guī)則,可以檢查可能被密封的類(lèi)型。

dotnet add package Meziantou.Analyzer

它應(yīng)該使用 MA0053 報(bào)告任何可以被密封的internal 類(lèi)型:

你也可以通過(guò)編輯 .editorconfig文件指示分析器報(bào)告 public類(lèi)型。

[*.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 這樣的工具來(lái)解決這個(gè)問(wèn)題。

dotnet format analyzers --severity info

注意:在.NET 7中,這應(yīng)該是 CA1851 的標(biāo)準(zhǔn)靜態(tài)分析的一部分 https://github.com/dotnet/roslyn-analyzers/pull/5594

補(bǔ)充說(shuō)明

所有的基準(zhǔn)都是使用以下配置運(yùn)行的:

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 RyuJIT
DefaultJob : .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

從上面的解釋和基準(zhǔn)測(cè)試中我們可以看到一些密封類(lèi)為我們帶來(lái)的好處,我們?cè)谠O(shè)計(jì)一個(gè)類(lèi)型的時(shí)候就應(yīng)該去考慮這個(gè)類(lèi)型是不是允許被繼承,如果不允許被繼承,則應(yīng)該考慮將其聲明為 sealed,如果你有嘗試過(guò) Sonar Cloud 這樣的靜態(tài)代碼分析工具,你也會(huì)發(fā)現(xiàn),有一些 private 的類(lèi)型如果沒(méi)有聲明為 sealed 就會(huì)被報(bào)告為 Code Smell 一個(gè)代碼中的壞味道

除了性能上的好處,首先將一個(gè)類(lèi)型聲明為 sealed 可以實(shí)現(xiàn)更好的 API 兼容性,如果從密封類(lèi)變成一個(gè)非密封類(lèi)不是一個(gè)破壞性的變更,但是從一個(gè)非密封類(lèi)變成一個(gè)密封類(lèi)是一個(gè)破壞性的變更

希望大家在自己的類(lèi)庫(kù)項(xiàng)目中新建類(lèi)型的時(shí)候會(huì)思考一下是否該將其聲明為 sealed,除此之外可以不 public 的類(lèi)型可以聲明為 internal,不 public 不必要的類(lèi)型,希望有越來(lái)越多更好更高質(zhì)量的開(kāi)源項(xiàng)目

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


網(wǎng)站名稱(chēng):.NET中密封類(lèi)的性能優(yōu)勢(shì),你知道幾個(gè)?
地址分享:http://m.5511xx.com/article/cdpdjcg.html