泛型真的會(huì)降低性能嗎?
2009-05-29 16:41 Jeffrey Zhao 閱讀(30646) 評(píng)論(95) 收藏 舉報(bào)在《.NET,你忘記了么?(八)—— 從dynamic到特性誤用》一文中,飛林沙同學(xué)提到,使用泛型會(huì)略微降低程序性能,因此在程序中使用List<Object>是不合理的行為,應(yīng)該使用ArrayList。這一點(diǎn)和老趙平時(shí)的觀點(diǎn)相悖,老趙一直提倡,在.NET 2.0之后,要盡可能使用List<T>,情愿是List<Object>也不要使用ArrayList。不過(guò)個(gè)中原因與性能無(wú)關(guān),我們稍候再敘述。飛同學(xué)的文章讓我有了將泛型與非泛型進(jìn)行性能比較的想法。這個(gè)比較非常容易,不過(guò)也得出了一些非常有意思的結(jié)論。
泛型容器與非泛型容器的性能比較
首先,我們來(lái)比較一種最“純粹”的泛型容器,它的目的是避免程序的其他方面對(duì)性能的影響。因此,這里我們構(gòu)造兩個(gè)最簡(jiǎn)單的容器,就是簡(jiǎn)單的模仿ArrayList和List<T>:
public class MyArrayList { public MyArrayList(int length) { this.m_items = new object[length]; } public object[] m_items; public object this[int index] { get { return this.m_items[index]; } set { this.m_items[index] = value; } } public IEnumerable InnerContainer { get { return this.m_items; } } } public class MyList<T> { public MyList(int length) { this.m_items = new T[length]; } public T[] m_items; public T this[int index] { get { return this.m_items[index]; } set { this.m_items[index] = value; } } public IEnumerable<T> InnerContainer { get { return this.m_items; } } }
MyArrayList為直接使用Object類型的容器,而MyList<T>則是泛型容器。老趙為他們實(shí)現(xiàn)了兩種操作,一是下標(biāo)訪問(wèn),二是直接把內(nèi)部的數(shù)組容器作為可遍歷的對(duì)象釋放出來(lái)。這兩種都是平時(shí)編程中最經(jīng)常使用的操作。
于是我們就可以編寫測(cè)試代碼了。首先,我們初始化兩種容器,并進(jìn)行“預(yù)熱”:
int length = 1000; object value = new object(); MyArrayList myArrayList = new MyArrayList(length); for (int i = 0; i < length; i++) { myArrayList[i] = myArrayList[i] ?? value; } MyList<object> myList = new MyList<object>(length); for (int i = 0; i < length; i++) { myList[i] = myList[i] ?? value; }
然后我們使用CodeTimer來(lái)進(jìn)行統(tǒng)計(jì):
CodeTimer.Initialize(); int iteration = 300 * 1000; CodeTimer.Time("MyArrayList下標(biāo)訪問(wèn)", iteration, () => { for (int i = 0; i < length; i++) { var o = myArrayList[i]; } }); CodeTimer.Time("MyList下標(biāo)訪問(wèn)", iteration, () => { for (int i = 0; i < length; i++) { var o = myList[i]; } });
Release Build并運(yùn)行。猜猜看,結(jié)果是什么?
MyArrayList下標(biāo)訪問(wèn)
Time Elapsed: 2,398ms
CPU Cycles: 5,042,561,997
Gen 0: 0
Gen 1: 0
Gen 2: 0
MyList下標(biāo)訪問(wèn)
Time Elapsed: 2,285ms
CPU Cycles: 4,935,066,741
Gen 0: 0
Gen 1: 0
Gen 2: 0
以上是在老趙的機(jī)器上得到的結(jié)果,從結(jié)果上看,泛型的MyList性能甚至略比MyArrayList有所提高。當(dāng)然測(cè)試的結(jié)果其實(shí)是互有勝負(fù),但是事實(shí)上,MyList的獲勝的次數(shù)甚至還略有領(lǐng)先。
那么我們?cè)賮?lái)看看“遍歷”的性能如何:
CodeTimer.Time("MyArrayList遍歷", iteration, () => { foreach (object o in myArrayList.InnerContainer) { var o1 = o; } }); CodeTimer.Time("MyList遍歷", iteration, () => { foreach (object o in myList.InnerContainer) { var o1 = o; } });
運(yùn)行的結(jié)果頗有意思:
MyArrayList遍歷
Time Elapsed: 21,367ms
CPU Cycles: 46,023,627,496
Gen 0: 2
Gen 1: 1
Gen 2: 0
MyList遍歷
Time Elapsed: 3,463ms
CPU Cycles: 7,448,928,223
Gen 0: 2
Gen 1: 0
Gen 2: 0
直接使用Object的MyArrayList性能居然差了這么多!個(gè)中原因老趙有所猜測(cè),在得到明確答案之后會(huì)接著與大家分享。
ArrayList和List<T>的性能
剛才比較了最“純粹”的性能,那么我們?cè)賮?lái)比較ArrayList和List<T>。因?yàn)槲覀兤鋵?shí)不知道它倆具體在實(shí)現(xiàn)上的細(xì)節(jié)是如何的,還是比較一下這兩個(gè)容器具體的性能比較好。比較的內(nèi)容還是兩項(xiàng):下標(biāo)訪問(wèn)及遍歷。代碼如下:
int length = 1000; object value = new object(); ArrayList arrayList = new ArrayList(length); for (int i = 0; i < length; i++) { arrayList.Add(value); arrayList[i] = arrayList[i] ?? value; } List<object> list = new List<object>(length); for (int i = 0; i < length; i++) { list.Add(value); list[i] = list[i] ?? value; } CodeTimer.Initialize(); int iteration = 300 * 1000; CodeTimer.Time("ArrayList下標(biāo)訪問(wèn)", iteration, () => { for (int i = 0; i < length; i++) { var o = arrayList[i]; } }); CodeTimer.Time("List下標(biāo)訪問(wèn)", iteration, () => { for (int i = 0; i < length; i++) { var o = list[i]; } }); CodeTimer.Time("ArrayList遍歷", iteration, () => { foreach (object o in arrayList) { var o1 = o; } }); CodeTimer.Time("List遍歷", iteration, () => { foreach (object o in list) { var o1 = o; } });
結(jié)果如下:
ArrayList下標(biāo)訪問(wèn)
Time Elapsed: 2,282ms
CPU Cycles: 4,838,476,797
Gen 0: 0
Gen 1: 0
Gen 2: 0
List下標(biāo)訪問(wèn)
Time Elapsed: 2,302ms
CPU Cycles: 4,979,267,920
Gen 0: 0
Gen 1: 0
Gen 2: 0
ArrayList遍歷
Time Elapsed: 5,187ms
CPU Cycles: 11,145,830,014
Gen 0: 4
Gen 1: 0
Gen 2: 0
List遍歷
Time Elapsed: 2,989ms
CPU Cycles: 6,459,825,955
Gen 0: 0
Gen 1: 0
Gen 2: 0
現(xiàn)在您還覺得泛型會(huì)降低性能,List<Object>比ArrayList的性能差嗎?
總結(jié)
從結(jié)果上已經(jīng)可以看出,泛型并不會(huì)影響性能,而List<T>的性能也不比ArrayList要差。因此老趙繼續(xù)堅(jiān)持:在有泛型支持的情況下,盡量使用泛型容器。例如使用List<Object>而不是ArrayList。除了“性能”之外,老趙的還有其他一些理由。例如使用List<Object>的話就可以使用框架內(nèi)部所定義的各種有用的輔助方法(要知道在.NET框架中,現(xiàn)在幾乎都是在針對(duì)IEnumerable<T>進(jìn)行開發(fā));而我們平時(shí)寫程序時(shí),也可以統(tǒng)一的針對(duì)泛型編程,如IList<T>,IEnumerable<T>,不必考慮List或Enumerable等非泛型元素。
放心使用List<Object>吧。
浙公網(wǎng)安備 33010602011771號(hào)