Reflection vs. Array
Podtitulek tohoto logu se zmiňuje o tom, že bych se zde rád věnoval střípkům z konferencí o .NETu. Dlouho jsem váhal, zda téma, které mě trklo, bude zajímavé. Ale poté, co jsem si vše vyzkoušel a napsal test, neváhal jsem.
Informace o tom, že použití reflection je pomalejší, je obecně známý fakt, se kterým jsem pracoval. Ovšem výsledky měření, které jsem provedl, mě celkem překvapily. Ale vrátím se na samý začátek, ať nevytvářím závěry dřív, než napíšu podstatu.Jeden z dotazů do konference zněl, zda se dá ze zadaného jména proměnné, k této přistoupit a změnit její hodnotu.
On ten dotaz zněl ještě přesněji. Což mě vedlo k odpovědi, proč raději nepoužít pole. Tomas Rampas však neváhal a odpověděl, přesně podle přání dotazujícího. V ten okamžik mě napadlo, že ve svých programech používám reflection, a uživatelsky definované atributy. Zatím jsem však neprováděl měření, kterým bych zjistil k jak velkému zpomalení, díky tomuto přístupu, vše vede. Takže jsem neváhal a zkusil napsat pár tříd, na kterých jsem si chtěl ověřit onen obecně známý fakt.
Definice testování
Ač jsem absolvoval nějaké pokusy (jistě je znáte i vy ze školních škamen), jedna z mála věcí, která mi utkvěla v paměti je, že se má testovat na stejném vzorku dat. Takže jsem definoval množinu, vlastně třídu, která bude mít možnost uchovat sto hodnot typu int. K těmto hodnotám se půjde dostat pomocí metod int GetValue(int index) a void SetValue(int index, int value). Pro lepší porovnání jsem pak přistoupil ještě k možnosti, že třídy, které používají reflection, si mohou informativní údaje uschovat do HashTable. Co mě napadlo až při pohledu na kód zaslaný do příspěvku, že vytvořím ještě třídu, která bude obsahovat property, místo fieldů.
Jak jsem měřil
Mám připravené čtyři třídy, které jsem pojmenoval TestArray, TestField, TestProperty a TestPropertyHash. Každá obsahovala zmíněné dvě metody. Přistup k hodnotám pole byl realizován s otestováním na přetečení a přistupu nad/pod mez pole. Ostatní tři třídy přistupovaly ke svým property/fields pomocí reflection. Veřejné proměnné byly pojmenovány Cislo0 .. Cislo99. Z předaného čísla indexu byl sestaven název proměnné a pomocí reflection získáno PropertyInfo/FieldInfo. Rozdíl byl u testování TestPropertyHash, kdy se nejdříve zjišťovala přítomnost informací v tabulce a až poté jsem případně zjišťoval potřebné informace.Po několika měřeních jsem zjistil, že simulovat aspoň trochu reálné prostředí zřejmě nepůjde, neboť naměřené hodnoty jsou příliš malé pro posouzení náročnosti. Takže jsem nastavil počet průchodů přes jednotlivé operace na poněkud vyšší hodnoty, než které se mohou (snad) reálně vyskytnout v aplikaci. I když ani takové chování se nemůže nikdy vyloučit. Takže nakonec jsem přistoupil k následujícím počtům.
- Start počítání času
- Vytvoření objektu celkem 20x
- Počet zapsání a opětovné vyzvednutí hodnoty každé proměnné v jednom vytvořeném objektu 50x
- Ukončení počítání času
Malé doplnění k měření
Jelikož jsem byl při znalosti naměřených hodnot zvědavý, co s metodami GetProperty a GetField udělají zapsané BindingFlags, pustil jsem měření ještě jednou s uvedením těchto Flagů BindingFlags.Public | BindingFlags.Instance. V tabulce jsou tato měření označena jako "O".
Výsledky měření
Jak už jsem se zmínil v úvodu, měřeními jsem byl celkem překvapen, i když musím říci, že se jedná o trošku přehnané hodnoty průchodů, a ať jsem vzpomínal jak chtěl, nenarazil jsem na případ, kdy bych něco takového ve svých aplikacích prováděl. Každopádně je to zajímavé a pokud někdo uvažuje o co nejrychlejším kódu, určitě bych doporučoval se reflection vyhnout. Měření ukázalo, že nejpomalejší přístup je pomocí reflection, která získává informace o veřejných Fieldech. Za ní se v těsném závěsu drží přístup k Property. O něco lepší je přístup, kdy se použije k uchování informací Hashtable, která pak udržovala celkem konstantní čas přístupu a je to asi nejrozumější volbou, pokud je potřeba použít reflection. Samozřejmě nejrychlejší cestou je pak přímý přístup k poli, který měl v prvních chvílích až nezanedbatelné měření. Ostatně kvůli tomu, jsem zvedal "laťku" výše, abych se ujistil, že takové měření nemá spíše statistickou chybu. Jak měření dopadla, se můžete přesvědčit v následující tabulce.
Ještě je potřeba zmínit, že pro měření jsem nakonec využil kódu, který uveřejnil Petr Lazecký.
Tabulka naměřených hodnot
Array O | Field O | Property O | PropertyHash O | Array | Field | Property | PropertyHash | |
---|---|---|---|---|---|---|---|---|
1. | 0,012859456 | 14,52278656 | 8,119284993 | 3,790104608 | 0,01253651 | 13,982752 | 8,30993995 | 3,31640418 |
2. | 0,011206173 | 14,44983762 | 8,554286648 | 3,439473173 | 0,011182706 | 14,10884847 | 8,185773611 | 3,274529863 |
3. | 0,010985754 | 13,98355406 | 8,173504454 | 3,165731119 | 0,011029614 | 14,14912007 | 8,3745666 | 3,262086382 |
4. | 0,011520179 | 13,81221636 | 8,388321141 | 3,229624156 | 0,010975697 | 13,93663608 | 8,400916318 | 3,455275461 |
5. | 0,010967595 | 13,6163504 | 7,940935789 | 3,147346939 | 0,010981005 | 13,9495827 | 8,087749979 | 3,135475598 |
6. | 0,011145551 | 13,83192249 | 7,90464654 | 3,178728861 | 0,010981843 | 14,60432401 | 8,161623894 | 3,274396606 |
7. | 0,011411506 | 13,58849072 | 7,942665059 | 3,167312047 | 0,011639468 | 14,41323045 | 7,990731224 | 4,447815854 |
8. | 0,011212878 | 14,40057773 | 7,937816398 | 3,512432725 | 0,0121711 | 14,40095934 | 8,592995478 | 3,496401638 |
9. | 0,011254503 | 13,61583302 | 7,928621375 | 3,168193164 | 0,011033525 | 14,45914774 | 9,120829044 | 3,327205832 |
10. | 0,010959493 | 13,48283371 | 7,859824363 | 3,134714608 | 0,012849398 | 13,82637821 | 8,273673889 | 3,491689586 |
11. | 0,011336357 | 13,82340297 | 7,903663454 | 3,147873543 | 0,010954744 | 15,38235139 | 8,636580348 | 4,733472271 |
12. | 0,011231874 | 13,56582778 | 7,937909986 | 3,149191587 | 0,011520179 | 13,96755789 | 8,301080724 | 3,216261005 |
13. | 0,011789767 | 13,5769588 | 7,930314328 | 3,148871435 | 0,010970109 | 13,90357601 | 8,08478843 | 3,143627752 |
14. | 0,011563481 | 13,60198321 | 7,920364739 | 3,156153086 | 0,011094427 | 13,54703936 | 8,445257149 | 4,925186022 |
15. | 0,011142757 | 13,57030321 | 7,905241867 | 3,168578409 | 0,012306033 | 13,66658137 | 8,025275279 | 3,137395675 |
Avg. | 0,011372488 | 13,82952524 | 8,023160076 | 3,246955297 | 0,011481757 | 14,15320567 | 8,332785461 | 3,575814915 |
Poohlédnutí se za výsledkem
Myslím, že k tomuto není třeba dodávat jakýchkoli slov. Čísla jsou jasná. Naměřenými hodnotami jsem byl překvapen víc, a očekával jsem o hodně menší rozdíly. Obzvláště mě pak překvapil rozdíl v přístupu k Property a Field. Můj osobní předpoklad byl spíše opačný na druhou stranu je to jen dobře, neustále zjišťuji, že se mám co učit a co zkoušet.
Budu rád, za jakékoli Vaše připomínky, které mohou pomoci nejen mě, ale i ostatním.