c# ---> asembler

0

Z tego co wiem każdy program napisany w języku wysokopoziomowym by zadziałać najpierw jest kompilowany na asemblera następnie na kod maszynowy. Jest jakaś możliwość żeby zobaczyć swój program jako kod asemblerowy? (sorry jak coś pomieszałam i tak nie jest :P)

0

Co do rozgraniczenia na języki wysokiego i niskiego poziomu - to niejednoznaczne. Dla jednych językiem niskiego poziomu będzie asm, a np. C już będzie językiem wysokiego poziomu ;) Dla innych niekoniecznie.

Co do pytania. C# jest kompilowany do IL - Intermediate Language. Możesz się posłużyć takim narzędziem jak ILSpy do tego. Natomiast z tego co wiem, C# nie kompiluje się do typowego assemblera. C# pracuje na maszynie wirtualnej.

1

@eliasta: w menu: Debug -> Windows -> Disassembly.

@Juhas, procesory nie wykonują kodu IL.

0

Program C# jest kompilowany w "locie" na instrukcje procesora po uruchomieniu programu .
Kompilacja w C# polega na przetworzeniu kodu źródłowego na język pośredni IL.
Typy podstawowe takie jak int , operacje dodawania, dzielenia itd są zdefiniowane w samym języku IL.
Implementacje niektórych metod dodaje też samo środowisko CLR . Wszystko po to żeby program działał jak najszybciej .

Do przeglądania kodu IL, metadanych , manifestu możesz użyć darmowego programu ildasm.exe, który powinien się znajdować w takim katalogu np.
"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.1 Tools\ildasm.exe"
Edytować kod IL najwygodniej jest w Visual Studio Code . A do ponownej kompilacji kodu IL możesz użyć programu Ilasm.exe . https://docs.microsoft.com/pl-pl/dotnet/framework/tools/ilasm-exe-il-assembler

Jest jeszcze takie coś: Kompilowanie aplikacji z architekturą .NET Native ale ja jeszcze tego nie próbowałem.

Specyfikację języka IL możesz pobrać ze strony : https://www.ecma-international.org/publications/standards/Ecma-335.htm
Warto znać język IL. Możesz wtedy ręcznie zoptymalizować kod .

1
Zimny Krawiec napisał(a):

Warto znać język IL. Możesz wtedy ręcznie zoptymalizować kod .

Zrobiłeś to kiedyś? Albo ktokolwiek kiedykolwiek?

0

Wiele razy . Na początku szło opornie ale potem język IL wydaje się bardzo prosty .
Jedna rzecz która jest trochę męcząca dla mnie , Standardowe biblioteki NET wyglądają trochę inaczej niż to co daje kompilator C# .
Wyglądają tak jakby ktoś je ręcznie pisał bez używania kompilatora.
Kompilator C# często dodaje dużo nadmiarowego kodu , który można pominąć .

1

A jakie były mierzalne efekty tego działania? I jak organizujesz pracę, aby tych ręczne optymalizacje nie zniknęły po ponownej kompilacji projektu?

0

Jak chcesz pisać w IL to musisz się przyzwyczaić że to działa na zasadzie stosu ,
Możesz wrzucić kilka danych na stos a potem je zdejmować,
Czasem bardzo jest to nieczytelne i męczące .

0

Nie do końca zrozumiałem o co pytasz ,
Mierzalnym efektem jest np. zmniejszenie o 30% objętości kodu albo zorbienie czegoś na co nie pozwala kompilator C# a pozwala środowisko CLR .

0

A jakie praktyczne znaczenie ma objętość kodu? I o jakie rzeczy, które CLR może, a C# nie chodzi?
Dla mnie mierzalny efektem optymalizacji jest zoptymalizowanie czegoś. ;) Czyli szybkości działania albo zużycia pamięci w jakiś znaczący i zauważalny dla użytkowników sposób.

0

@somekind: No jak Ty nie rozumiesz. Przecież to proste: "musisz się przyzwyczaić że to działa na zasadzie stosu". STOSU!!! :)
ROTFL

Aaa, przypomniałem sobie, że to ja właściwie napisałem trochę kodu w IL ale to był inny IL, z IEC 61131-3. I tam faktycznie były rzeczy, których w innym języku się nie dało napisać, przynajmniej na starszych sterownikach Siemensa.

1

W Visual Studio podczas debugowania można normalnie oglądać wykonywany kod binarny (i jego odpowiednik w asemblerze), również dla programów pisanych w C#.
Nie trzeba nic wiedzieć o IL ani nawet o jego istnieniu.

v.png

Z tym, że oglądanie wygenerowanego asemblera w wersji debug trochę mija się z celem, bo widzimy wersję niezoptymalizowaną kodu.
Jeśli chcemy zobaczyć co kompilator jest rzeczywiście w stanie z naszym kodem zrobić, trzeba debugować wersję release programu oraz wyłączyć w ustawieniach Visuala opcje "Suppress JIT optimization on module load" i "Enable Just My Code".
Jednak wtedy debugger potrafi zachowywać się dziwnie.

0

Oglądanie tego w ogóle mija się z celem . C# to nie jest niezarządzany kod C++ .
Z tego nic kompletnie nie wyczytasz . Co innego kod IL.

1

Kod z optymalizacją i bez optymalizacji .

Gdybym optymalizował ręcznie kod to prawdopodobnie zrobiłbym to identycznie albo prawie identycznie.

using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("Podaj pierwszą liczbę : ");
            int a = int.Parse(Console.ReadLine());

            Console.Write("Podaj drugą liczbę : ");
            int b = int.Parse(Console.ReadLine());

            if (a > b) Console.WriteLine($"{a} > {b}");
            else
            Console.WriteLine($"{a} < {b}");
        }
    }
}

kod metody main bez optymalizacji kod IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       112 (0x70)
  .maxstack  3
  .locals init ([0] int32 a,
           [1] int32 b,
           [2] bool V_2)
  IL_0000:  nop
  IL_0001:  ldstr      bytearray (50 00 6F 00 64 00 61 00 6A 00 20 00 70 00 69 00   // P.o.d.a.j. .p.i.
                                  65 00 72 00 77 00 73 00 7A 00 05 01 20 00 6C 00   // e.r.w.s.z... .l.
                                  69 00 63 00 7A 00 62 00 19 01 20 00 3A 00 20 00 ) // i.c.z.b... .:. .
  IL_0006:  call       void [mscorlib]System.Console::Write(string)
  IL_000b:  nop
  IL_000c:  call       string [mscorlib]System.Console::ReadLine()
  IL_0011:  call       int32 [mscorlib]System.Int32::Parse(string)
  IL_0016:  stloc.0
  IL_0017:  ldstr      bytearray (50 00 6F 00 64 00 61 00 6A 00 20 00 64 00 72 00   // P.o.d.a.j. .d.r.
                                  75 00 67 00 05 01 20 00 6C 00 69 00 63 00 7A 00   // u.g... .l.i.c.z.
                                  62 00 19 01 20 00 3A 00 20 00 )                   // b... .:. .
  IL_001c:  call       void [mscorlib]System.Console::Write(string)
  IL_0021:  nop
  IL_0022:  call       string [mscorlib]System.Console::ReadLine()
  IL_0027:  call       int32 [mscorlib]System.Int32::Parse(string)
  IL_002c:  stloc.1
  IL_002d:  ldloc.0
  IL_002e:  ldloc.1
  IL_002f:  cgt
  IL_0031:  stloc.2
  IL_0032:  ldloc.2
  IL_0033:  brfalse.s  IL_0053
  IL_0035:  ldstr      "{0} > {1}"
  IL_003a:  ldloc.0
  IL_003b:  box        [mscorlib]System.Int32
  IL_0040:  ldloc.1
  IL_0041:  box        [mscorlib]System.Int32
  IL_0046:  call       string [mscorlib]System.String::Format(string,
                                                              object,
                                                              object)
  IL_004b:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0050:  nop
  IL_0051:  br.s       IL_006f
  IL_0053:  ldstr      "{0} < {1}"
  IL_0058:  ldloc.0
  IL_0059:  box        [mscorlib]System.Int32
  IL_005e:  ldloc.1
  IL_005f:  box        [mscorlib]System.Int32
  IL_0064:  call       string [mscorlib]System.String::Format(string,
                                                              object,
                                                              object)
  IL_0069:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_006e:  nop
  IL_006f:  ret
} // end of method Program::Main

kod IL po optymalizacji

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       102 (0x66)
  .maxstack  3
  .locals init ([0] int32 a,
           [1] int32 b)
  IL_0000:  ldstr      bytearray (50 00 6F 00 64 00 61 00 6A 00 20 00 70 00 69 00   // P.o.d.a.j. .p.i.
                                  65 00 72 00 77 00 73 00 7A 00 05 01 20 00 6C 00   // e.r.w.s.z... .l.
                                  69 00 63 00 7A 00 62 00 19 01 20 00 3A 00 20 00 ) // i.c.z.b... .:. .
  IL_0005:  call       void [mscorlib]System.Console::Write(string)
  IL_000a:  call       string [mscorlib]System.Console::ReadLine()
  IL_000f:  call       int32 [mscorlib]System.Int32::Parse(string)
  IL_0014:  stloc.0
  IL_0015:  ldstr      bytearray (50 00 6F 00 64 00 61 00 6A 00 20 00 64 00 72 00   // P.o.d.a.j. .d.r.
                                  75 00 67 00 05 01 20 00 6C 00 69 00 63 00 7A 00   // u.g... .l.i.c.z.
                                  62 00 19 01 20 00 3A 00 20 00 )                   // b... .:. .
  IL_001a:  call       void [mscorlib]System.Console::Write(string)
  IL_001f:  call       string [mscorlib]System.Console::ReadLine()
  IL_0024:  call       int32 [mscorlib]System.Int32::Parse(string)
  IL_0029:  stloc.1
  IL_002a:  ldloc.0
  IL_002b:  ldloc.1
  IL_002c:  ble.s      IL_004a
  IL_002e:  ldstr      "{0} > {1}"
  IL_0033:  ldloc.0
  IL_0034:  box        [mscorlib]System.Int32
  IL_0039:  ldloc.1
  IL_003a:  box        [mscorlib]System.Int32
  IL_003f:  call       string [mscorlib]System.String::Format(string,
                                                              object,
                                                              object)
  IL_0044:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0049:  ret
  IL_004a:  ldstr      "{0} < {1}"
  IL_004f:  ldloc.0
  IL_0050:  box        [mscorlib]System.Int32
  IL_0055:  ldloc.1
  IL_0056:  box        [mscorlib]System.Int32
  IL_005b:  call       string [mscorlib]System.String::Format(string,
                                                              object,
                                                              object)
  IL_0060:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0065:  ret
} // end of method Program::Main
0

Zimny Krawiec:
To czyste marnowanie czasu. Zysk dla użytkownika końcowego jest zerowy, albo ujemny, bo czas spędzony na oszczędzaniu kilku bajtów można przeznaczyć na poprawienie funkcjonalności czy wydajności (poprzez np polepszenie złożoności obliczeniowej, a nie samej stałej w złożoności).

Jeśli lubisz siedzieć w IL to jest fajne zadanie dla ciebie: https://github.com/oracle/graal/issues/349 :)

0
Chory Lew napisał(a):

Oglądanie tego w ogóle mija się z celem . C# to nie jest niezarządzany kod C++ .
Z tego nic kompletnie nie wyczytasz . Co innego kod IL.

Jak to "nic nie wyczytasz"? To jest rzeczywiście wykonywany kod. Jak kogoś nie interesuje to niech nie ogląda, ale… moim zdaniem czytanie IL ma jeszcze mniejszy sens.

0

To weź wstaw jakiś krótki programik - 2 -3 linijki kodu , listing tego twojego asemblera
i omów co on zawiera . Bo ja odnośnie IL mogę ci wszystko wytłumaczyć co i jak ,

Pozdrawiam serdecznie ;)

0

Ja czasami czytam bajtkod Javowy, ale tylko jak chcę się upewnić jak coś tam działa pod spodem. Nie czytam bajtkodu po to, by ocenić jak dobrą (czy nie do końca dobrą) robotę zrobił kompilator.

Nie widzę sensu poprawiania bajtkodu skoro JIT w JVMie radzi sobie z optymalizacją (względnie) skomplikowanych abstrakcji na poziomie języka: dyskusja nad foreach w .NET i porównanie do Javy

0
Zimny Krawiec napisał(a):

Bo ja odnośnie IL mogę ci wszystko wytłumaczyć co i jak ,

Tylko że… po co czytać IL, skoro masz ten sam kod w C#? :-)
Chyba tylko żeby zobaczyć na co przekłada się jakiś lukier składniowy, ot choćby ten foreach.

0

OK . Weźmy pod lupę instrukcję foreach na przykładzie typu List<T> , żeby lepiej zrozumieć jak to hula ;)

using System;
using System.Collections.Generic;
namespace ConsoleApp40
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> lista = new List<int>(5);
            lista.Add(1);
            lista.Add(2);
            lista.Add(3);
            lista.Add(4);
            lista.Add(5);

            foreach (int item in lista)
            {
                Console.Write(item + ",");
            }
        }
    }
}

Otrzymujemy kod IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       106 (0x6a)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<int32> lista,
           [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> V_1,
           [2] int32 item)
  IL_0000:  ldc.i4.5
  IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor(int32)
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.1
  IL_0009:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_000e:  ldloc.0
  IL_000f:  ldc.i4.2
  IL_0010:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_0015:  ldloc.0
  IL_0016:  ldc.i4.3
  IL_0017:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_001c:  ldloc.0
  IL_001d:  ldc.i4.4
  IL_001e:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_0023:  ldloc.0
  IL_0024:  ldc.i4.5
  IL_0025:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_002a:  ldloc.0
  IL_002b:  callvirt   instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
  IL_0030:  stloc.1
  .try
  {
    IL_0031:  br.s       IL_0050
    IL_0033:  ldloca.s   V_1
    IL_0035:  call       instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
    IL_003a:  stloc.2
    IL_003b:  ldloc.2
    IL_003c:  box        [mscorlib]System.Int32
    IL_0041:  ldstr      ","
    IL_0046:  call       string [mscorlib]System.String::Concat(object,
                                                                object)
    IL_004b:  call       void [mscorlib]System.Console::Write(string)
    IL_0050:  ldloca.s   V_1
    IL_0052:  call       instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
    IL_0057:  brtrue.s   IL_0033
    IL_0059:  leave.s    IL_0069
  }  // end .try
  finally
  {
    IL_005b:  ldloca.s   V_1
    IL_005d:  constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
    IL_0063:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0068:  endfinally
  }  // end handler
  IL_0069:  ret
} // end of method Program::Main

Czyli zamiast instrukcji forach możemy napisać tak : ( możemy też zrezygnować z jednej zmiennej )

using System;
using System.Collections.Generic;
namespace ConsoleApp40
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> lista = new List<int>(5);
            lista.Add(1);
            lista.Add(2);
            lista.Add(3);
            lista.Add(4);
            lista.Add(5);
            using (List<int>.Enumerator item = lista.GetEnumerator())
            {
                while (item.MoveNext())
                {
                    Console.Write(item.Current + ",");
                }
            }
        }
    }
}
0

albo krócej :. Myślę,że nikt się chyba nie zgorszy ;)

using System;
using System.Collections.Generic;
namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
          
            using (List<int>.Enumerator item = new List<int> { 1, 2, 3, 4, 5 }.GetEnumerator())
            {
                while (item.MoveNext())
                {
                    Console.Write(item.Current + ",");
                }
            }
        }
    }
}
0

Ale coś z tego wynika czy tylko potwierdzasz słowa @Azarien?

Możemy używać enumeratorow i używaliśmy zanim wynaleziono koło i inne różne rzeczy.

0

@Mistrzowski Lew: czyli potwierdziłeś, że foreach jest lukrem składniowym na GetEnumerator ;-)

0

Wiem że to nie jest żadna wiedza tajemna . Wyluzujcie chopaki ;)

0

Spoko. Hobbystycznie to sobie można sobie oglądać jak co działa ale w normalnej pracy to strata czasu. Ja jestem często po tej stronie co płaci i zwykle dość nerwowo reaguje na takie tematy bo dla mnie to strata czasu czyli pieniędzy.
Kiedyś znajomy pisał własny parser do XMl-a. A było to w czasach jak już był LINQ a On o tym nie wiedział. Sporo niepotrzebnych godzin i trzeba się było pożegnać.

1 użytkowników online, w tym zalogowanych: 0, gości: 1