Java Virtual Machine - คอมไพเลอร์ JIT
ในบทนี้เราจะเรียนรู้เกี่ยวกับคอมไพเลอร์ JIT และความแตกต่างระหว่างภาษาที่คอมไพล์และตีความ
เรียบเรียงเทียบกับภาษาที่ตีความ
ภาษาเช่น C, C ++ และ FORTRAN เป็นภาษาที่คอมไพล์ รหัสของพวกเขาจะถูกส่งเป็นรหัสไบนารีที่กำหนดเป้าหมายไปที่เครื่องต้นแบบ ซึ่งหมายความว่าโค้ดระดับสูงจะถูกคอมไพล์เป็นรหัสไบนารีพร้อมกันโดยคอมไพเลอร์แบบสแตติกที่เขียนขึ้นโดยเฉพาะสำหรับสถาปัตยกรรมพื้นฐาน ไบนารีที่สร้างขึ้นจะไม่ทำงานบนสถาปัตยกรรมอื่นใด
ในทางกลับกันภาษาที่ตีความเช่น Python และ Perl สามารถทำงานบนเครื่องใดก็ได้ตราบเท่าที่มีล่ามที่ถูกต้อง มันไปทีละบรรทัดเหนือโค้ดระดับสูงโดยแปลงเป็นรหัสไบนารี
โดยทั่วไปโค้ดที่ตีความจะช้ากว่าโค้ดที่คอมไพล์ ตัวอย่างเช่นพิจารณาการวนซ้ำ การตีความจะแปลงรหัสที่สอดคล้องกันสำหรับการวนซ้ำแต่ละครั้ง ในทางกลับกันโค้ดที่คอมไพล์แล้วจะทำให้การแปลเป็นเพียงแบบเดียว นอกจากนี้เนื่องจากล่ามมองเห็นเพียงบรรทัดเดียวในแต่ละครั้งพวกเขาจึงไม่สามารถดำเนินการโค้ดที่สำคัญใด ๆ เช่นการเปลี่ยนลำดับการดำเนินการของคำสั่งเช่นคอมไพเลอร์
เราจะดูตัวอย่างของการเพิ่มประสิทธิภาพดังกล่าวด้านล่าง -
Adding two numbers stored in memory. เนื่องจากการเข้าถึงหน่วยความจำสามารถใช้ CPU ได้หลายรอบคอมไพเลอร์ที่ดีจะออกคำแนะนำในการดึงข้อมูลจากหน่วยความจำและดำเนินการเพิ่มเติมเมื่อมีข้อมูลเท่านั้น จะไม่รอและในระหว่างนี้ให้ดำเนินการตามคำแนะนำอื่น ๆ ในทางกลับกันจะไม่มีการเพิ่มประสิทธิภาพดังกล่าวในระหว่างการตีความเนื่องจากล่ามไม่ทราบรหัสทั้งหมดในช่วงเวลาใดเวลาหนึ่ง
แต่ภาษาที่ตีความสามารถทำงานบนเครื่องใดก็ได้ที่มีล่ามที่ถูกต้องของภาษานั้น
Java คอมไพล์หรือตีความ?
ชวาพยายามหาพื้นกลาง เนื่องจาก JVM อยู่ระหว่างคอมไพเลอร์ javac และฮาร์ดแวร์พื้นฐานคอมไพเลอร์ javac (หรือคอมไพเลอร์อื่น ๆ ) จึงรวบรวมโค้ด Java ใน Bytecode ซึ่งเข้าใจโดย JVM เฉพาะแพลตฟอร์ม จากนั้น JVM จะรวบรวม Bytecode ในรูปแบบไบนารีโดยใช้การคอมไพล์ JIT (Just-in-time) ตามที่โค้ดดำเนินการ
ฮอตสปอต
ในโปรแกรมทั่วไปมีโค้ดเพียงส่วนเล็ก ๆ ที่ถูกเรียกใช้งานบ่อยครั้งและบ่อยครั้งที่โค้ดนี้มีผลต่อประสิทธิภาพของแอปพลิเคชันทั้งหมดอย่างมาก ส่วนของโค้ดดังกล่าวเรียกว่าHotSpots.
หากบางส่วนของโค้ดถูกเรียกใช้งานเพียงครั้งเดียวการคอมไพล์จะเป็นการเสียความพยายามและการตีความ Bytecode จะเร็วกว่า แต่ถ้าส่วนนั้นเป็นส่วนที่ร้อนและมีการดำเนินการหลายครั้ง JVM จะคอมไพล์แทน ตัวอย่างเช่นหากมีการเรียกเมธอดหลายครั้งรอบพิเศษที่จะต้องใช้ในการคอมไพล์โค้ดจะถูกหักล้างด้วยไบนารีที่เร็วขึ้นที่สร้างขึ้น
ยิ่งไปกว่านั้นยิ่ง JVM รันเมธอดเฉพาะหรือลูปมากเท่าไหร่ข้อมูลก็จะรวบรวมเพื่อทำการปรับให้เหมาะสมมากขึ้นเพื่อให้สร้างไบนารีได้เร็วขึ้น
ให้เราพิจารณารหัสต่อไปนี้ -
for(int i = 0 ; I <= 100; i++) {
System.out.println(obj1.equals(obj2)); //two objects
}
หากตีความรหัสนี้ล่ามจะอนุมานสำหรับการวนซ้ำแต่ละครั้งที่คลาสของ obj1 เนื่องจากแต่ละคลาสใน Java มีเมธอด .equals () ซึ่งขยายจากคลาส Object และสามารถแทนที่ได้ ดังนั้นแม้ว่า obj1 จะเป็นสตริงสำหรับการวนซ้ำแต่ละครั้งการหักจะยังคงทำได้
ในทางกลับกันสิ่งที่จะเกิดขึ้นจริงคือ JVM จะสังเกตเห็นว่าสำหรับการวนซ้ำแต่ละครั้ง obj1 เป็นของคลาส String ดังนั้นจึงสร้างโค้ดที่สอดคล้องกับเมธอด .equals () ของคลาส String โดยตรง ดังนั้นจึงไม่จำเป็นต้องมีการค้นหาและโค้ดที่คอมไพล์แล้วจะทำงานได้เร็วขึ้น
พฤติกรรมแบบนี้จะเกิดขึ้นได้ก็ต่อเมื่อ JVM รู้ว่าโค้ดทำงานอย่างไร ดังนั้นจึงต้องรอก่อนที่จะรวบรวมบางส่วนของโค้ด
ด้านล่างนี้เป็นอีกตัวอย่างหนึ่ง -
int sum = 7;
for(int i = 0 ; i <= 100; i++) {
sum += i;
}
ล่ามสำหรับแต่ละลูปจะดึงค่าของ 'sum' จากหน่วยความจำเพิ่ม 'I' เข้าไปและเก็บกลับเข้าไปในหน่วยความจำ การเข้าถึงหน่วยความจำเป็นการดำเนินการที่มีราคาแพงและโดยปกติจะใช้ CPU หลายรอบ เนื่องจากโค้ดนี้ทำงานหลายครั้งจึงเป็น HotSpot JIT จะรวบรวมโค้ดนี้และทำการเพิ่มประสิทธิภาพดังต่อไปนี้
สำเนา 'ผลรวม' ในเครื่องจะถูกเก็บไว้ในรีจิสเตอร์เฉพาะสำหรับเธรดเฉพาะ การดำเนินการทั้งหมดจะทำกับค่าในรีจิสเตอร์และเมื่อลูปเสร็จสิ้นค่าจะถูกเขียนกลับไปยังหน่วยความจำ
จะเกิดอะไรขึ้นถ้าเธรดอื่นกำลังเข้าถึงตัวแปรด้วย เนื่องจากการอัปเดตกำลังดำเนินการกับสำเนาภายในของตัวแปรโดยเธรดอื่นพวกเขาจะเห็นค่าเก่า จำเป็นต้องมีการซิงโครไนซ์เธรดในกรณีเช่นนี้ พื้นฐานของการซิงค์เบื้องต้นคือการประกาศ 'ผลรวม' เป็นค่าความผันผวน ตอนนี้ก่อนที่จะเข้าถึงตัวแปรเธรดจะล้างรีจิสเตอร์ในเครื่องและดึงค่าจากหน่วยความจำ หลังจากเข้าถึงแล้วค่าจะถูกเขียนลงในหน่วยความจำทันที
ด้านล่างนี้คือการเพิ่มประสิทธิภาพทั่วไปบางส่วนที่ทำโดยคอมไพเลอร์ JIT -
- วิธีการ inlining
- การกำจัดรหัสตาย
- Heuristics สำหรับการเพิ่มประสิทธิภาพไซต์การโทร
- พับคงที่