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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

DotNetAnywhere:可供选择的 .NET 运行时

發(fā)布時(shí)間:2023/12/4 asp.net 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DotNetAnywhere:可供选择的 .NET 运行时 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

我最近在收聽一個(gè)名為DotNetRock?的優(yōu)質(zhì)播客,其中有以Knockout.js而聞名的Steven Sanderson?正在討論?" WebAssembly And Blazor "。

也許你還沒聽過,Blazor?正試圖憑借WebAssembly的魔力將 .NET 帶入到瀏覽器中。如果您想了解更多信息,Scott Hanselmen 已經(jīng)在?" .NET和WebAssembly——這會(huì)是前端的未來嗎? "一文中做了一番介紹。( 點(diǎn)擊查看該文的翻譯)。

盡管 WebAssembly 非常酷炫,然而更讓我感興趣的是 Blazor 如何使用DotNetAnywhere作為底層的 .NET 運(yùn)行時(shí)。本文將討論DotNetAnywhere 是什么,能做什么,以及同完整的 .NET Framework 做比較。


DotNetAnywhere

首先值得指出的是,DotNetAnywhere (DNA) 被設(shè)計(jì)為一個(gè)完全兼容的 .NET 運(yùn)行時(shí),可以運(yùn)行被完整的.NET 框架編譯的 dll 和 exe 。除此之外 (至少在理論上)?支持?以下的.NET 運(yùn)行時(shí)的功能,真是令人激動(dòng)!

  • 泛型

  • 垃圾收集和析構(gòu)

  • 弱引用

  • 完整的異常處理 - try/catch/finally

  • PInvoke

  • 接口

  • 委托

  • 事件

  • 可空類型

  • 一維數(shù)組

  • 多線程

另外對(duì)于反射提供部分支持

  • 非常有限的只讀方法
    typeof(), GetType(), Type.Name, Type.Namespace, Type.IsEnum(),

最后,還有一些目前不支持的功能:

  • 屬性

  • 大部分的反射方法

  • 多維數(shù)組

  • Unsafe 代碼

各種各樣的錯(cuò)誤或缺少的功能可能會(huì)讓代碼無法在 DotNetAnywhere下運(yùn)行,但其中一些已經(jīng)被Blazor 修復(fù),所以值得時(shí)不時(shí)檢查 Blazor 的發(fā)布版本。

如今,DotNetAnywhere 的原始倉(cāng)庫(kù)不再活躍?(最后一個(gè)持續(xù)的活動(dòng)是在2012年1月),所以未來任何的開發(fā)或錯(cuò)誤修復(fù)都可能在 Blazor 的倉(cāng)庫(kù)中執(zhí)行。如果你曾經(jīng)在 DotNetAnywhere 中修復(fù)過某些東西,可以考慮在那里發(fā)一個(gè)PR。

更新:還有其他版本的各種錯(cuò)誤修復(fù)和增強(qiáng):

  • https://github.com/ncave/dotnet-js

  • https://github.com/memsom/dna

源代碼概覽

我覺得 DotNetAnywhere 運(yùn)行時(shí)最令人印象深刻的一點(diǎn)是?只由一個(gè)人開發(fā),并且?只用了 40,000 行代碼!反觀,完整的 .NET 框架僅是垃圾收集器就有將近37000 行代碼?( 更多信息請(qǐng)我之前發(fā)布的CoreCLR 源代碼漫游指南?)。

機(jī)器碼 - 共 17,710 行

LOCFile
3,164JIT_Execute.c
1,778JIT.c
1,109PInvoke_CaseCode.h
630Heap.c
618MetaData.c
563MetaDataTables.h
517Type.c
491MetaData_Fill.c
467MetaData_Search.c
452JIT_OpCodes.h

托管代碼 - 共 28,783 行

LOCFile
2393corlib/System.Globalization/CalendricalCalculations.cs
2314corlib/System/NumberFormatter.cs
1582System.Drawing/System.Drawing/Pens.cs
1443System.Drawing/System.Drawing/Brushes.cs
1405System.Core/System.Linq/Enumerable.cs
745corlib/System/DateTime.cs
693corlib/System.IO/Path.cs
632corlib/System.Collections.Generic/Dictionary.cs
598corlib/System/String.cs
467corlib/System.Text/StringBuilder.cs

關(guān)鍵組件

接下來,讓我們看一下 DotNetAnywhere 中的關(guān)鍵組件,正是我們了解怎么兼容 .NET 運(yùn)行時(shí)的好辦法。同樣我們也能看到它與微軟 .NET Framework 的差異。

加載 .NET dll

DotNetAnywhere 所要做的第一件事就是加載、解析包含在 .dll 或者.exe 中的?元數(shù)據(jù)和代碼。這一切都存放在MetaData.c中,主要是在LoadSingleTable(..)?函數(shù)中。通過添加一些調(diào)試代碼,我能夠從一般的 .NET dll 中獲取所有類型的?元數(shù)據(jù)?摘要,這是一個(gè)非常有趣的列表:

MetaData contains ? ? 1 Assemblies (MD_TABLE_ASSEMBLY)MetaData contains ? ? 1 Assembly References (MD_TABLE_ASSEMBLYREF)MetaData contains ? ? 0 Module References (MD_TABLE_MODULEREF)MetaData contains ? ?40 Type References (MD_TABLE_TYPEREF)MetaData contains ? ?13 Type Definitions (MD_TABLE_TYPEDEF)MetaData contains ? ?14 Type Specifications (MD_TABLE_TYPESPEC)MetaData contains ? ? 5 Nested Classes (MD_TABLE_NESTEDCLASS)MetaData contains ? ?11 Field Definitions (MD_TABLE_FIELDDEF)MetaData contains ? ? 0 Field RVA's (MD_TABLE_FIELDRVA)MetaData contains ? ? 2 Propeties (MD_TABLE_PROPERTY)MetaData contains ? ?59 Member References (MD_TABLE_MEMBERREF)MetaData contains ? ? 2 Constants (MD_TABLE_CONSTANT)MetaData contains ? ?35 Method Definitions (MD_TABLE_METHODDEF)MetaData contains ? ? 5 Method Specifications (MD_TABLE_METHODSPEC)MetaData contains ? ? 4 Method Semantics (MD_TABLE_PROPERTY)MetaData contains ? ? 0 Method Implementations (MD_TABLE_METHODIMPL)MetaData contains ? ?22 Parameters (MD_TABLE_PARAM)MetaData contains ? ? 2 Interface Implementations (MD_TABLE_INTERFACEIMPL)MetaData contains ? ? 0 Implementation Maps? (MD_TABLE_IMPLMAP)MetaData contains ? ? 2 Generic Parameters (MD_TABLE_GENERICPARAM)MetaData contains ? ? 1 Generic Parameter Constraints (MD_TABLE_GENERICPARAMCONSTRAINT)MetaData contains ? ?22 Custom Attributes (MD_TABLE_CUSTOMATTRIBUTE)MetaData contains ? ? 0 Security Info Items? (MD_TABLE_DECLSECURITY)

更多關(guān)于?元數(shù)據(jù)?的資料請(qǐng)參閱?介紹 CLR 元數(shù)據(jù),解析.NET 程序集—–關(guān)于 PE 頭文件?和?ECMA 標(biāo)準(zhǔn)?等文章。


執(zhí)行 .NET IL

DotNetAnywhere 的另一大功能是 "即時(shí)編譯器" (JIT),即執(zhí)行 IL 的代碼,從?JIT_Execute.c和JIT.c?中開始執(zhí)行。在?JITit(..) 函數(shù)?的主入口中 "執(zhí)行循環(huán)",其中最令人印象深刻的是在一個(gè) 1,374 行代碼的?switch?中就有 200 多個(gè)?case?!!

從更高的層面看,它所經(jīng)歷的整個(gè)過程如下所示:


與定義在?CIL_OpCodes.h?(CIL_XXX) .NET IL 操作碼 ( Op-Codes) ?不同,DotNetAnywhere JIT 操作碼 (Op-Codes) 是定義在?JIT_OpCodes.h?(JIT_XXX)中。

有趣的是這部分 JIT 代碼是 DotNetAnywhere 中唯一一處使用匯編編寫?,并且只是?win32?。 它允許使用?jump?或者?goto?在 C 源碼中跳轉(zhuǎn)標(biāo)簽,所以當(dāng) IL 指令被執(zhí)行時(shí),實(shí)際上并不會(huì)離開?JITit(..)?函數(shù),控制(流程)只是從一處移動(dòng)到別處,不必進(jìn)行完整的方法調(diào)用。

#ifdef __GNUC__#define GET_LABEL(var, label)
var = &&label
#define GO_NEXT() goto **(void**)(pCurOp++)#else#ifdef WIN32#define GET_LABEL(var, label) \{ __asm mov edi, label \__asm mov var, edi }#define GO_NEXT() \{ __asm mov edi, pCurOp \__asm add edi, 4 \__asm mov pCurOp, edi \__asm jmp DWORD PTR [edi - 4] }#endif

IL 差異

在完整的 .NET framework 中,所有的 IL 代碼在被 CPU 執(zhí)行之前都是由 ?Just-in-Time Compiler (JIT)?轉(zhuǎn)換為機(jī)器碼。

如你所見, DotNetAnywhere "解釋" (interprets) IL時(shí)是逐條執(zhí)行指令,甚至?xí){(diào)用?JIT.c?文件來完成。?沒有機(jī)器碼?被反射發(fā)出 (emitted) ,所以這個(gè)命名還是有點(diǎn)奇怪!?

或許這只是一個(gè)差異,但實(shí)在是無法讓我搞清楚它是如何進(jìn)行 "解釋" (interpreting) 代碼和 "即時(shí)編譯" (JITting),即使我再閱讀完下面的文章還是不得其解!! (有人能指教一下嗎?)

  • 即時(shí)編譯器和解釋器有什么區(qū)別?

  • 了解傳統(tǒng)的解釋器、JIT 編譯器、JIT 解釋器 和 AOT 編譯器 的不同之處

  • JIT vs Interpreters

  • 為什么我們將 Java 字節(jié)碼轉(zhuǎn)換為機(jī)器碼的東西稱為 “JIT編譯器” 而不是 “JIT解釋器” ?

  • 了解 JIT 編譯和優(yōu)化


垃圾回收

所有關(guān)于 DotNetAnywhere 的垃圾回收(GC) 代碼都在?Heap.c?中,而且還是 600 行易于閱讀的代碼。給你一個(gè)概覽吧,下面是它暴露的函數(shù)列表:

void Heap_Init();
void Heap_SetRoots(tHeapRoots *pHeapRoots, void *pRoots, U32 sizeInBytes);void Heap_UnmarkFinalizer(HEAP_PTR heapPtr);
void Heap_GarbageCollect();U32 Heap_NumCollections();U32 Heap_GetTotalMemory();HEAP_PTR Heap_Alloc(tMD_TypeDef *pTypeDef, U32 size);HEAP_PTR Heap_AllocType(tMD_TypeDef *pTypeDef);
void Heap_MakeUndeletable(HEAP_PTR heapEntry);
void Heap_MakeDeletable(HEAP_PTR heapEntry);
tMD_TypeDef* Heap_GetType(HEAP_PTR heapEntry);
HEAP_PTR Heap_Box(tMD_TypeDef *pType, PTR pMem);
HEAP_PTR Heap_Clone(HEAP_PTR obj);
U32 Heap_SyncTryEnter(HEAP_PTR obj);
U32 Heap_SyncExit(HEAP_PTR obj);
HEAP_PTR Heap_SetWeakRefTarget(HEAP_PTR target, HEAP_PTR weakRef);HEAP_PTR* Heap_GetWeakRefAddress(HEAP_PTR target);
void Heap_RemovedWeakRefTarget(HEAP_PTR target);

GC 差異

就像我們對(duì)比 JIT/Interpreter 一樣, 在 GC 上的差異同樣可見。

Conservative GC

首先,DotNetAnywhere 的 GC 是?Conservative GC。簡(jiǎn)單地說,這意味著它不知道 (或者說肯定) 內(nèi)存的哪些區(qū)域是對(duì)象的引用/指針,還是一個(gè)隨機(jī)數(shù) (看起來像內(nèi)存地址)。而在.NET Framework 中 JIT 收集這些信息并存在GCInfo structure中,所以它的 GC 可以有效利用,而 DotNetAnywhere 是做不到。

相反, 在?標(biāo)記(Mark)?的階段,GC?獲取所有可用的 " 根 (roots) ", 將一個(gè)對(duì)象中的所有內(nèi)存地址視為 "潛在的" 引用(因此說它是 "conservative")。然后它必須查找每個(gè)可能的引用,看看它是否真的指向 "對(duì)象的引用"。通過跟蹤?平衡二叉搜索樹?(按內(nèi)存地址排序) 來執(zhí)行操作, 流程如下所示:

但是,這意味著所有的對(duì)象引用在分配時(shí)都必須存儲(chǔ)在二叉樹中,這會(huì)增加分配的開銷。另外還需要額外的內(nèi)存,每個(gè)堆多占用 20 個(gè)字節(jié)。我們看看?tHeapEntry?的數(shù)據(jù)結(jié)構(gòu)?(所有的指針占用 4 字節(jié),?U8?等于 1 字節(jié),而?padding?可忽略不計(jì)),?tHeapEntry *pLink[2]?是啟用二叉樹查找所需的額外數(shù)據(jù)。

struct tHeapEntry_ { ? ?// Left/right links in the heap binary treetHeapEntry *pLink[2]; ? ?// The 'level' of this node. Leaf nodes have lowest levelU8 level; ? ?// Used to mark that this node is still in use. ? ?
? ?// If this is set to 0xff, then this heap entry is undeletable.U8 marked; ? ?// Set to 1 if the Finalizer needs to be run. ?
? ?// Set to 2 if this has been added to the Finalizer queue ? ?
? ?// Set to 0 when the Finalizer has been run (or there is no Finalizer in the first place) ?
? ?// Only set on types that have a FinalizerU8 needToFinalize; ? ?// unusedU8 padding; ?
? ?// The type in this heap entrytMD_TypeDef *pTypeDef; ? ?
? ?// Used for locking sync, and tracking WeakReference that point to this objecttSync *pSync; ? ?// The user memoryU8 memory[0]; };

為什么 DotNetAnywhere 這樣做呢??? DotNetAnywhere的作者Chris Bacon?是這樣?解釋:

告訴你吧,整個(gè)堆代碼確實(shí)需要重寫,減少每個(gè)對(duì)象的內(nèi)存開銷,并且不需要分配二叉樹。一開始設(shè)計(jì) GC 時(shí)沒有考慮那么多,(現(xiàn)在做的話)會(huì)增加很多代碼。這是我一直想做的事情,但從來沒有動(dòng)手。為了盡快使用 GC 而只好如此。?在最初的設(shè)計(jì)中完全沒有 GC。它的速度非???#xff0c;以至于內(nèi)存也會(huì)很快用完。

更多 "Conservative" 機(jī)制和 "Precise" GC機(jī)制的細(xì)節(jié)請(qǐng)看:

  • Precise 對(duì)比 conservative 以及內(nèi)部指針

  • .NET CLR 如何區(qū)分托管指針和非托管指針?

GC 只做了 "標(biāo)記-掃描", 不會(huì)做壓縮

在 GC 方面另一個(gè)不同的行為是它不會(huì)在回收后做任何內(nèi)存?壓縮?,正如 Steve Sanderson 在?working on Blazor?中所說:

在服務(wù)器端執(zhí)行期間,我們實(shí)際上并不需要任何內(nèi)存固定 (pin),在客戶端執(zhí)行過程中并沒有任何互操作,所有的東西(實(shí)際上)都是固定的。因?yàn)?DotNetAnywhere 的 GC只做標(biāo)記掃描,沒有任何壓縮階段。

此外,當(dāng)一個(gè)對(duì)象被分配給 DotNetAnywhere 時(shí),只是調(diào)用了?malloc(), 它的代碼細(xì)節(jié)在?Heap_Alloc(..) 函數(shù)?中。所以它也沒有"Generations" 或者 "Segments"?的概念,你在 .NET Framework GC 中見到的如 "Gen 0"、"Gen 1" 或者 "大對(duì)象堆" 等都不會(huì)出現(xiàn)。


線程模型

最后,我們來看看線程模型,它與 .NET Framework 中的線程模型截然不同。

線程差異

DotNetAnywhere (表面上)樂于為你創(chuàng)建線程并執(zhí)行代碼, 然而這只是一種幻覺. 事實(shí)上它只會(huì)跑在?一個(gè)線程?中, 不同的線程之間?切換上下文:

你可以通過下面的代碼了解, ( 引用自?Thread_Execute() 函數(shù))將??numInst?設(shè)置為?100?并傳入?JIT_Execute(..)?中:

for (;;) {U32 minSleepTime = 0xffffffff;I32 threadExitValue;status = JIT_Execute(pThread, 100);switch (status) {....} }

一個(gè)有趣的副作用是 DotNetAnywhere 中corlib?的實(shí)現(xiàn)代碼將變得非常簡(jiǎn)單。如Interlocked.CompareExchange()?函數(shù)的內(nèi)部實(shí)現(xiàn)?所示, 你所期待的同步就缺失了:

tAsyncCall* System_Threading_Interlocked_CompareExchange_Int32(PTR pThis_, PTR pParams, PTR pReturnValue) {U32 *pLoc = INTERNALCALL_PARAM(0, U32*);U32 value = INTERNALCALL_PARAM(4, U32);U32 comparand = INTERNALCALL_PARAM(8, U32);*(U32*)pReturnValue = *pLoc; ? ?if (*pLoc == comparand) {*pLoc = value;} ? ?return NULL; }

基準(zhǔn)對(duì)比

作為性能測(cè)試, 我將使用C# 最簡(jiǎn)版本?實(shí)現(xiàn)的?基于二叉樹的計(jì)算機(jī)語(yǔ)言基準(zhǔn)測(cè)試做對(duì)比。

注意:DotNetAnywhere 旨在運(yùn)行于低內(nèi)存設(shè)備,所以不意味著能與完整的 .NET Framework具有相同的性能。對(duì)比結(jié)果時(shí)切記!!

.NET Framework, 4.6.1 - 0.36 seconds

Invoked=TestApp.exe 15 stretch tree of depth 16 ? ? ? ? check: 13107132768 ? ?trees of depth 4 ? ? ? ?check: 10158088192 ? ? trees of depth 6 ? ? ? ?check: 10403842048 ? ? trees of depth 8 ? ? ? ?check: 1046528512 ? ? ?trees of depth 10 ? ? ? check: 1048064128 ? ? ?trees of depth 12 ? ? ? check: 104844832 ? ? ? trees of depth 14 ? ? ? check: 1048544long lived tree of depth 15 ? ? ?check: 65535Exit code ? ? ?: 0Elapsed time ? : 0.36Kernel time ? ?: 0.06 (17.2%)User time ? ? ?: 0.16 (43.1%) page fault # ? : 6604Working set ? ?: 25720 KB Paged pool ? ? : 187 KB Non-paged pool : 24 KB Page file size : 31160 KB

DotNetAnywhere - 54.39 seconds

Invoked=dna TestApp.exe 15 stretch tree of depth 16 ? ? ? ? check: 13107132768 ? ?trees of depth 4 ? ? ? ?check: 10158088192 ? ? trees of depth 6 ? ? ? ?check: 10403842048 ? ? trees of depth 8 ? ? ? ?check: 1046528512 ? ? ?trees of depth 10 ? ? ? check: 1048064128 ? ? ?trees of depth 12 ? ? ? check: 104844832 ? ? ? trees of depth 14 ? ? ? check: 1048544long lived tree of depth 15 ? ? ?check: 65535Total execution time = 54288.33 ms Total GC time = 36857.03 msExit code ? ? ?: 0Elapsed time ? : 54.39Kernel time ? ?: 0.02 (0.0%)User time ? ? ?: 54.15 (99.6%) page fault # ? : 5699Working set ? ?: 15548 KB Paged pool ? ? : 105 KB Non-paged pool : 8 KB Page file size : 13144 KB

顯然,DotNetAnywhere 在這個(gè)基準(zhǔn)測(cè)試中運(yùn)行速度并不快(0.36秒/ 54秒)。然而,如果我們對(duì)比另一個(gè)基準(zhǔn)測(cè)試,它的表現(xiàn)就好很多。DotNetAnywhere 在分配對(duì)象(類)時(shí)有很大的開銷,而在使用結(jié)構(gòu)時(shí)就不那么明顯了。

?Benchmark 1?(using?classes)Benchmark 2?(using?structs)
Elapsed Time (secs)3.12.0
GC Collections9667
Total GC time (msecs)983.59439.73

最后,我要感謝?Chris Bacon。DotNetAnywhere 真是一個(gè)偉大的代碼庫(kù),對(duì)于我們實(shí)現(xiàn) .NET 運(yùn)行時(shí)很有幫助。

原文地址:http://www.cnblogs.com/chenug/p/8436819.html


.NET社區(qū)新聞,深度好文,歡迎訪問公眾號(hào)文章匯總 http://www.csharpkit.com

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的DotNetAnywhere:可供选择的 .NET 运行时的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。