11

Jeśli spojrzę na IL, która jest tworzona w Linqpad dla dwóch poniższych fragmentów kodu, zastanawiam się, co się tutaj stanie.Czy kompilator C# jest mądrzejszy od kompilatora VB.NET?

C#

int i = 42; 

wyniki w następujący kod IL

IL_0000: ret 

natomiast VB

Dim i As Integer = 42 

jest

IL_0000: ldc.i4.s 2A 
IL_0002: stloc.0 

Najwyraźniej kompilator C# rozumie, że wartość nigdy nie jest używana, a zatem po prostu nic nie zwraca. W VB.NET rzeczywisty kod jest tłumaczony.

Czy to ze względu na różnice w optymalizacji kompilatora czy jest coś jeszcze w pracy?

Aktualizacja: Żeby to wyjaśnić - po prostu wprowadzić tę jedną linię do LINQPad i spojrzeć na to tworzy IL (zdecydowanie uruchamiając odpowiedni kompilator). Tam nie jest programem.

+0

Wierzę, że kompilator C# wykonuje obecnie więcej optymalizacji (prawie na pewno to się zmieni, gdy pojawi się Roslyn). Ale w tym przypadku jest to trochę fałszywa wygrana dla kompilatora C#, ponieważ prawie na pewno zostanie zoptymalizowany przez kompilator JIT. –

+0

@AakashM: Kiedy piszę, to wszystko, co wchodzę do LinqPad. Nie ma innego kodu. LinqPad produkuje IL, kompilując w tle. – Olaf

+0

Czy kompiluje się to samo w języku C# podczas debugowania? Zakładam, że C# optymalizuje więcej, gdy jest w trybie zwolnienia, a następnie VB.NET. To jeden z powodów, dla których nie można ocenić zmiennych w wydaniu w C#, które nie są używane, gdy można w VB.NET. Wolę to drugie. –

Odpowiedz

10

zabierając pytanie LINQPad, wpadłem vbc i csc z /optimize+ /debug- na temat tych programów:

Module f 

Public Sub Main() 
    Dim i As Integer = 42 
End Sub 

End Module 

i

public static class f 
{ 

public static void Main() 
{ 
    int i = 42; 
} 

} 

i dostał te wyniki cil z ILDASM:

Dla VB:

.method public static void Main() cil managed 
{ 
    .entrypoint 
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = (01 00 00 00) 
    // Code size  4 (0x4) 
    .maxstack 1 
    .locals init (int32 V_0) 
    IL_0000: ldc.i4.s 42 
    IL_0002: stloc.0 
    IL_0003: ret 
} // end of method f::Main 

Dla C#:

.method public hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    // Code size  1 (0x1) 
    .maxstack 8 
    IL_0000: ret 
} // end of method f::Main 

Więc tak, przynajmniej w tym zakresie csc jest 'mądrzejszy' niż vbc. Ale założę się, że JITter usunie wszelkie różnice w czasie wykonywania.

edit

Sprawdziłem i faktycznie wykonywany kod natywny jest różne, przynajmniej w moim systemie.Włożyłem w Console.ReadLine() połączeń w obie dać mi szansę, aby dołączyć debugera, i mam te disassemblies:

Z VB:

00000000 sub   rsp,38h 
00000004 mov   dword ptr [rsp+20h],0 
0000000c mov   rax,7FF000434D8h 
00000016 mov   eax,dword ptr [rax] 
00000018 test  eax,eax 
0000001a je   0000000000000021 
0000001c call  FFFFFFFFE45BA230 
00000021 mov   dword ptr [rsp+20h],2Ah 
00000029 call  FFFFFFFFE26ABF20 
0000002e mov   qword ptr [rsp+28h],rax 
00000033 nop 
00000034 jmp   0000000000000036 
00000036 add   rsp,38h 
0000003a ret 

Od C#:

00000000 sub   rsp,38h 
00000004 mov   rax,7FF000534D8h 
0000000e mov   eax,dword ptr [rax] 
00000010 test  eax,eax 
00000012 je   0000000000000019 
00000014 call  FFFFFFFFE45AA230 
00000019 call  FFFFFFFFE391BF20 
0000001e mov   qword ptr [rsp+20h],rax 
00000023 nop 
00000024 jmp   0000000000000026 
00000026 add   rsp,38h 
0000002a ret 

teraz , mój zespół prawie nie istnieje, ale nawet widzę, że

w wartość from-VB odnosi się do stałej wartości heksadecymalnej 2A, która wynosi 42 dziesiętnie. Więc idź, to robi wykonać więcej instrukcji na końcu.

+2

również mamy większy stos jak w 8 (C#) vs 1 – V4Vendetta

1

Myślę, że w przypadku C# kompilator wykonuje część alokacji pamięci i zapisuje wartość int w jednym kroku, aw przypadku VB oświadczenie DIM jest pierwszym krokiem, który po prostu alokuje pamięć, a następnie zapisuje wartość w drugim kroku. DIM jest generyczny i jest używany ze wszystkimi typami danych, więc może to być coś o jeden krok we wszystkich przypadkach. Myśli?