轻量化动态编译库 Natasha v8.0 正式发布!
.NET8.0 與 動(dòng)態(tài)編譯
Hello 各位小伙伴,我于 2024年1月10日 發(fā)布了 Natasha 一個(gè)全新的里程碑版本 v8.0,對(duì)于老用戶而言,此次發(fā)布版本號(hào)跨度較大,是因?yàn)槲覜Q定使用新的版本號(hào)計(jì)劃,主版本號(hào)將隨 Runtime 版本號(hào)的增加而增加。
淺談 .NET8.0
在 .NET8.0 Runtime 方向的深度解析文章出來之前,八卦了一些新聞,例如前階段的文檔收費(fèi)風(fēng)波,就那一段時(shí)間我覺得粥里的烏江榨菜都不香了;又目睹馬某某說微軟不開源,這損犢子玩意,以馬祭碼吧。
.NET8.0 更新的東西真的太多了,僅作為八卦聊聊,比如略低分配的異步狀態(tài)機(jī),ConfigureAwait 的改進(jìn),Win 線程池與托管線程池的切換,減少了 GCHandle 濫用的 Socket,向量運(yùn)算提升字符操作的性能等,這么串起來,整個(gè)網(wǎng)絡(luò)通信技術(shù)棧均有受益,照這么下去,.NET10 時(shí) Asp.net Core 的相關(guān)性能測(cè)試跑第一也不是不可能的了。話說回來,其中官方比較看重的一項(xiàng)提升 “SearchValues”。官方引入 "SearchValues.Create()" API 根據(jù)要查找的字符來返回不同的策略實(shí)現(xiàn),本質(zhì)上是為了緩解 O(M*N) 的性能問題,定位算法策略多達(dá) 10 種,包括簡(jiǎn)單的"四八取值定位",純 Ascii 字符定位,范圍定位,向量運(yùn)算定位,位圖網(wǎng)格定位等(算法名都沒找到,字面意思幫助理解), 除了向量算法屬于普通應(yīng)用盲區(qū),其他算法都蠻好理解的,向量算法可以參考時(shí)總寫過的一些博客。
動(dòng)態(tài)編譯 與 Natasha
在介紹新版之前,必須讓新來者了解動(dòng)態(tài)編譯相關(guān)的知識(shí),動(dòng)態(tài)編譯在 .NET 生態(tài)中一直扮演著重要角色,例如 Dapper , Json.net , AutoMapper , EFCore , 動(dòng)態(tài)編譯版 Razor ,
Orleans 等類庫(kù)中或多或少都存在動(dòng)態(tài)編譯相關(guān)的代碼,在 Source Generators 出現(xiàn)之前 [運(yùn)行時(shí)動(dòng)態(tài)] 一直是建設(shè) .NET 生態(tài)的重要技能,但繁重的 IL 以及 Expression 代碼無疑不給開發(fā)者帶來巨大的維護(hù)和升級(jí)成本,不僅如此,在執(zhí)行性能上, Emit 方法的執(zhí)行性性能只能趨近于原生編譯,并不能超過(這里糾正一下看到某篇文章提到 emit 要比原代代碼編譯執(zhí)行快的觀點(diǎn)), 然而 SG 以及 AOT 兼容性方案的出現(xiàn)不僅解決了一些動(dòng)態(tài)代碼性能上的問題,還讓 .NET 生態(tài)順利開展出另個(gè)分支,即 AOT 生態(tài)。
說到 AOT, 在啟動(dòng)耗時(shí)過長(zhǎng),內(nèi)存拮據(jù),服務(wù)端對(duì)發(fā)布包大小有嚴(yán)格限制這三類場(chǎng)景中,AOT 如今已經(jīng)成為開發(fā)界所熱衷的方案。.NET8.0 中更加全面的支持了 AOT,Asp.net Core 推出了 WebApplication.CreateSlimBuilder() 作為 Web 的 AOT 方案,在 .NET8.0 發(fā)布后,除了官方類庫(kù),應(yīng)該屬老葉的 FreeSql 在動(dòng)靜兼容上做的是又快又穩(wěn)了。在 .NET8.0 之前更早實(shí)現(xiàn)兼容方案的,不得不提九哥的 WebApiClient。即使有這些前車之鑒,Natasha 也無法參考。 Natasha 作為動(dòng)態(tài)編譯類庫(kù)不得不站在 .NET8.0 的另一個(gè)重大技術(shù)特性之上,即 .NET8.0 默認(rèn)開啟的動(dòng)態(tài) PGO 優(yōu)化。AOT 并不能作為最佳的性能選擇方案,相反運(yùn)行時(shí)的最簡(jiǎn)動(dòng)態(tài)策略以及對(duì)機(jī)器碼的動(dòng)態(tài)優(yōu)化更加合適性能敏感場(chǎng)景。在此之前我也曾仔細(xì)想過 [編譯時(shí)動(dòng)態(tài)] 的適用場(chǎng)景有多么廣泛,能否取代 [運(yùn)行時(shí)動(dòng)態(tài)],結(jié)論是不管 [編譯時(shí)動(dòng)態(tài)] 有多么優(yōu)秀也無法取代 [運(yùn)行時(shí)動(dòng)態(tài)],徹底放棄 [運(yùn)行時(shí)動(dòng)態(tài)] 的做法也是十分欠考慮的,而且在純動(dòng)態(tài)業(yè)務(wù)的場(chǎng)景中 SG 以及 AOT 方案是十分無力的,此時(shí)需要一個(gè) [運(yùn)行時(shí)動(dòng)態(tài)] 方案來達(dá)到業(yè)務(wù)目標(biāo),這里推薦使用 Natasha.
那么使用新版 Natasha 來完成 [運(yùn)行時(shí)動(dòng)態(tài)] 的相關(guān)功能有什么好處呢?
答案是高效快速、輕量方便,智能省心.
Natasha 是基于 Roslyn 開發(fā)的,它允許將 C# 腳本代碼轉(zhuǎn)換成程序集,并享受編譯優(yōu)化、運(yùn)行時(shí)優(yōu)化帶來的性能提升;在易用性上新版 Natasha 組件層次分明,API 規(guī)范可查,在保證靈活性的同時(shí)還封裝了很多細(xì)節(jié);在擴(kuò)展性上 Natasha 每次更新都會(huì)盡量挖掘 Roslyn 的隱藏功能給大家使用;在封裝粒度上,Natasha 自有一套減少用戶編譯成本的方案,讓更多的細(xì)節(jié)變得透明,接下來可以看一看新版 Natasha 都有哪些變化。
Natasha 項(xiàng)目地址:https://github.com/dotnetcore/Natasha
Natasha8.0 的新顏
開發(fā)相關(guān)
Natasha 應(yīng)用用了前一篇文章提到的 CI/CD Runner,并加以實(shí)戰(zhàn)改造,在 PR 管理,ISSUE 管理等管道功能上得到了便利的支持。
此版本我們有三個(gè)大方向上的編碼任務(wù),分別是功能性上的輕量化路線,擴(kuò)展性上的動(dòng)態(tài)方法使用率統(tǒng)計(jì),以及兼容性上對(duì) standard2.0 的支持。
Natasha 從本次更新起,停止了對(duì)非 LTS .NET 版本進(jìn)行 UT 測(cè)試,在開發(fā)者使用非 LTS 版本 Runtime 時(shí)小概率可會(huì)出現(xiàn)意外情況,若遇到可提交 issue。
一. API 命名規(guī)范
-
With 系列 API: 帶有關(guān)閉、排除、枚舉附加值等條件狀態(tài)開關(guān)時(shí)使用的 API。 例如:
WithCombineUsingCode和WithoutCombineUsingCode,WithHighVersionDependency、WithLowVersionDependency、WithDebugCompile、WithReleaseCompile、WithFileOutput等,又例如編譯選項(xiàng)的 API 都是作為附加條件賦給選項(xiàng)的,因此都由 With 開頭(注:與 Roslyn 風(fēng)格不同,With 方法不返回新對(duì)象). -
Set 系列 API: 屬單向賦值類 API, 例如:
SetDllFilePath、SetReferencesFilter等. -
Config 系列 API: 具有對(duì)主類中,某重要組件的額外配置,通常是各類 options 操作的 API, 例如:
ConfigCompilerOption、ConfigSyntaxOptions等. -
特殊功能 API: 此類 API 需要非常獨(dú)立且明確的功能,常用而顯眼,例如
UseRandomDomain、UseSmartMode、OutputAsFullAssembly、GetAssembly等顯眼包 API.
二. 性能提升
新版 Natasha 使用并發(fā)的方式將兩種預(yù)熱方法(引用/實(shí)現(xiàn)程序集預(yù)熱)的執(zhí)行時(shí)間從 .NET8.0 實(shí)驗(yàn)環(huán)境的 2-4s 降低到了 0.700 -1 s 左右;預(yù)熱的內(nèi)存漲幅從 60-70M 降到 30-40M。
新版 Natasha 允許開發(fā)者靈活管理元數(shù)據(jù)覆蓋策略,比如
- 合并共享域及當(dāng)前域的元數(shù)據(jù).
- 僅使用當(dāng)前域元數(shù)據(jù).
- 僅使用指定的元數(shù)據(jù)等.
這使得 Natasha 可以支持自定義輕量化編譯,在實(shí)驗(yàn)案例中輕量化編譯比預(yù)熱編譯節(jié)省了約 15M 左右的內(nèi)存。
以下是引用程序集與實(shí)現(xiàn)程序集的預(yù)熱耗時(shí)統(tǒng)計(jì)截圖
引用程序集預(yù)熱
實(shí)現(xiàn)程序集預(yù)熱
以下是引用程序集與實(shí)現(xiàn)程序集的預(yù)熱內(nèi)存統(tǒng)計(jì)截圖
引用程序集預(yù)熱
實(shí)現(xiàn)程序集預(yù)熱
三. Standard2.0 兼容方案
新版編譯單元的依賴項(xiàng)變?yōu)榱?Standard2.0, 編譯單元項(xiàng)目移除了 System.Reflection.MetadataLoadContext 依賴包, Natasha 將直接從文件中提取元數(shù)據(jù),避免一些繁瑣的加載操作,另外我們還移除了對(duì) DotNetCore.Natasha.Domain 的依賴,盡管域?qū)τ?Natasha 來說十分重要,域作為 Runtime 的重要特性,它嚴(yán)重牽制著 Natasha 的兼容性,為此我對(duì) Natasha 的框架進(jìn)行了重新設(shè)計(jì),將域以及一些運(yùn)行時(shí)方法交由第三方去實(shí)現(xiàn),而 Natasha 只保留和調(diào)用 Standard2.0 的接口,這兩個(gè)接口為 DotNetCore.Natasha.DynamicLoad.Base 包中的 INatashaDynamicLoadContextBase 和 INatashaDynamicLoadContextCreator,開發(fā)者可以根據(jù)兩個(gè)接口自行實(shí)現(xiàn)域功能,但這里 Core3.0 以上版本我推薦使用 DotNetCore.Natasha.CSharp.Compiler.Domain Natasha 官方實(shí)現(xiàn)的域功能,該包繼承自 DotNetCore.Natasha.Domain , 這是一個(gè)功能強(qiáng)大且穩(wěn)定的 .NET 域?qū)崿F(xiàn)包。
當(dāng)然 Natasha 的使用方式也發(fā)生了一些變化:
//首先向 Natasha 加載上下文中注入域創(chuàng)建者實(shí)現(xiàn)類 NatashaDomainCreator
//NatashaDomainCreator 來自包 DotNetCore.Natasha.CSharp.Compiler.Domain,實(shí)現(xiàn)了 INatashaDynamicLoadContextCreator 接口
NatashaManagement.RegistDomainCreator<NatashaDomainCreator>();
//若需要預(yù)熱,也可以直接使用泛型預(yù)熱,泛型預(yù)熱將自動(dòng)調(diào)用 NatashaManagement.RegistDomainCreator<NatashaDomainCreator>();
NatashaManagement.Preheating<NatashaDomainCreator>(false, false);
與此同時(shí),新版 Natasha 解耦了編譯單元及模板,部分開發(fā)者在使用 Natasha 時(shí)習(xí)慣自己構(gòu)建腳本代碼,而不需要 Natasha 本身模板的參與,為此我們解耦了模板與編譯單元的相關(guān)代碼,現(xiàn)在您可以引用 DotNetCore.Natasha.CSharp.Template.Core 來使用模板的相關(guān)功能,或者單獨(dú)引用 DotNetCore.Natasha.CSharp.Compiler 僅使用編譯單元的功能。
對(duì)于運(yùn)行時(shí)目前區(qū)分了 "Core" 和 "Framewokr" 版本,"Core" 相關(guān)的代碼將繼續(xù)維護(hù)著,而與 "Framework" 相關(guān)的代碼任務(wù)已經(jīng)停止,從去年年底我已無精力去做 Framework 的兼容工作,經(jīng)濟(jì)來源對(duì)于 2024 年的我來說是個(gè)巨大難題,更多的思考與嘗試都將圍繞著如何維持生活來展開,但是 Natasha 會(huì)接受 PR,接受開源貢獻(xiàn)者的代碼。如果您不想使用上一版本的 Framework 實(shí)現(xiàn),不介意您聯(lián)系我進(jìn)行有償定制,這里也希望諸各位的公司項(xiàng)目早日脫離 Framework 苦海。
四. 域的改進(jìn)
提到動(dòng)態(tài)編譯不得不說的一個(gè)前提就是“域”,再次強(qiáng)調(diào)這里所說的域是 .NETCore3.0 + 版本的 ALC (程序集加載上下文),Natasha 對(duì) ALC 進(jìn)行了較全面的封裝,您可以單獨(dú)引用 Natasha.Domain 以便進(jìn)行插件加載等操作,
本次更新我對(duì)域操作進(jìn)行了修正與補(bǔ)充:
- Natasha 實(shí)現(xiàn)的 ALC 將避開依賴程序集的重復(fù)加載。
- 我發(fā)現(xiàn)之前的代碼中,在共享域加載為主的邏輯中,ALC 默認(rèn)將程序集交由共享域處理,共享域處理不過接由當(dāng)前域處理,新版本在確定共享域存在程序集的情況下,將直接返回共享域的程序集,無需另外處理。
- 在依賴程序集被排除的情況下,如果該程序集在共享域中存在,將返回共享域的程序集。
新增 Natasha.CSharp.Compiler.Domain 項(xiàng)目繼承 Natasha.Domain 項(xiàng)目并實(shí)現(xiàn)基礎(chǔ)編譯接口。
使用域加載插件
domain1.LoadPluginXXX(file)
在 Natasha 中使用加載插件,并加載插件元數(shù)據(jù)及 Using Code.
var loadContext = DomainManagement.Random();
//或
var loadContext = (new AssemblyCSharpBuilder().UseRandomDomain()).LoadContext;
var domain = (NatashaDomain)(loadContext.Domain);
//排除基類接口,否則你反射出來的方法只能在當(dāng)前域編碼使用(更多詳情請(qǐng)學(xué)習(xí)微軟官方關(guān)于插件的相關(guān)知識(shí))。
Func<AssemblyName, bool>? excludeInterfaceBase= item => item.Name!.Contains("IPluginBase");
//獲取插件程序集.
var assembly = domain.LoadPluginWithHighDependency(file, excludeInterfaceBase);
//添加元數(shù)據(jù)以及 using code.
loadContext.AddReferenceAndUsingCode(assembly, excludeInterfaceBase);
五. 元數(shù)據(jù)管理優(yōu)化
元數(shù)據(jù)以及 using code 對(duì)于 Roslyn 編譯來說屬于重點(diǎn)依賴對(duì)象,新版 Natasha 增加了 NatashaLoadContext 來管理元數(shù)據(jù),在 vs 開發(fā)過程中,由于動(dòng)態(tài)腳本沒有智能提示和隱式 using 覆蓋,因此早期 Natasha 推出了透明模式,讓元數(shù)據(jù)管理變得透明不可見,預(yù)熱過程將緩存元數(shù)據(jù)和 using code,使用時(shí)自動(dòng)覆蓋元數(shù)據(jù)引用以及 using code。對(duì)于 using code 的全覆蓋,類似于近期 vs 推出的隱式 usings 的功能,Natasha 還為編譯單元增加了語義過濾器的支持,以便自動(dòng)處理編譯診斷。
同時(shí) NatashaLoadContext 還支持解析實(shí)現(xiàn)程序集和引用程序集,早期 Natasha 僅在預(yù)熱時(shí)會(huì)緩存引用程序集的元數(shù)據(jù),而如今,Natasha 不僅支持兩種程序集的預(yù)熱還支持在不預(yù)熱的情況下允許開發(fā)者自管理元數(shù)據(jù)。
/// <summary>
/// 預(yù)熱方法,調(diào)用此方法之前需要調(diào)用 RegistDomainCreator<TCreatorT> 確保域的創(chuàng)建
/// </summary>
/// <param name="excludeReferencesFunc"></param>
/// <param name="useRuntimeUsing">是否使用實(shí)現(xiàn)程序集的 using code</param>
/// <param name="useRuntimeReference">是否使用實(shí)現(xiàn)程序集的元數(shù)據(jù)</param>
/// <param name="useFileCache">是否使用 using 緩存</param>
public static void Preheating(
Func<AssemblyName?, string?, bool>? excludeReferencesFunc,
bool useRuntimeUsing = false,
bool useRuntimeReference = false,
bool useFileCache = false);
預(yù)熱案例1: 自動(dòng)注入域?qū)崿F(xiàn),從內(nèi)存中的 [實(shí)現(xiàn)程序集] 中提取元數(shù)據(jù)和 using code.
NatashaManagement.Preheating<NatashaDomainCreator>(true, true);
預(yù)熱案例2: 手動(dòng)注入域?qū)崿F(xiàn), 從 refs 文件夾下的 [引用程序集] 中提取元數(shù)據(jù)和 using code. (需提前引入 DotNetCore.Compile.Environment 包).
NatashaManagement.RegistDomainCreator<NatashaDomainCreator>();
NatashaManagement.Preheating(false, false);
預(yù)熱案例3: 自動(dòng)注入域?qū)崿F(xiàn),從 refs 文件夾下的 [引用程序集] 中提取 using code. (需提前引入 DotNetCore.Compile.Environment 包),從內(nèi)存中的[實(shí)現(xiàn)程序集]中提取元數(shù)據(jù), 此種方法一旦運(yùn)行過一次,就會(huì)產(chǎn)生 using 緩存文件,此時(shí)即使刪除 refs 文件夾程序仍會(huì)正常工作.
NatashaManagement.Preheating<NatashaDomainCreator>(false, true, true);
六. 多種編譯模式
1. 智能編譯模式
使用智能編譯模式,編譯單元 AssemblyCSharpBuilder 將默認(rèn)合并 共享加載上下文(NatashaLoadContext.DefaultContext) 和 當(dāng)前上下文(builder.LoadContext) 的元數(shù)據(jù)以及 using code,并自動(dòng)開啟語義過濾,如下是較完整的使用代碼:
1.若不使用內(nèi)存程序集,則需要引入 DotNetCore.Compile.Environment 來輸出引用程序集。
2.預(yù)熱并注冊(cè)域?qū)崿F(xiàn)。
3.啟用智能模式編碼。
NatashaManagement.Preheating<NatashaDomainCreator>();
AssemblyCSharpBuilder builder = new();
var myAssembly = builder
.UseRandomDomain()
.UseSmartMode() //啟用智能模式
.Add("public class A{ }")
.GetAssembly();
2. 輕便編譯模式
新版 Natasha 允許開發(fā)者使用編譯單元進(jìn)行輕量級(jí)編譯,如果您只是想創(chuàng)建一個(gè)計(jì)算表達(dá)式或者一個(gè)簡(jiǎn)單邏輯的映射,建議您使用編譯單元的輕便模式進(jìn)行動(dòng)態(tài)編譯。輕便模式不會(huì)合并主域的元數(shù)據(jù)和 using 代碼,只會(huì)使用當(dāng)前域的,并且不會(huì)觸發(fā)語義過濾。
AssemblyCSharpBuilder builder = new();
builder
.UseRandomDomain()
.UseSimpleMode() //啟用輕便模式
.ConfigLoadContext(ldc=> ldc
.AddReferenceAndUsingCode(typeof(Math).Assembly)
.AddReferenceAndUsingCode(typeof(MathF))
.AddReferenceAndUsingCode(typeof(object)))
.Add("public static class A{ public static int Test(int a, int b){ return a+b; } }");
var func = builder
.GetAssembly()
.GetDelegateFromShortName<Func<int,int,int>>("A", "Test");
func(1,2);
3. 自定義編譯模式
AssemblyCSharpBuilder builder = new();
builder
.UseRandomDomain()
.WithSpecifiedReferences(元數(shù)據(jù)集合)
.WithoutCombineUsingCode()
.WithReleaseCompile()
.Add("using System.Math; using System; public static class A{ public static int Test(int a, int b){ return a+b; } }");
其中 WithSpecifiedReferences 方法允許您傳入引用集合,例如 Roslyn 成員提供的Basic.Reference.Assemblies引用程序集包。由于案例中指定了 WithoutCombineUsingCode 方法,該方法將不會(huì)自動(dòng)覆蓋 using code, 因此腳本中需要手動(dòng)添加 using code例如 using System;。
七. 動(dòng)態(tài)調(diào)試
新版本 Natasha 允許在編譯單元在指定 Debug 編譯模式后,使用 VS 進(jìn)入到方法內(nèi)進(jìn)行調(diào)試.
同時(shí)這里介紹一種隱藏的 Release 模式,該模式允許在生成程序集時(shí)攜帶有 Debug 相關(guān)的信息,之前被定義為 Debug 的 Plus 版本/可調(diào)試的 Release 模式,還可以增加您反編譯時(shí)的可讀性(這個(gè)功能 Roslyn 隨后幾個(gè)版本可能會(huì)加入到優(yōu)化級(jí)別的枚舉中暴露給開發(fā)者)。
也許我們已經(jīng)在 VS 中體驗(yàn)過了?這個(gè)功能后續(xù)我會(huì)繼續(xù)跟進(jìn)測(cè)試研究。
//調(diào)試信息寫入文件,原始的寫入方式,對(duì) Win 平臺(tái)支持良好
builder.WithDebugCompile(item=>item.WriteToFileOriginal())
//調(diào)試信息寫入文件,兼容性寫入方式
builder.WithDebugCompile(item=>item.WriteToFile())
//調(diào)試信息整合到程序集中
builder.WithDebugCompile(item=>item.WriteToAssembly())
//Release 發(fā)布無法進(jìn)行調(diào)試
builder.WithReleaseCompile()
//Release 模式將攜帶 debugInfo 一起輸出
builder.WithFullReleaseCompile()
案例
AssemblyCSharpBuilder builder = new();
builder
.UseRandomDomain()
.UseSimpleMode()
.WithDebugCompile(item => item.WriteToAssembly())
.ConfigLoadContext(ldc=> ldc
.AddReferenceAndUsingCode(typeof(object).Assembly)
.AddReferenceAndUsingCode(typeof(Math).Assembly)
.AddReferenceAndUsingCode(typeof(MathF).Assembly));
builder.Add(@"
namespace MyNamespace{
public class A{
public static int N1 = 10;
public static float N2 = 1.2F;
public static double N3 = 3.44;
private static short N4 = 0;
public static object Invoke(){
int[] a = [1,2,3];
return N1 + MathF.Log10((float)Math.Sqrt(MathF.Sqrt(N2) + Math.Tan(N3)));
}
}
}
");
var method = builder
.GetAssembly()
.GetDelegateFromShortName<Func<object>>("A", "Invoke");
//斷點(diǎn)調(diào)試此行代碼
var result = method();
八. 程序集輸出
Natasha 8.0 版本允許您在動(dòng)態(tài)編譯完成后輸出完整程序集或引用程序集,注意這里并沒有進(jìn)行什么智能判斷,需要您手動(dòng)控制行為,域加載引用程序集會(huì)引發(fā)異常。請(qǐng)看以下例子來達(dá)到僅輸出的目的。
//編譯結(jié)果為引用程序集,且寫入文件,且不會(huì)加載到域。
builder
.OutputAsRefAssembly();
.WithFileOutput()
.WithoutInjectToDomain();
注: 如果您希望把 Natasha 作為一個(gè)插件生產(chǎn)器,那么很遺憾,目前它并不能像 VS 編輯器那樣輸出完整的依賴以及依賴文件。
九. 輸出文件
Natasha 支持 dll/pdb/xml 文件輸出,其中 xml 存儲(chǔ)了程序集注釋相關(guān)的信息。參考 API
//該方法將使程序集輸出到默認(rèn)文件夾下的 dll/pdb/xml 文件中
//可傳入一個(gè)文件夾路徑
//可以傳入三個(gè)文件的路徑
builder.WithFileOutput(string dllFilePath, string? pdbFilePath = null, string? commentFilePath = null)
builder.WithFileOutput(string? folder = null);
//分離的 API
builder.SetDllFilePath/SetPdbFilePath/SetCommentFilePath();
周邊擴(kuò)展
一. 動(dòng)態(tài)程序集方法使用率統(tǒng)計(jì)
眾所周知,單元測(cè)試中測(cè)試方法覆蓋率統(tǒng)計(jì)通常使用 VS 自帶的工具進(jìn)行靜態(tài)統(tǒng)計(jì),還有 CLI 工具,這里 Natasha 推出一種新的擴(kuò)展,允許開發(fā)者動(dòng)態(tài)的統(tǒng)計(jì)[由 Natasha 生成的動(dòng)態(tài)程序集]中的[方法]使用情況,目前已通過測(cè)試,并發(fā)布了第一個(gè)擴(kuò)展包。此項(xiàng)技術(shù)還需要搜集需求和建議,因此我們的 ISSUE 被設(shè)置為 phase-done,歡迎大家留言提需求和建議。
使用方法:
- 引入
DotNetCore.Natasha.CSharp.Extension.Codecov擴(kuò)展包。 - 編碼并獲取結(jié)果。
builder.WithCodecov();
Assembly asm = builder.GetAssembly();
List<(string MethodName, bool[] Usage)>? list = asm.GetCodecovCollection();
情景假設(shè): A 類中有方法 Method , Method 方法體共 6 行代碼邏輯,在執(zhí)行過程中僅執(zhí)行了前4行。
result 集合如下:
"MyNamespace.A.Method":
[0] = true,
[1] = true,
[2] = true,
[3] = true,
[4] = false,
[5] = false,
二. 動(dòng)態(tài)只讀字典
目前該庫(kù)還是維護(hù)狀態(tài),因?yàn)樗莾H Natasha 關(guān)鍵項(xiàng)目之外的最重要項(xiàng)目,但目前沒有隨著 Natasha 發(fā)布新版。
動(dòng)態(tài)只讀字典通過正向特征樹算法,計(jì)算最小查找次數(shù)(權(quán)值)來動(dòng)態(tài)構(gòu)建一段查找代碼,并交由 Natasha 編譯,并提供 GetValue 、 TryGetValue 、Change 、索引操作。
我對(duì) .NET8.0 推出的凍結(jié)字典進(jìn)行了性能對(duì)比,對(duì)比環(huán)境 .NET8.0, 字典類型 FrozenDictionary<string, string>, 對(duì)比結(jié)果:
凍結(jié)字典除非后續(xù)在 JIT 動(dòng)態(tài)優(yōu)化出更簡(jiǎn)潔高效的代碼,否則它無法在這個(gè)場(chǎng)景中超越動(dòng)態(tài)字典,主打性能的類庫(kù)越精細(xì)越不好優(yōu)化,特征算法目前來講十分復(fù)雜且構(gòu)建低效,在特征過多時(shí)構(gòu)建延遲十分明顯,代碼上需要進(jìn)行優(yōu)化與重構(gòu),Swifter.Json 作者提出了差異算法,且經(jīng)過案例推演也證實(shí)差異算法在某些場(chǎng)景中可以取得更小的權(quán)值,因此我們需要引入差異算法來與特征算法形成競(jìng)爭(zhēng),對(duì)于代碼腳本來說,下一步我將使用更高效的 Runtime API 來提高代碼執(zhí)行性能,爭(zhēng)取在下一個(gè)版本呢取得更好的性能,后續(xù)我們還將橫向?qū)Ρ?Indexof / SearchValue 等高性能查找算法,以確定在特殊情況下是否能夠借鑒 Runtime 中的算法來提升性能。
在性能過剩的今天,ConcurrentDictionay 已經(jīng)滿足大部分人的需求了,這個(gè)類庫(kù)沒有帶給我任何金錢收益和榮譽(yù)成就,甚至至今為止也未受到過任何需求,因此此庫(kù)優(yōu)先級(jí)對(duì)我來說很低,對(duì)一個(gè)初級(jí)算法都不到的人來說,這庫(kù)挺令我頭疼,也許最好的走向是讓一個(gè)英語好的,頭腦思路清晰的小伙子把算法思路提交給官方,讓官方動(dòng)態(tài)優(yōu)化凍結(jié)字典。
結(jié)尾
即便 Roslyn 版的 Natasha 已經(jīng)發(fā)布幾年時(shí)間,但我對(duì) Roslyn 仍然有一種陌生且無力的感覺,Roslyn 文檔少的可憐,更多的功能還需要自己去研究挖掘,我會(huì)將一些提上日程的重要開發(fā)計(jì)劃發(fā)布到 issue 中并征集意見,例如:https://github.com/dotnetcore/Natasha/issues/240 , 開發(fā)不易,求個(gè) Star。
Natasha 項(xiàng)目地址:https://github.com/dotnetcore/Natasha
Natasha 文檔地址: https://natasha.dotnetcore.xyz/zh-Hans/docs/ (文檔站點(diǎn)技術(shù)更新,稍晚俞佬將進(jìn)行修復(fù)和上傳)
總結(jié)
以上是生活随笔為你收集整理的轻量化动态编译库 Natasha v8.0 正式发布!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Skywalking(8.7)安装以及d
- 下一篇: 面试官:禁用Cookie后Session