C# 泛型編譯特性對(duì)性能的影響小結(jié)
C#作為一種強(qiáng)類型語(yǔ)言,具有豐富的泛型支持,允許開發(fā)者編寫可以應(yīng)對(duì)不同數(shù)據(jù)類型的通用代碼。然而,在泛型編譯時(shí),針對(duì)結(jié)構(gòu)和類作為泛型參數(shù)時(shí),會(huì)對(duì)性能產(chǎn)生不同的影響。
泛型編譯行為
在C#中,泛型編譯行為取決于泛型參數(shù)的類型。具體而言,當(dāng)泛型參數(shù)是結(jié)構(gòu)(Struct)時(shí),編譯器會(huì)針對(duì)每個(gè)具體的結(jié)構(gòu)類型生成特定的實(shí)現(xiàn)。而當(dāng)泛型參數(shù)是類(Class)時(shí),編譯器則可能生成更通用的實(shí)現(xiàn)。
結(jié)構(gòu) vs 類
結(jié)構(gòu)(Struct)
結(jié)構(gòu)是值類型,它們存儲(chǔ)在棧上,具有較小的內(nèi)存開銷。當(dāng)泛型參數(shù)是結(jié)構(gòu)時(shí),編譯器會(huì)針對(duì)每個(gè)具體的結(jié)構(gòu)類型生成專門的實(shí)現(xiàn),這可能導(dǎo)致更高的性能。因?yàn)槊總€(gè)結(jié)構(gòu)類型都有自己的實(shí)現(xiàn),避免了裝箱和拆箱的開銷,同時(shí)優(yōu)化了內(nèi)存分配和訪問(wèn)。
類(Class)
類是引用類型,存儲(chǔ)在堆上,需要通過(guò)引用進(jìn)行訪問(wèn)。當(dāng)泛型參數(shù)是類時(shí),編譯器可能生成更通用的實(shí)現(xiàn)。這可能導(dǎo)致較低的性能,因?yàn)橥ㄓ脤?shí)現(xiàn)需要進(jìn)行動(dòng)態(tài)調(diào)度和引用類型的操作,增加了一些開銷。
測(cè)試性能差異
針對(duì)不同的泛型參數(shù)進(jìn)行性能測(cè)試是一種有效的方法,以觀察結(jié)構(gòu)和類對(duì)泛型編譯特性的影響。在測(cè)試中,可能會(huì)發(fā)現(xiàn)對(duì)結(jié)構(gòu)類型的泛型參數(shù),其性能可能更高,而對(duì)類類型的泛型參數(shù),其性能可能略低。
using System.Diagnostics; namespace ConsoleApp1 { internal interface IValueGetter { int GetValue(int index); } internal class MyTestClass<T> where T : IValueGetter { private readonly T _valueGetter; public MyTestClass(T valueGetter) { _valueGetter = valueGetter; } public void Run() { long r = 0L; for (int i = 0; i < int.MaxValue; i++) { r += _valueGetter.GetValue(i); } } } internal struct StructValueGetter : IValueGetter { public readonly int GetValue(int index) { return index + 3; } } internal struct StructValueGetter2(int someField) : IValueGetter { public readonly int GetValue(int index) { return index + 5; } } internal class ClassValueGetter1 : IValueGetter { public int GetValue(int index) { return index + 5; } } internal class ClassValueGetter2 : IValueGetter { public int GetValue(int index) { return index + 7; } } internal static class Demo2 { public static void Run() { var t1 = new MyTestClass<StructValueGetter>(new StructValueGetter()); RunDemo("StructValueGetter ", t1.Run); var t2 = new MyTestClass<ClassValueGetter1>(new ClassValueGetter1()); RunDemo("ClassValueGetter1 ", t2.Run); var t3 = new MyTestClass<ClassValueGetter2>(new ClassValueGetter2()); RunDemo("ClassValueGetter2 ", t3.Run); var t4 = new MyTestClass<IValueGetter>(new ClassValueGetter1()); RunDemo("IValueGetter-1 ", t4.Run); var t5 = new MyTestClass<ClassValueGetter1>(new ClassValueGetter1()); RunDemo("ClassValueGetter1 ", t5.Run); var t6 = new MyTestClass<StructValueGetter2>(new StructValueGetter2()); RunDemo("StructValueGetter2", t6.Run); var t7 = new MyTestClass<IValueGetter>(new ClassValueGetter2()); RunDemo("IValueGetter-2 ", t7.Run); var t8 = new MyTestClass<IValueGetter>(new StructValueGetter()); RunDemo("IValueGetter-3 ", t8.Run); var t9 = Activator.CreateInstance(typeof(MyTestClass<>).MakeGenericType(typeof(StructValueGetter)), new StructValueGetter()); Action action9 = (Action)Delegate.CreateDelegate(typeof(Action), t9, t9.GetType().GetMethod("Run")); RunDemo("Dynamic-Struct ", action9); } static void RunDemo(string caption, Action action) { var stopWatch = Stopwatch.StartNew(); action(); stopWatch.Stop(); Console.WriteLine($"{caption} time = {stopWatch.Elapsed}"); } } } Demo2.Run();
在.net 8.0 Release 編譯執(zhí)行的參考結(jié)果如下:
StructValueGetter time = 00:00:00.6920186
ClassValueGetter1 time = 00:00:01.1887137
ClassValueGetter2 time = 00:00:05.2889692
IValueGetter-1 time = 00:00:01.1652195
ClassValueGetter1 time = 00:00:01.1625259
StructValueGetter2 time = 00:00:00.6488674
IValueGetter-2 time = 00:00:05.2114724
IValueGetter-3 time = 00:00:07.1394676
Dynamic-Struct time = 00:00:00.6491220
結(jié)論
泛型編譯特性對(duì)性能有所影響,我們發(fā)現(xiàn):
- 泛型參數(shù)是 Struct 比 class 的性能要好,大約有兩倍的差異;
- 泛型參數(shù)如果存在多個(gè) Struct 可能時(shí),性能沒(méi)有影響,但如果泛型參數(shù)存在多個(gè) class 可能時(shí),性能急劇下降5倍之多;
- 泛型參數(shù)如果是接口形式,無(wú)論實(shí)際填充的結(jié)構(gòu)還是類,其最終的執(zhí)行性能一定是很慢的;
- 使用反射(例如:MakeGenericType)構(gòu)建出的泛型實(shí)例,其實(shí)際運(yùn)行性能并不受影響,非常適合高度定制的運(yùn)行時(shí)類型構(gòu)建,這一點(diǎn)非常重要,例如你可以在運(yùn)行時(shí)檢測(cè)實(shí)際情況,構(gòu)建出不同的比較器對(duì)象,雖然構(gòu)建的工廠方法返回的是接口,但你可以使用反射的方式動(dòng)態(tài)傳入字典的比較器參數(shù)(實(shí)際上c#的
Dictionary<TKey, TValue>
這點(diǎn)設(shè)計(jì)是失敗的,他的comparer
不是一個(gè)泛型參數(shù),而是接口);
綜上所述,了解C#泛型編譯特性對(duì)性能的影響是編寫高性能代碼的重要一部分,合理使用對(duì)于關(guān)鍵性代碼性能至關(guān)重要。
到此這篇關(guān)于C# 泛型編譯特性對(duì)性能的影響 的文章就介紹到這了,更多相關(guān)C# 泛型編譯特性內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#統(tǒng)計(jì)C、C++及C#程序代碼行數(shù)的方法
這篇文章主要介紹了C#統(tǒng)計(jì)C、C++及C#程序代碼行數(shù)的方法,較為詳細(xì)的分析了C#統(tǒng)計(jì)文本文件的原理與相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08Unity圖形學(xué)之ShaderLab入門基礎(chǔ)
Unity中所有Shader文件都通過(guò)一種陳述性語(yǔ)言進(jìn)行描述,稱為“ShaderLab”, 這篇文章主要介紹了Unity圖形學(xué)之ShaderLab入門基礎(chǔ),需要的朋友可以參考下2022-01-01C#異步迭代IAsyncEnumerable應(yīng)用實(shí)現(xiàn)
IAsyncEnumerable可以來(lái)實(shí)現(xiàn)異步迭代,本文就主要介紹了C#異步迭代IAsyncEnumerable應(yīng)用實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06