การคอมไพล์ C # JIT และ. NET


87

ฉันสับสนเล็กน้อยเกี่ยวกับรายละเอียดของการทำงานของคอมไพเลอร์ JIT ฉันรู้ว่า C # รวบรวมลงไปที่ IL ครั้งแรกที่เรียกใช้คือ JIT'd สิ่งนี้เกี่ยวข้องกับการแปลเป็นรหัสดั้งเดิมหรือไม่ รันไทม์. NET (เป็น Virtual Machine หรือไม่) โต้ตอบกับรหัส JIT? ฉันรู้ว่ามันไร้เดียงสา แต่ฉันสับสนตัวเองจริงๆ ความประทับใจของฉันคือเสมอว่าแอสเซมบลีไม่ได้ถูกตีความโดยรันไทม์. NET แต่ฉันไม่เข้าใจรายละเอียดของการโต้ตอบ

คำตอบ:


84

ใช่รหัส JIT'ing IL เกี่ยวข้องกับการแปล IL เป็นคำแนะนำของเครื่องดั้งเดิม

ใช่รันไทม์. NET โต้ตอบกับรหัสเครื่องดั้งเดิมของ JIT'ed ในแง่ที่ว่ารันไทม์เป็นเจ้าของบล็อกหน่วยความจำที่ครอบครองโดยรหัสเครื่องดั้งเดิมรันไทม์เรียกไปยังรหัสเครื่องดั้งเดิมเป็นต้น

คุณถูกต้องที่รันไทม์. NET ไม่ตีความรหัส IL ในชุดประกอบของคุณ

สิ่งที่จะเกิดขึ้นคือเมื่อการดำเนินการถึงฟังก์ชันหรือบล็อกโค้ด (เช่นประโยคอื่นของบล็อก if) ที่ยังไม่ได้รับการคอมไพล์ JIT เป็นรหัสเครื่องดั้งเดิม JIT'r จะถูกเรียกให้รวบรวมบล็อกของ IL เป็นรหัสเครื่องดั้งเดิม . เมื่อเสร็จแล้วการเรียกใช้โปรแกรมจะป้อนรหัสเครื่องที่เพิ่งปล่อยออกมาเพื่อรันตรรกะของโปรแกรม ถ้าในขณะที่การดำเนินการที่เรียกใช้รหัสเครื่องพื้นเมืองถึงการเรียกฟังก์ชั่นการทำงานที่ยังไม่ได้รับการเรียบเรียงรหัสเครื่อง JIT'r จะเรียกเพื่อรวบรวมว่าฟังก์ชั่น "เพียงในเวลา" และอื่น ๆ

JIT'r ไม่จำเป็นต้องรวบรวมลอจิกทั้งหมดของเนื้อฟังก์ชั่นเป็นรหัสเครื่องพร้อมกัน หากฟังก์ชันมีคำสั่ง if บล็อกคำสั่งของส่วนคำสั่ง if หรือ else อาจไม่ถูกคอมไพล์ JIT จนกว่าการดำเนินการจะผ่านบล็อกนั้นจริงๆ เส้นทางรหัสที่ไม่ได้ดำเนินการจะยังคงอยู่ในรูปแบบ IL จนกว่าจะดำเนินการ

รหัสเครื่องดั้งเดิมที่คอมไพล์แล้วจะถูกเก็บไว้ในหน่วยความจำเพื่อให้สามารถใช้งานได้อีกครั้งในครั้งต่อไปที่ส่วนของโค้ดดำเนินการ ครั้งที่สองที่คุณเรียกใช้ฟังก์ชันมันจะทำงานเร็วกว่าครั้งแรกที่คุณเรียกเพราะไม่จำเป็นต้องมีขั้นตอน JIT ในครั้งที่สอง

ในเดสก์ท็อป. NET รหัสเครื่องดั้งเดิมจะถูกเก็บไว้ในหน่วยความจำตลอดอายุการใช้งานของ appdomain ใน. NET CF รหัสเครื่องดั้งเดิมอาจถูกโยนทิ้งไปหากแอปพลิเคชันมีหน่วยความจำเหลือน้อย จะถูกรวบรวม JIT อีกครั้งจากรหัส IL เดิมในครั้งถัดไปที่การประมวลผลผ่านรหัสนั้น


14
การแก้ไขที่มีความรู้เล็กน้อย - 'JIT'r ไม่จำเป็นต้องรวบรวมตรรกะทั้งหมดของเนื้อความของฟังก์ชัน' - ในการใช้งานทางทฤษฎีที่อาจเป็นเช่นนั้น แต่ในการใช้งานรันไทม์. NET ที่มีอยู่คอมไพเลอร์ JIT จะรวบรวมทั้งหมด วิธีการทั้งหมดในครั้งเดียว
Paul Alexander

18
ในฐานะที่เป็นไปทำไมนี้จะทำมันเป็นหลักเพราะคอมไพเลอร์ที่ไม่ใช่ JIT ไม่สามารถสรุปได้ว่าการเพิ่มประสิทธิภาพบางอย่างที่มีอยู่บนแพลตฟอร์มเป้าหมายใดก็ตาม ตัวเลือกเดียวคือการคอมไพล์ให้เป็นตัวหารร่วมที่ต่ำที่สุดหรือโดยทั่วไปเพื่อรวบรวมหลายเวอร์ชันโดยแต่ละตัวกำหนดเป้าหมายไปที่แพลตฟอร์มของตัวเอง JIT กำจัดข้อเสียดังกล่าวเนื่องจากการแปลสุดท้ายเป็นรหัสเครื่องเสร็จสิ้นบนเครื่องเป้าหมายซึ่งคอมไพเลอร์รู้ว่ามีการเพิ่มประสิทธิภาพใดบ้าง
Chris Shain

1
คริสคิดว่า JIT รู้ว่ามีการเพิ่มประสิทธิภาพใดบ้าง แต่ก็ไม่มีเวลาที่จะใช้การเพิ่มประสิทธิภาพที่สำคัญใด ๆ NGEN น่าจะมีประสิทธิภาพมากกว่า
Grigory

2
@Grigory ใช่ NGEN มีช่วงเวลาที่หรูหราซึ่งคอมไพเลอร์ JIT ไม่มี แต่มีการตัดสินใจเกี่ยวกับ codegen มากมายที่ JIT สามารถทำได้เพื่อปรับปรุงประสิทธิภาพของโค้ดที่คอมไพล์แล้วซึ่งไม่ต้องใช้เวลามากในการคิดออก ตัวอย่างเช่นคอมไพเลอร์ JIT สามารถเลือกชุดคำสั่งเพื่อให้ตรงกับฮาร์ดแวร์ที่มีอยู่ในรันไทม์มากที่สุดโดยไม่ต้องเพิ่มเวลาที่สำคัญให้กับขั้นตอนการคอมไพล์ JIT ฉันไม่คิดว่า. NET JITter จะทำสิ่งนี้ในรูปแบบที่สำคัญ แต่ก็เป็นไปได้
dthorpe

24

โค้ดถูก "คอมไพล์" เป็น Microsoft Intermediate Language ซึ่งคล้ายกับรูปแบบแอสเซมบลี

เมื่อคุณดับเบิลคลิกที่ไฟล์ปฏิบัติการ Windows จะโหลดmscoree.dllซึ่งจะตั้งค่าสภาพแวดล้อม CLR และเริ่มโค้ดโปรแกรมของคุณ คอมไพเลอร์ JIT เริ่มอ่านโค้ด MSIL ในโปรแกรมของคุณและคอมไพล์โค้ดเป็นคำสั่ง x86 แบบไดนามิกซึ่ง CPU สามารถดำเนินการได้


2

ฉันจะอธิบายการรวบรวมรหัส IL เป็นคำแนะนำของ CPU ดั้งเดิมตามตัวอย่างด้านล่าง

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

โดยพื้นฐานแล้ว CLR จะรู้ทุกรายละเอียดเกี่ยวกับประเภทและวิธีการที่เรียกจากประเภทนั้นเนื่องจากข้อมูลเมตา

เมื่อ CLR เริ่มดำเนินการ IL ในคำสั่ง CPU ดั้งเดิมเวลานั้น CLR จะจัดสรรโครงสร้างข้อมูลภายในสำหรับทุกประเภทที่อ้างอิงโดยรหัสของ Main

ในกรณีของเราเรามีคอนโซลเพียงประเภทเดียวดังนั้น CLR จะจัดสรรโครงสร้างข้อมูลภายในหนึ่งโครงสร้าง ด้วยโครงสร้างภายในนั้นเราจะจัดการการเข้าถึงประเภทที่อ้างอิง

ภายในโครงสร้างข้อมูลนั้น CLR มีรายการเกี่ยวกับวิธีการทั้งหมดที่กำหนดโดยประเภทนั้น แต่ละรายการมีที่อยู่ที่สามารถพบการใช้งานวิธีการ

เมื่อเริ่มต้นโครงสร้างนี้ CLR จะตั้งค่าแต่ละรายการในFUNCTION ที่ไม่มีเอกสารซึ่งอยู่ภายใน CLR เอง และอย่างที่คุณเดาได้FUNCTION นี้คือสิ่งที่เราเรียกว่า JIT Compiler

โดยรวมแล้วคุณสามารถพิจารณา JIT Compiler เป็นฟังก์ชัน CLR ซึ่งรวบรวม IL เข้ากับคำสั่งของ CPU ดั้งเดิม ให้ฉันแสดงรายละเอียดว่ากระบวนการนี้จะเป็นอย่างไรในตัวอย่างของเรา

1. เมื่อ Main โทรไปยัง WriteLine เป็นครั้งแรกฟังก์ชัน JITCompiler จะถูกเรียกใช้

2. ฟังก์ชั่น JIT Compiler รู้ว่าวิธีใดถูกเรียกใช้และประเภทใดที่กำหนดเมธอดนี้

3. จากนั้น Jit Compiler จะค้นหาแอสเซมบลีที่กำหนดประเภทนั้นและรับรหัส IL สำหรับวิธีการที่กำหนดโดยประเภทนั้นในกรณีของเรารหัส IL ของวิธี WriteLine

4. คอมไพเลอร์ JIT จัดสรรบล็อกหน่วยความจำDYNAMICหลังจากนั้น JIT จะตรวจสอบและรวบรวมรหัส IL เป็นรหัส CPU ดั้งเดิมและบันทึกรหัส CPU นั้นในบล็อกหน่วยความจำนั้น

5. จากนั้นคอมไพเลอร์ JIT จะกลับไปที่รายการโครงสร้างข้อมูลภายในและแทนที่แอดเดรส (ซึ่งส่วนใหญ่อ้างอิงถึงการใช้โค้ด IL ของ WriteLine) ด้วยแอดเดรสบล็อกหน่วยความจำที่สร้างแบบไดนามิกใหม่ซึ่งมีคำสั่ง CPU ดั้งเดิมของ WriteLine

6. ในที่สุดฟังก์ชัน JIT Compiler จะข้ามไปยังโค้ดในบล็อกหน่วยความจำและเรียกใช้โค้ดเนทีฟของวิธีการเขียน

7. หลังจากดำเนินการ WriteLine โค้ดจะกลับไปที่ Mains'code ซึ่งจะดำเนินการต่อไปตามปกติ


1

.NET ใช้ภาษากลางเรียกว่า MSIL บางครั้งย่อว่า IL คอมไพเลอร์อ่านซอร์สโค้ดของคุณและสร้าง MSIL เมื่อคุณรันโปรแกรมคอมไพลเลอร์. NET Just In Time (JIT) จะอ่านรหัส MSIL ของคุณและสร้างแอปพลิเคชันที่เรียกใช้งานได้ในหน่วยความจำ คุณจะไม่เห็นสิ่งนี้เกิดขึ้น แต่ควรรู้ว่าเกิดอะไรขึ้นเบื้องหลัง


0

.NET Framework ใช้ CLR Environment เพื่อสร้าง MSIL (Microsoft Intermediate Language) หรือที่เรียกว่า IL คอมไพเลอร์อ่านซอร์สโค้ดของคุณและเมื่อคุณสร้าง / คอมไพล์โครงการของคุณมันจะสร้าง MSIL ตอนนี้เมื่อคุณเรียกใช้โปรเจ็กต์ของคุณในที่สุด. NET JIT ( Just-in-time Compiler ) ก็เริ่มทำงาน JIT อ่านโค้ด MSIL ของคุณและสร้างโค้ดเนทีฟ (ซึ่งเป็นคำสั่ง x86) ซึ่ง CPU สามารถเรียกใช้งานได้อย่างง่ายดาย JIT อ่านคำสั่ง MSIL ทั้งหมดและดำเนินการทีละบรรทัด

หากคุณสนใจที่จะดูสิ่งที่เกิดขึ้นเบื้องหลังมีคำตอบแล้ว โปรดติดตาม - ที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.