Kompilasi C # JIT dan .NET

87

Saya menjadi sedikit bingung tentang detail cara kerja kompiler JIT. Saya tahu bahwa C # dikompilasi ke IL. Pertama kali dijalankan itu JIT'd. Apakah ini melibatkan terjemahan ke dalam kode asli? Apakah runtime .NET (sebagai Mesin Virtual?) Berinteraksi dengan kode JIT? Saya tahu ini naif, tetapi saya sendiri benar-benar bingung. Kesan saya selalu bahwa rakitan tidak diinterpretasikan oleh .NET Runtime tetapi saya tidak memahami detail interaksi.

Steve
sumber

Jawaban:

84

Ya, JIT'ing kode IL melibatkan menerjemahkan IL ke dalam instruksi mesin asli.

Ya, runtime .NET berinteraksi dengan kode mesin native JIT, dalam artian bahwa runtime memiliki blok memori yang ditempati oleh kode mesin native, panggilan runtime ke kode mesin native, dll.

Anda benar bahwa runtime .NET tidak menafsirkan kode IL di majelis Anda.

Apa yang terjadi adalah ketika eksekusi mencapai fungsi atau blok kode (seperti, klausa lain dari blok if) yang belum dikompilasi JIT ke dalam kode mesin asli, JIT'r dipanggil untuk mengkompilasi blok IL itu ke dalam kode mesin asli . Setelah selesai, eksekusi program memasukkan kode mesin yang baru dikeluarkan untuk menjalankan logika programnya. Jika ketika menjalankan eksekusi kode mesin asli mencapai fungsi panggilan untuk fungsi yang belum dikompilasi ke kode mesin, JIT'r ini dipanggil untuk mengkompilasi bahwa fungsi "tepat waktu". Dan seterusnya.

JIT'r tidak selalu mengkompilasi semua logika badan fungsi ke dalam kode mesin sekaligus. Jika fungsi memiliki pernyataan if, blok pernyataan dari klausa if atau else tidak dapat dikompilasi JIT sampai eksekusi benar-benar melewati blok itu. Jalur kode yang belum dieksekusi tetap dalam bentuk IL sampai mereka dieksekusi.

Kode mesin native yang telah dikompilasi disimpan dalam memori sehingga dapat digunakan lagi saat berikutnya bagian kode tersebut dijalankan. Kedua kalinya Anda memanggil suatu fungsi, fungsi itu akan berjalan lebih cepat daripada pertama kali Anda memanggilnya karena tidak diperlukan langkah JIT untuk kedua kalinya.

Di desktop .NET, kode mesin asli disimpan di memori selama masa pakai domain aplikasi. Dalam .NET CF, kode mesin asli dapat dibuang jika aplikasi kehabisan memori. Ini akan menjadi JIT yang dikompilasi lagi dari kode IL asli saat eksekusi berikutnya melewati kode itu.

dthorpe.dll
sumber
14
Koreksi pedantic minor - 'JIT'r tidak selalu mengkompilasi semua logika tubuh fungsi' - Dalam implementasi teoretis yang mungkin terjadi, tetapi dalam implementasi runtime .NET yang ada, compiler JIT akan mengkompilasi keseluruhan metode sekaligus.
Paul Alexander
18
Mengenai mengapa hal ini dilakukan, ini terutama karena kompiler non-jit tidak dapat berasumsi bahwa pengoptimalan tertentu tersedia pada platform target tertentu. Satu-satunya pilihan adalah dengan mengkompilasi ke penyebut umum terendah, atau lebih umum, untuk mengkompilasi beberapa versi, masing-masing ditargetkan pada platformnya sendiri. JIT menghilangkan kerugian itu karena terjemahan akhir ke kode mesin dilakukan pada mesin target, di mana kompilator mengetahui pengoptimalan apa yang tersedia.
Chris Shain
1
Chris, meskipun JIT tahu pengoptimalan apa yang tersedia, belum ada waktu untuk menerapkan pengoptimalan yang signifikan. Mungkin NGEN lebih kuat.
Grigory
2
@Grigory Ya, NGEN memiliki kemewahan waktu yang tidak dimiliki oleh kompilator JIT, tetapi ada banyak keputusan codegen yang dapat diambil JIT untuk meningkatkan kinerja kode yang dikompilasi yang tidak memerlukan banyak waktu untuk dipecahkan. Misalnya, kompilator JIT dapat memilih set instruksi yang paling cocok dengan perangkat keras yang tersedia yang ditemukan pada waktu proses tanpa menambahkan waktu yang signifikan ke langkah kompilasi JIT. Saya tidak berpikir .NET JITter melakukan ini secara signifikan, tetapi itu mungkin.
dthorpe
24

Kode "dikompilasi" ke dalam Microsoft Intermediate Language, yang mirip dengan format assembly.

Ketika Anda mengklik dua kali file yang dapat dieksekusi, Windows akan memuat mscoree.dllyang kemudian mengatur lingkungan CLR dan memulai kode program Anda. Compiler JIT mulai membaca kode MSIL dalam program Anda dan secara dinamis mengkompilasi kode tersebut ke dalam instruksi x86, yang dapat dijalankan oleh CPU.

pengguna541686
sumber
2

Saya akan menjelaskan mengompilasi kode IL ke dalam instruksi CPU asli melalui contoh di bawah ini.

public class Example 
{
    static void Main() 
    {
        Console.WriteLine("Hey IL!!!");
    }
}

Terutama CLR mengetahui setiap detail tentang jenis dan metode apa yang dipanggil dari jenis itu karena metadata.

Ketika CLR mulai mengeksekusi IL ke dalam instruksi CPU asli, CLR mengalokasikan struktur data internal untuk setiap jenis yang dirujuk oleh kode Utama.

Dalam kasus kami, kami hanya memiliki satu jenis Konsol, sehingga CLR akan mengalokasikan satu struktur data internal. Melalui struktur internal itu, kami akan mengelola akses ke tipe yang direferensikan.

Di dalam struktur data itu, CLR memiliki entri tentang semua metode yang ditentukan oleh tipe itu. Setiap entri menyimpan alamat di mana implementasi metode dapat ditemukan.

Saat menginisialisasi struktur ini, CLR menyetel setiap entri dalam FUNCTION yang tidak terdokumentasi yang terdapat di dalam CLR itu sendiri. Dan seperti yang bisa Anda tebak, FUNCTION ini yang kami sebut JIT Compiler.

Secara keseluruhan, Anda dapat mempertimbangkan JIT Compiler sebagai fungsi CLR, yang mengompilasi IL ke dalam instruksi CPU asli. Izinkan saya menunjukkan kepada Anda secara detail bagaimana proses ini akan terjadi dalam contoh kita.

1. Saat Main membuat panggilan pertamanya ke WriteLine, fungsi JITCompiler dipanggil.

2. Fungsi Compiler JIT mengetahui metode apa yang dipanggil dan tipe apa yang mendefinisikan metode ini.

3. Kemudian Jit Compiler mencari assembly di mana mendefinisikan tipe itu dan mendapatkan kode IL untuk metode yang ditentukan oleh tipe itu dalam kasus kode IL dari metode WriteLine.

4. Compiler JIT mengalokasikan blok memori DYNAMIC , setelah itu JIT memverifikasi dan mengkompilasi kode IL ke dalam kode CPU asli dan menyimpan kode CPU tersebut di blok memori tersebut.

5. Kemudian compiler JIT kembali ke entri struktur data internal, dan mengganti alamat (yang terutama mengacu pada implementasi kode IL dari WriteLine) dengan alamat blok memori baru yang dibuat secara dinamis, yang berisi instruksi CPU asli dari WriteLine.

6. Terakhir, fungsi JIT Compiler melompat ke kode di blok memori dan mengeksekusi kode asli dari metode garis tulis.

7. Setelah eksekusi WriteLine, kode kembali ke Kode Utama yang melanjutkan eksekusi seperti biasa.

So_oP
sumber
1

.NET menggunakan bahasa perantara yang disebut MSIL, terkadang disingkat sebagai IL. Kompilator membaca kode sumber Anda dan menghasilkan MSIL. Ketika Anda menjalankan program, kompilator .NET Just In Time (JIT) membaca kode MSIL Anda dan menghasilkan aplikasi yang dapat dijalankan di memori. Anda tidak akan melihat semua ini terjadi, tetapi ada baiknya Anda mengetahui apa yang terjadi di balik layar.

Gagan
sumber
0

.NET Framework menggunakan CLR Environment untuk menghasilkan MSIL (Microsoft Intermediate Language), juga disebut sebagai IL. Kompilator membaca kode sumber Anda dan ketika Anda membangun / mengkompilasi proyek Anda, MSIL akan dihasilkan. Sekarang, ketika Anda akhirnya menjalankan proyek Anda, The .NET JIT ( Just-in-time Compiler ) datang beraksi. JIT membaca kode MSIL Anda dan menghasilkan kode asli (yang merupakan instruksi x86) yang dapat dengan mudah dieksekusi oleh CPU. JIT membaca semua instruksi MSIL dan menjalankannya Baris demi Baris.

Jika Anda tertarik untuk melihat, apa yang terjadi di balik layar, itu sudah terjawab. Silakan ikuti - Di sini

Dikshit Kathuria
sumber