2010-07-31 9 views
6

Chciałbym wiedzieć - czy .NET JITter rekurencyjnie wstawi małe funkcje wywoływane z innych małych funkcji?Czy .NET JIT będzie wstawiać małą funkcję, która wywołuje inną małą funkcję?

Wystarczy na przykład:

public static float Square(float value) 
{ 
    return value * value; 
} 

public static float Cube(float value) 
{ 
    return Square(value) * value; 
} 

Jeśli zadzwonię Cube skądś to będzie inline całą drogę, albo będę skończyć z wywołaniem funkcji do Square?

A jeśli tak, to jak głęboko będzie się powtarzał, aby wykonać inline? (Powiedzmy, że byłem na tyle szalony, aby zaimplementować funkcję Quartic lub Quintic w ten sam sposób.)

Odpowiedz

10

Niestety, wybrałeś zły przykład. Kompilator JIT x86 nie wstawia metod, które zwracają zmienną. Nie jestem w 100% pewien, dlaczego, myślę, że ma to na celu uniknięcie powtarzających się problemów, gdy zmiennoprzecinkowe są konwertowane na 80-bitową wartość zmiennoprzecinkową w FPU. Dokładność wewnętrzna wynosi 80 bitów, ale te dodatkowe bity są odcinane, gdy 80-bitowa wartość zostanie obcięta z powrotem do 32-bitowej wartości, gdy zostanie przepłukana z powrotem do pamięci. Zbyt długie utrzymywanie wartości w jednostkach FPU zapobiega występowaniu tego obcinania i zmienia wynik obliczeń.

przypadku wymiany pływaka przez podwójne i skompilować ten kod:

static void Main(string[] args) { 
    Console.WriteLine(Cube(2.0)); 
} 

Wtedy to kod maszynowy jest generowany, gdy optymalizator JIT jest włączone:

00000000 push  ebp        ; setup stack frame 
00000001 mov   ebp,esp 
00000003 call  6DA2BEF0      ; Console.get_Out() 
00000008 fld   qword ptr ds:[010914B0h]  ; ST0 = 8.0 
0000000e sub   esp,8       ; setup argument for WriteLine 
00000011 fstp  qword ptr [esp] 
00000014 mov   ecx,eax       ; call Console.Out.WriteLine 
00000016 mov   eax,dword ptr [ecx] 
00000018 call  dword ptr [eax+000000D0h] 
0000001e pop   ebp        ; done 
0000001f ret 

Nie tylko inline funkcji, był w stanie ocenić wyrażenia w czasie kompilacji. I bezpośrednio przekazuje wynik, wywołując Console.WriteLine (8.0). Całkiem dobrze, huh?

Użyj podwójnie, nie pływaj.

+1

Interesujące ... ponieważ pracuję w XNA, gdzie * wszystko * jest zmiennoprzecinkowe! Nieco dotyczy ... –

+0

* "Kompilator JIT x86 nie wbudowuje metod, które zwracają zmienną wartość." * - Czy jesteś tego absolutnie pewien? Przeszukałem wysokie i niskie wyniki i nie mogę znaleźć żadnych odniesień do tego. Najlepszą rzeczą, jaką znalazłem, była ta strona w Connect: https://connect.microsoft.com/VisualStudio/feedback/details/536781/unexpected-jit-inlining-behavior, która wydaje się sugerować, że funkcje zwracające float * do *, w rzeczywistości, inline. I ten (stary) artykuł sugeruje, że zasady dotyczące przymusu (a więc mogą być inne w praktyce) są takie same dla elementów pływających i podwójnych: http://blogs.msdn.com/b/davidnotario/archive/2005/08/08/449092. aspx –

+1

@Andrew: reguły wprowadzania nie są udokumentowane, a jedynie w niektórych postach na blogu. Ważne, ponieważ muszą być w stanie to zmienić, aby poprawić fluktuacje bez łamania założeń wynikających z udokumentowanego zachowania. Mogę tylko udokumentować to, co widzę, jak robi się mój jitter x86. I zdecydowanie nie wprowadza wersji float tych metod. Czy widzisz inne zachowanie? –

Powiązane problemy