2009-08-19 17 views
5

TłoCzy mogę porównać kod IL, aby określić, która technika jest szybsza lub lepsza?

This question dało mi do myślenia o czymś. Ostatnio, odkąd przyglądałem się funkcjonalności IL IL, porównywałem kod IL dwóch podejść do tego samego problemu, aby "określić", który jest najlepszy.

Stosując pytanie umieszczonego powyżej, o konwersji tablicy, to wygenerował kod IL dla obu odpowiedzi:

var arr = new string[] { "1", "2", "3", "4" }; 
var result = Array.ConvertAll(arr, s => Int32.Parse(s)); 

wymienić:

IL_0001: ldc.i4.4  
IL_0002: newarr  System.String 
IL_0007: stloc.2  
IL_0008: ldloc.2  
IL_0009: ldc.i4.0  
IL_000A: ldstr  "1" 
IL_000F: stelem.ref 
IL_0010: ldloc.2  
IL_0011: ldc.i4.1  
IL_0012: ldstr  "2" 
IL_0017: stelem.ref 
IL_0018: ldloc.2  
IL_0019: ldc.i4.2  
IL_001A: ldstr  "3" 
IL_001F: stelem.ref 
IL_0020: ldloc.2  
IL_0021: ldc.i4.3  
IL_0022: ldstr  "4" 
IL_0027: stelem.ref 
IL_0028: ldloc.2  
IL_0029: stloc.0  
IL_002A: ldloc.0  
IL_002B: ldsfld  UserQuery.CS$<>9__CachedAnonymousMethodDelegate1 
IL_0030: brtrue.s IL_0045 
IL_0032: ldnull  
IL_0033: ldftn  b__0 
IL_0039: newobj  System.Converter<System.String,System.Int32>..ctor 
IL_003E: stsfld  UserQuery.CS$<>9__CachedAnonymousMethodDelegate1 
IL_0043: br.s  IL_0045 
IL_0045: ldsfld  UserQuery.CS$<>9__CachedAnonymousMethodDelegate1 
IL_004A: call  System.Array.ConvertAll 
IL_004F: stloc.1  

b__0: 
IL_0000: ldarg.0  
IL_0001: call  System.Int32.Parse 
IL_0006: stloc.0  
IL_0007: br.s  IL_0009 
IL_0009: ldloc.0  
IL_000A: ret   

i drugą odpowiedź:

var arr = new string[] { "1", "2", "3", "4" }; 
var result = arr.Select(s => int.Parse(s)).ToArray(); 

wyprodukowano:

IL_0001: ldc.i4.4  
IL_0002: newarr  System.String 
IL_0007: stloc.2  
IL_0008: ldloc.2  
IL_0009: ldc.i4.0  
IL_000A: ldstr  "1" 
IL_000F: stelem.ref 
IL_0010: ldloc.2  
IL_0011: ldc.i4.1  
IL_0012: ldstr  "2" 
IL_0017: stelem.ref 
IL_0018: ldloc.2  
IL_0019: ldc.i4.2  
IL_001A: ldstr  "3" 
IL_001F: stelem.ref 
IL_0020: ldloc.2  
IL_0021: ldc.i4.3  
IL_0022: ldstr  "4" 
IL_0027: stelem.ref 
IL_0028: ldloc.2  
IL_0029: stloc.0  
IL_002A: ldloc.0  
IL_002B: ldsfld  UserQuery.CS$<>9__CachedAnonymousMethodDelegate1 
IL_0030: brtrue.s IL_0045 
IL_0032: ldnull  
IL_0033: ldftn  b__0 
IL_0039: newobj  System.Func<System.String,System.Int32>..ctor 
IL_003E: stsfld  UserQuery.CS$<>9__CachedAnonymousMethodDelegate1 
IL_0043: br.s  IL_0045 
IL_0045: ldsfld  UserQuery.CS$<>9__CachedAnonymousMethodDelegate1 
IL_004A: call  System.Linq.Enumerable.Select 
IL_004F: call  System.Linq.Enumerable.ToArray 
IL_0054: stloc.1  

b__0: 
IL_0000: ldarg.0  
IL_0001: call  System.Int32.Parse 
IL_0006: stloc.0  
IL_0007: br.s  IL_0009 
IL_0009: ldloc.0  
IL_000A: ret  

Patrząc na to wszystko, co mogę powiedzieć jest to, że ta ostatnia opcja

  • trwa 1 dodatkowy wiersz
  • używa LINQ gdy 1st odpowiedź nie
  • tworzy INT inaczej poprzez IL_0039.

Pytania

  • W tym konkretnym przykładzie, są moje założenia są prawidłowe?
  • Na ogół, w jaki sposób powinienem poradzić sobie z porównywaniem dwóch rozwiązań za pomocą kodu IL?
  • Ogólnie rzecz biorąc, czy rozwiązanie z mniejszą IL LOC oznacza, że ​​będzie ono szybsze lub zużywa mniej pamięci?
  • Jak mówi tytuł, czy mogę porównać kod IL, aby określić, która technika jest szybsza lub lepsza?

FWIW, nie robię tego często, tylko raz w rzadkim czasie, gdy pojawia się dyskusja między programistami w pracy. Ktoś powie "och, to jest bardziej wydajne", a my wyrzucimy go na linqpad, żeby sprawdzić kod IL. Także FWIW, prawie zawsze przestrzegam tego, jak działa, zanim uzyskałem efektywne/szybkie podejście. Wystarczy więc ludzie nie sądzę, jestem stale porównując kod IL co Zajmuję :)

+0

Cześć, Allen, dziękuję bardzo! – abatishchev

Odpowiedz

8
  • W tym konkretnym przykładzie, są moje założenia są prawidłowe?
  • Na ogół, w jaki sposób powinienem poradzić sobie z porównywaniem dwóch rozwiązań za pomocą kodu IL?
  • Ogólnie rzecz biorąc, czy rozwiązanie z mniejszą IL LOC oznacza, że ​​będzie ono szybsze lub zużywa mniej pamięci?
  • Jak mówi tytuł, czy mogę porównać kod IL, aby określić, która technika jest szybsza lub lepsza?

1) Twoje założenia są poprawne w tym, co się dzieje.

2) Musisz zrozumieć, co robi kod IL w celu ustalenia, co jest „lepsze”

3) Nie oznacza to, że zajmuje mniej instrukcjami, aby uruchomić. Jednak te indywidualne instrukcje mogą zużywać więcej pamięci lub mniej. Na przykład instrukcja, do której się odwołujesz, w jednym przypadku tworzy delegata Func, a w drugim tworzy obiekt konwertera. Bez większej ilości informacji trudno jest stwierdzić, która z tych dwóch rzeczy jest droższa.

4) Tak i nie ....

Problem polega na tym, że kod IL będzie Ci powiedzieć co się dzieje, ale to naprawdę zagnieżdżone wzywa w IL, że będą duże kierowca wydajność. Jeśli kod IL wykonuje wszędzie proste operacje, ogólnie im krótszy, tym lepiej (chociaż poszczególne operacje IL mogą różnić się szybkością, same). Kiedy kod wywołuje metody lub konstruktory na innych typach, na przykład twoich, nie można tego stwierdzić sam. Jedna linia IL może zająć więcej czasu w jednym przypadku (jeśli na przykład wywoła kosztowną metodę) niż 50 w innym przypadku (gdzie wykonuje proste operacje).

Na przykład w powyższym przykładzie pierwsze 20 operacji jest bardzo, bardzo szybkich, podczas gdy ostatnie kilka zajmuje prawie cały twój czas wykonania.

+0

Ciekawe, pomyślałem, że pokaże wszystkie zagnieżdżone wywołania IL, dzięki za wspaniałą odpowiedź! Jako boczne pytanie, czy wiesz, gdzie mogę dowiedzieć się więcej o IL lub czy powinienem zająć się tym zbytnio? Przepraszam, wiem, że to są rodzaj zaangażowanych pytań. –

+2

Aby naprawdę zrozumieć IL, rozpocznij czytanie teorii kompilacji i/lub języka asemblera. Pomaga tonie zorientować się, co robi. W przeciwnym razie, sekcje MSDN na temat Reflection.Emit mają wiele dobrych informacji. –

3

Fragment pracy dla obu odpowiedzi jest wykonywany w IL 004A (i IL 004F dla drugiego). O ile nie znasz kosztów połączeń zewnętrznych, nie ma praktycznych podstaw, na których można by porównać wyniki dwóch odpowiedzi.

Powiązane problemy