2014-09-04 45 views
0

下面是一個簡單的問題。Reflection.Emit Performance

比方說,我們要展開一個循環的方法,如:

public int DoSum1(int n) 
{ 
    int result = 0; 
    for(int i = 1;i <= n; i++) 
    { 
     result += i; 
    } 
    return result; 
} 

到一個方法只能進行簡單的加法:

public int DoSum2() 
{ 
    return 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20; 
} 

[http://etutorials.org/Programming/Programming+C.Sharp/Part+III+The+CLR+and+the+.NET+Framework/Chapter+18.+Attributes+and+Reflection/18.3+Reflection+Emit/][1]

從邏輯上講,我們要在某些時候需要代碼在IL中創建DoSum2。 在這個IL代碼中,我們將使用與未優化方法相同的迭代計數來執行實際循環。

什麼,如果所需的代碼生成將使用類似的時間來執行量創造一個超快速的動態方法的點???

或許你可以舉個例子,當它在一點,實在值得類似情況下使用的Emit?

回答

1

什麼,如果所需的代碼生成將使用的時間類似的量來執行

這是不是真的具體到Reflection.Emit創建一個超快速的動態方法的地步,但運行時一般的代碼生成,所以我會相應地回答。

首先,我不建議使用的代碼生成簡單地進行微優化,編譯器通常執行像展開循環。讓JIT編譯器做它的工作。

其次,你是對的,因爲通常只會生成只會執行一次的代碼。發射和JIT編譯IL所需的時間並不是非實質性的。如果它將被執行很多次,你只應該煩惱生成代碼。

現在,肯定案件下,運行時代碼生成可以證明是有益的。事實上,這是一項我非常重視的技術。我在需要處理大量動態數據的電子交易環境中工作。這引起了一些問題,最重要的是內存使用和吞吐量。

我們的交易應用程序需要保持大量的內存中的數據,使每個記錄的足跡是至關重要的。諸如地圖/字典之類的動態數據結構比具有優化的字段佈局的「POCO」類效率低,並且根據設計,可能需要裝箱一些值。一旦數據的形狀已知,我就通過生成客戶端存儲類來避免這種開銷。實際上,內存佈局就像我在編譯時知道數據的形狀一樣。

吞吐量也是一個主要問題; (de)序列化動態數據通常會涉及一些額外的內省和額外的間接層。需要序列化記錄?好的,首先你需要查詢字段是什麼。然後,對於每個字段,您需要確定其類型,然後選擇該類型的序列化程序,然後調用序列化程序。如果您的數據結構具有可選字段,則可能需要執行一些額外的預處理,例如計算出席地圖的大小以及出席地圖中的哪些位對應於哪些字段。如果您需要處理數據,則所有這些開銷都會成爲一個真正的問題。我通過在服務器端和客戶端上生成專門的(de)序列化器來避免這種開銷。由於串行器是按需生成的,因此它們可以知道數據的確切形狀,並且可以像手動優化的串行器那樣高效地讀取/寫入數據。當你在很高的頻率下進行大量數據更新時,這可能會產生巨大的差異。

現在,請記住,我們是一個邊緣案例。大多數應用程序沒有我們所具有的積極的內存和吞吐量要求,因此運行時代碼生成不是必需的。如果您真的需要,並且您已經耗盡所有其他可能性,則應該只使用該路線。雖然它可以幫助提高性能,但生成的代碼可能很難調試和維護。

+0

「這不是真的專用於Reflection.Emit」: 正確的,我可以使用CodeDom,而不是慢。 我想整個想法是,這樣的性能優化是爲了再次使用相同的生成裝配。 ...在我的例子中,該方法不可重複使用。 「 」「POCO」類與優化的字段佈局「: 我想知道如果你正在談論在MSIL中使用較短的地址?否則請解釋。 您提到的關於序列化的例子非常有趣。事實上,你將不得不運行大量的條件反射代碼。 – Olograph 2014-09-04 21:23:02

+0

關於我的POCO評論,我的意思是,如果底層數據存儲在適當類型的字段中,而不是稀疏映射/字典,則封裝更緊湊。運行時可以安排這些字段以消除不必要的填充,並且最終不會像使用散列表中的未使用的插槽(更不用說不必要的填充值)。 – 2014-09-05 13:33:55

+0

好點,謝謝! – Olograph 2014-09-06 21:31:59