Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

CreateInstance vs CreateInstance<T>

W swoich potyczkach dosyć często mam do czynienia z COM-ami, przy tworzeniu instancji których równie często sięgam po Activator.CreateInstance. I pewnie nie byłoby w tym nic odkrywczego ani niezwykłego, gdyby nie pewna obserwacja.

Weźmy dla przykładu kawałek kodu, w którym tworzymy instancję klasy TestClass korzystając z wersji genericsowej i zwykłej:

class TestClass
{
 public TestClass()
 {
 }
}

class Program
{
 const Int32 CYCLES = 100000;
 static void Main(string[] args)
 {
  CreateInstanceGenericTest();
  CreateInstanceNonGenericTest();
 }

 private static void CreateInstanceNonGenericTest()
 {
  using (OperationTimer ot = new OperationTimer("CreateInstance NON Generic"))
  {
   TestClass t = null;
   for (int i = 0; i < CYCLES; i++)
   {
    t = (TestClass)Activator.CreateInstance(typeof(TestClass));
   }
  }
 }

 private static void CreateInstanceGenericTest()
 {
  using (OperationTimer ot = new OperationTimer("CreateInstance Generic"))
  {
   TestClass t = null;
   for (int i = 0; i < CYCLES; i++)
   {
    t = (TestClass)Activator.CreateInstance<TestClass>(); //jedyna różnica
   }
  }
 }
}

po wykonaniu którego dostajemy czasy:

  ,375 seconds CreateInstance Generic
  ,574 seconds CreateInstance NON Generic

Czyli wszystko mniej-więcej zgodnie z oczekiwaniami. Wersja genericsowa nieznacznie sprawniejsza (powiedzmy dwukrotnie).

Dokonajmy jednak prostej modyfikacji. Zmieńmy mianowicie modyfikator dostępu dla klasy TestClass z internal na public, pozostawiając resztę bez zmian:

public class TestClass
{
 public TestClass()
 {
 }
}

a otrzymamy następujące czasy:

  ,372 seconds CreateInstance Generic
  ,035 seconds CreateInstance NON Generic

Jak widać powyższa zmiana zupełnie nie wpłynęła na wersję genericsową, natomiast spowodowała prawie 20x przyspieszenie wersji niegenericsowej, wobec czego wersja nie używająca generics jest teraz o rząd wielkości szybsza od odpowiednika genericsowego. Wow!

Ale o so chodzi?! :) Mam nadzieję, że ktoś wie jaka idea przyświecała takiemu rozróżnieniu i wytłumaczy mi to łopatologicznie :)

PS. Pomocnicza klasa OperationTimer mierząca czas wykonania kodu wewnątrz bloku using jest żywcem ściągnięta z książki 'CLR via C#' Jeffreya Richtera i opiera się na klasie Stopwatch z System.Diagnostics.

PS2. W przypadku zagnieżdżenia klasy TestClass niezależnie od wybranego modyfikatora dostępu wyniki są takie jak w przypadku pierwszym, czyli wersja genericsowa szybsza.

Opublikowane 12 lutego 2009 01:57 przez mgrzeg
Filed under: ,

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

 

Wojciech Gebczyk said:

Michal,

To wlasnie masz potwierdzenie roznicy predkosci w Reflection po publicznych a po prywatnych o ktorej wciaz mowie glosno gdzie sie da a ostatnio na grupie jakis czas temu:-)))

Ale czemu takie sa wyniki POROWNUJAC do generickow, nie wime :-)

lutego 12, 2009 12:28
 

bszafko said:

W prism(compositewpf) w eventAggregation jest używana właśnie dokładnie taka wersja z generics jak w Twoim przykładzie. Ciekaw jestem o ile by przyśpieszył mechanizm eventAggregation gdyby zastosować  wersję bez generics.

lutego 13, 2009 15:10
 

mgrzeg said:

Bartek, smiem twierdzic, ze w ogole, lub niezauwazalnie. Tworzenie obiektow to czesto narzut na IO, alokacje pamieci i wiele innych rzeczy, ktore decyduja o czasie tworzenia obiektow. Stawiam gofra, ze z podanych przykladow przyczyna decydujaca o krotszym czasie tworzenia obiektu w wersji niegenericsowej jest addytywna, a nie multiplikatywna wzgledem czasu kreacji i w decydujacym momencie jest pomijalna.

lutego 13, 2009 16:12
 

simon said:

Hej:) W załączonym przez Ciebie przykładzie problem z generykami bierze się stad, iż generyczna metoda CreateInstance odwołuje się _zawsze_ do metody tworzącej RuntimeTimeHandle tworzącej nowy obiekt. Metoda niegeneryczna zaś korzysta sobie z cache delegatów konstruujących obiekty - po pierwszym wywołaniu każde następne działa już prawie jak normalny kod kompilowany.

Coś takiego właściwie można sobie napisać samemu - to tylko kilka linijek w IL-u. Polecam zabawy z klasę DynamicMethod. Dzięki metodom wygenerowanym za pomocą tej klasy można _całkowicie_ ominąć sprawdzanie widoczności memberów i wygenerować sobie śliczne i szybciutkie delegaty konstruujące obiekty.

:)

lutego 15, 2009 14:29

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij
W oparciu o Community Server (Personal Edition), Telligent Systems