2011-10-20 11 views
8

Przeczytałem kilka artykułów i pytań/odpowiedzi, które kończą najlepszą praktyką, to pozwolić kompilatorowi JIT wykonać całą optymalizację wywołań funkcji inline. Ma sens.Czy kompilator JIT optymalizuje (inline) niepotrzebne deklaracje zmiennych?

Co z deklaracjami zmiennych wstawianych? Czy kompilator również je optymalizuje?

Oznacza to, że będzie to:

 Dim h = (a + b + c)/2  'Half-Perimeter 

     If maxEdgeLength/(Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) <= MaximumTriangleAspectRatio Then 
      'Do stuff here. 
     End If 

mają lepsze osiągi niż to:

 Dim perimeter = a + b + c 'Perimeter 
     Dim h = perimeter/2  'Half-Perimeter 

     Dim area = Math.Sqrt(h * (h - a) * (h - b) * (h - c)) 'Heron's forumula. 
     Dim inradius = area/h 
     Dim aspectRatio = maxEdgeLength/inradius 

     If aspectRatio <= MaximumTriangleAspectRatio Then 
      'Do stuff here. 
     End If 

Oczywiście wolę ten ostatni, ponieważ jest to łatwiejsze do odczytania i debugowania, ale nie mogę sobie pozwolić pogorszenie wydajności, jeśli istnieje.

Uwaga: Zidentyfikowałem już ten kod jako wąskie gardło - nie ma potrzeby stosowania retorty o przedwczesnej optymalizacji. :-)

+6

Nie stać Cię na dodatkowe 20 bajtów pamięci RAM?Jest to mało prawdopodobne, aby zwiększyć wydajność aplikacji, zwłaszcza, że ​​ten kod jest uruchamiany wielokrotnie za każdym razem o 20 bajtów. –

+0

Czy kompilator (a nie JIT!) Nie byłby "odpowiedzialny" za tego rodzaju optymalizację podczas tłumaczenia na MSIL? (A jeśli tak, w jakim stopniu optymalizuje kompilator MS C#.) W każdym razie * porównuj go w relatywistycznym środowisku *, ponieważ jest to jedyny sposób "na pewno wiedzieć", który jest szybszy iw jakim zakresie. –

+4

Jesteś jedyną osobą, która może odpowiedzieć na to pytanie. Napisałeś kod w obie strony. Uruchom go w obie strony, zmierz czas, a następnie dowiesz się, który z nich jest szybszy. To, co robi lub nie robi jitter, jest nieistotne; wiedza na temat tego, jitter nie odpowiada na pytanie "który z nich jest szybszy?" –

Odpowiedz

16

Zmienne tymczasowe o nazwach lub nazwach nie są problemem.

Można jednak znacznie zoptymalizować tę nierówność.

Kod było:

If maxEdgeLength/(Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) <= MaximumTriangleAspectRatio Then 

pomnożyć obie strony przez pierwiastek kwadratowy, eliminując podział (nierówność jest zachowana, ponieważ pierwiastek kwadratowy nie może wrócić liczbę ujemną):

If maxEdgeLength <= (Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) * MaximumTriangleAspectRatio Then 

Teraz kwadratowy obie strony, aby wyeliminować ten kosztowny pierwiastek kwadratowy:

If maxEdgeLength * maxEdgeLength <= h * (h - a) * (h - b) * (h - c)/h/h * MaximumTriangleAspectRatio * MaximumTriangleAspectRatio Then 

Anuluj, a m ostatecznie przez h.

If maxEdgeLength * maxEdgeLength * h <= (h - a) * (h - b) * (h - c) * MaximumTriangleAspectRatio * MaximumTriangleAspectRatio Then 

To będzie dużo szybciej. Jeśli to obliczenie się powtórzy, rozważ buforowanie wyników części tego wyrażenia, aby uzyskać jeszcze większą poprawę.

Użyj komentarzy, aby objaśnić formułę. Pozbycie się funkcji wywoływania wąskich gardeł wart jest napisania tego wyrażenia w formacie mniejszym niż prosty.

+1

Zamiast (po prostu) używać komentarzy, zmień obliczenie na samodzielną funkcję - w kodzie takim jest zbyt długo. Wyjaśnij *, że * funkcjonuje w razie potrzeby (w szczególności wyjaśnij transformację wykonaną powyżej ...). –

+6

+1 za stwierdzenie, że optymalizacji nie można znaleźć w kodzie ani w kompilacji, ale w samym algorytmie. –

5

Nawiasem mówiąc, po prostu grać adwokata diabła, też chciałem zwrócić na to uwagę:

JIT inline całej funkcji patrzy na długość w bajtach MSIL, a nie złożoność obliczeń. Dodanie zmiennych lokalnych (i oczekiwanie, że JIT je zgłosi) może zwiększyć rozmiar MSIL funkcji wystarczającej do tego, aby cała funkcja nie była kandydatem do wstawiania.

Nie ma większego znaczenia niż niepotrzebne użycie Math.Sqrt, ale jest to możliwe. Jak powiedział Eric Lippert, będziesz wiedzieć więcej, mierząc. Jednak taki pomiar jest ważny tylko dla jednego określonego programu i nie jest generalizowany dla różnych procesorów lub przyszłych wersji środowiska wykonawczego .NET (w tym dodatków Service Pack), które często modyfikują zachowanie JIT. A więc chcesz połączyć analityczne i empiryczne podejście do optymalizacji.

+1

Doskonały punkt Ben; poza drobnymi przypadkami nie ma czegoś takiego jak "optymalizacja". Każda tak zwana "optymalizacja" jest w rzeczywistości kompromisem, który mamy * mamy nadzieję * jest lepszy, ale może nie być. Obliczysz więcej wykorzystania rejestru dla tego obliczenia dla mniejszej liczby rejestrów dostępnych dla tego obliczenia i mam nadzieję, że wybrałeś ten, który musi być szybszy. Albo zamienisz więcej pamięci na mniej czasu i masz nadzieję, że nie spowoduje to braku pamięci podręcznej później. I tak dalej. –

+0

@EricLippert: Tak, absolutna optymalizacja wymaga kompromisów. Ale to jest poza punktami, które robię. Podczas korzystania z kompilacji JIT podlegają zmienionym algorytmom optymalizacyjnym, które powodują różne kompromisy z tym samym wejściem MSIL. Nawet w przypadku tradycyjnych kompilatorów AOT, ta sama sekwencja instrukcji może działać inaczej na różnych procesorach o różnych charakterystykach CPI. –

Powiązane problemy