.NET Core中的去虛

責(zé)任編輯:editor004

作者:Jonathan Allen

2017-12-25 11:18:05

摘自:INFOQ

在 NET最初被設(shè)計出來時,方法在默認(rèn)情況下必須是非虛方法。例如:  如果方法是final的,而類不明確或者不是final的,則不允許接口去虛,因為派生類在實現(xiàn)接口時還可以重寫final方法。

在.NET最初被設(shè)計出來時,方法在默認(rèn)情況下必須是非虛方法。這有幾個原因,其中一個是,非虛方法通常比虛方法快很多。除了虛函數(shù)表查詢本身的成本之外,虛函數(shù)通常還無法內(nèi)聯(lián)。由于.NET的發(fā)展趨勢是傾向于使用大量的小方法,所以非內(nèi)聯(lián)方法的函數(shù)調(diào)用開銷最終會超過方法本身的開銷。我們在文章“關(guān)于C#的抽象與For-Each性能”中介紹了這種內(nèi)聯(lián)的部分效果。

在過去的幾年中,我們習(xí)慣的C#一直在變化。以前,大接口并不常見,但現(xiàn)在,可以完全匹配所有類的“影子接口”都非常常見了。這始于WCF,它鼓勵這種做法,雖然不是必須的。隨著DI框架性能的提升,在項目中見到面向所有非DTO類的影子接口已經(jīng)很平常了。

方法去虛有多種方式,本質(zhì)上講,就是在特定的情況下把它們視為非虛方法。Java HotSpot就以具備這項特性而聞名。在Java中,所有方法在默認(rèn)情況下都是虛方法,因此,在Java的歷史中,解決這種性能問題的需求出現(xiàn)得早很多。

在今年三月份,.NET Core悄悄地對“去虛(Devirtualization)”發(fā)起了挑戰(zhàn)。簡單去虛特性處理了三種基本的場景:

在sealed類上調(diào)用虛方法; 在sealed方法上調(diào)用虛方法; 在明確知道類型的情況下調(diào)用虛方法(例如,緊挨著構(gòu)造函數(shù))。

接口去虛也有一些基礎(chǔ)的支持,但有限制。例如:

如果方法是final的,而類不明確或者不是final的,則不允許接口去虛,因為派生類在實現(xiàn)接口時還可以重寫final方法。

需要注意的是,僅僅將類標(biāo)記為“sealed”是不足以從去虛接口受益的。如果你正在使用DI框架隱藏運行時使用了哪個具體類,那么JIT編譯器可能無法確定使用了什么類型。

這在Java中之所以不是問題,是因為Java去虛技術(shù)的工作原理完全不同。它是根據(jù)運行時指標(biāo)試探性地去虛接口調(diào)用,對最常調(diào)用的方法重新即時編譯。其中還包含了專門的防衛(wèi)語句,以防具體的類型修改和去虛需要解除。

展望

.NET Core 2.0提供了上述特性,但還有許多工作要做。下面是去虛路線圖上的部分重點工作。

眾所周知,在涉及接口調(diào)用時,結(jié)構(gòu)很糟糕,因為它們不僅是虛的,而且還需要對值進(jìn)行裝箱。因此,有幾項工作是為了盡可能地減少虛調(diào)用和裝箱。其中一個重要的部分是一類結(jié)構(gòu),這是一個高級JIT概念,超出了本報道的范圍。

JIT本身的類型跟蹤改進(jìn)。顯然,在許多情況下,JIT在一個地方知道具體的類型,但無法將信息傳遞下去,因此,JIT不得不采用更通用的機(jī)器代碼。

試探性去虛也在考慮范圍。根據(jù)概述,該特性不會和Java里的一樣。更準(zhǔn)確地說,它會根據(jù)JIT過程中已知的覆寫清單做決定。(據(jù)推測,這會用在接口只有單個類實現(xiàn)的情況下,這在上文提到的DI場景下經(jīng)常發(fā)生。)

其中有一種特殊情況,就是EqualityComparer.Default防衛(wèi)去虛。由于在絕大多數(shù)情況下,IEqualityComparer調(diào)用都會指向默認(rèn)實現(xiàn)(視情況不同,要么是IEquatable,要么是Object.Equals),所以他們覺得,如果不減慢使用非默認(rèn)IEqualityComparer的情況,那么提升使用默認(rèn)實現(xiàn)場景的執(zhí)行速度是值得的。

查看英文原文:Devirtualization in .NET Core

鏈接已復(fù)制,快去分享吧

企業(yè)網(wǎng)版權(quán)所有?2010-2024 京ICP備09108050號-6京公網(wǎng)安備 11010502049343號