大家在平時開發中大多都會遵循介面編程,這樣就可以方便實現依賴註入也方便實現多態等各種小技巧,但這種是以犧牲性能為代價換取代碼的靈活性,萬物皆有陰陽,看你的應用場景進行取捨。 一:背景 1. 緣由 在項目的性能改造中,發現很多方法簽名的返回值都是採用IEnumerable介面,比如下麵這段代碼: 2. ...
大家在平時開發中大多都會遵循介面編程,這樣就可以方便實現依賴註入也方便實現多態等各種小技巧,但這種是以犧牲性能為代價換取代碼的靈活性,萬物皆有陰陽,看你的應用場景進行取捨。
一:背景
1. 緣由
在項目的性能改造中,發現很多方法簽名的返回值都是採用IEnumerable介面,比如下麵這段代碼:
public static void Main(string[] args)
{
var list = GetHasEmailCustomerIDList();
foreach (var item in list){}
Console.ReadLine();
}
public static IEnumerable<int> GetHasEmailCustomerIDList()
{
return Enumerable.Range(1, 5000000).ToArray();
}
2. 有什麼問題
這段代碼乍一看也沒啥什麼性能問題,foreach迭代天經地義,這個還能怎麼優化???
<1> 從MSIL中尋找問題
首先我們儘可能把原貌還原出來,簡化後的MSIL如下。
.method public hidebysig static
void Main (
string[] args
) cil managed
{
IL_0009: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
IL_000e: stloc.1
.try
{
IL_000f: br.s IL_001a
// loop start (head: IL_001a)
IL_0011: ldloc.1
IL_0012: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
IL_0017: stloc.2
IL_0018: nop
IL_0019: nop
IL_001a: ldloc.1
IL_001b: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0020: brtrue.s IL_0011
// end loop
IL_0022: leave.s IL_002f
} // end .try
finally
{
IL_0024: ldloc.1
IL_0025: brfalse.s IL_002e
IL_0027: ldloc.1
IL_0028: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002d: nop
IL_002e: endfinally
} // end handler
IL_002f: ret
} // end of method Program::Main
從IL中看到了標準的get_Current,MoveNext,Dispose
還有一個try,finally
,一下子多了這麼多方法和關鍵詞,不就是一個簡單的foreach迭代數組嘛? 至於搞的這麼複雜嘛?這樣在大數據下怎麼快的起來?
還有一個奇葩的事,如果你仔細觀察IL代碼,比如這句:[mscorlib]System.Collections.Generic.IEnumerable``1<int32>::GetEnumerator()
, 這個GetEnumerator前面是介面IEnumerable,正常情況下應該是具體迭代類吧,按理說應該會調用Array的GetEnumerator方法,如下所示。
[Serializable]
[ComVisible(true)]
[__DynamicallyInvokable]
public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable
{
[__DynamicallyInvokable]
public IEnumerator GetEnumerator()
{
int lowerBound = GetLowerBound(0);
if (Rank == 1 && lowerBound == 0)
{
return new SZArrayEnumerator(this);
}
return new ArrayEnumerator(this, lowerBound, Length);
}
}
<2> 從windbg中尋找問題
IL中發現的第二個問題我特別好奇,