การปรับปรุง ART ใน Android 8.0

Android Runtime (ART) ได้รับการปรับปรุงอย่างมากใน Android 8.0 เวอร์ชันที่เปิดตัว รายการด้านล่างสรุปการเพิ่มประสิทธิภาพที่ผู้ผลิตอุปกรณ์จะได้รับ ใน ART

ตัวเก็บขยะแบบบีบอัดพร้อมกัน

ตามที่ได้ประกาศไปในงาน Google I/O ว่า ART มีตัวรวบรวมขยะ (GC) แบบพร้อมกันขนาดกะทัดรัดใหม่ใน Android 8.0 ตัวรวบรวมนี้จะบีบอัดฮีปทุกครั้งที่ GC ทำงานและขณะที่แอปทำงาน โดยจะหยุดชั่วคราวเพียงเล็กน้อยเพื่อประมวลผล รูทของเธรด ประโยชน์ของฟีเจอร์นี้มีดังนี้

  • GC จะบีบอัดฮีปเสมอ โดยมีขนาดฮีปเล็กลงโดยเฉลี่ย 32% เมื่อเทียบกับ Android 7.0
  • การบีบอัดช่วยให้การจัดสรรออบเจ็กต์ตัวชี้การเพิ่มแบบเฉพาะเธรดเป็นไปได้เร็วขึ้น โดยการจัดสรรจะเร็วขึ้น 70% เมื่อเทียบกับใน Android 7.0
  • มีเวลาหยุดชั่วคราวที่สั้นลง 85% สำหรับการทดสอบ H2 เมื่อเทียบกับ GC ของ Android 7.0
  • เวลาหยุดชั่วคราวจะไม่ปรับขนาดตามขนาดฮีปอีกต่อไป แอปควรใช้ฮีปขนาดใหญ่ได้โดยไม่ต้องกังวลเรื่องความกระตุก
  • รายละเอียดการใช้งาน GC - Read barriers
    • Read barriers คือปริมาณงานเล็กๆ ที่ทำสำหรับการอ่านฟิลด์ของแต่ละออบเจ็กต์
    • ซึ่งได้รับการเพิ่มประสิทธิภาพในคอมไพเลอร์ แต่อาจทำให้บางกรณีการใช้งานช้าลง

การเพิ่มประสิทธิภาพลูป

ART ใช้การเพิ่มประสิทธิภาพลูปที่หลากหลายใน Android 8.0 รุ่นที่เผยแพร่

  • การกำจัดการตรวจสอบขอบเขต
    • แบบคงที่: ช่วงได้รับการพิสูจน์แล้วว่าอยู่ภายในขอบเขตที่เวลาคอมไพล์
    • ไดนามิก: การทดสอบขณะรันไทม์ช่วยให้มั่นใจว่าลูปจะอยู่ในขอบเขต (ยกเลิกการเพิ่มประสิทธิภาพหากไม่เป็นเช่นนั้น)
  • การกำจัดตัวแปรการเหนี่ยวนำ
    • นำการเหนี่ยวนำที่หยุดทำงานออก
    • แทนที่การเหนี่ยวนำที่ใช้หลังจากลูปเท่านั้นด้วยนิพจน์รูปแบบปิด
  • การกำจัดโค้ดที่ไม่ได้ใช้ภายในส่วนเนื้อหาของลูป การนำลูปทั้งหมดที่ ไม่ได้ใช้แล้วออก
  • การลดความแข็งแรง
  • การแปลงลูป: การกลับด้าน การสลับ การแยก การคลาย การแปลงแบบยูนิโมดูลาร์ ฯลฯ
  • SIMDization (หรือที่เรียกว่า Vectorization)

เครื่องมือเพิ่มประสิทธิภาพลูปจะอยู่ในการเพิ่มประสิทธิภาพของตัวเองในคอมไพเลอร์ ART การเพิ่มประสิทธิภาพลูปส่วนใหญ่จะคล้ายกับการเพิ่มประสิทธิภาพและการลดความซับซ้อน ในที่อื่นๆ ความท้าทายเกิดขึ้นกับการเพิ่มประสิทธิภาพบางอย่างที่เขียน CFG ใหม่ในลักษณะที่ซับซ้อนกว่าปกติ เนื่องจากเครื่องมือ CFG ส่วนใหญ่ (ดู nodes.h) มุ่งเน้นที่การสร้าง CFG ไม่ใช่การเขียนใหม่

การวิเคราะห์ลำดับชั้นของชั้นเรียน

ART ใน Android 8.0 ใช้การวิเคราะห์ลำดับชั้นของคลาส (CHA) ซึ่งเป็นการเพิ่มประสิทธิภาพคอมไพเลอร์ ที่เปลี่ยนการเรียกเสมือนเป็นการเรียกโดยตรงตามข้อมูล ที่สร้างขึ้นจากการวิเคราะห์ลำดับชั้นของคลาส การเรียกเสมือนมีค่าใช้จ่ายสูงเนื่องจาก มีการใช้งานการค้นหา vtable และต้องมีการโหลดที่ขึ้นต่อกัน 2 รายการ นอกจากนี้ การโทรเสมือนยังไม่สามารถแทรกในบรรทัดได้

สรุปการปรับปรุงที่เกี่ยวข้องมีดังนี้

  • การอัปเดตสถานะของวิธีการติดตั้งใช้งานแบบครั้งเดียวแบบไดนามิก - เมื่อสิ้นสุดเวลาลิงก์ของคลาส เมื่อมีการป้อนข้อมูลลงใน vtable แล้ว ART จะทำการเปรียบเทียบรายการต่อรายการ กับ vtable ของคลาสซูเปอร์
  • การเพิ่มประสิทธิภาพคอมไพเลอร์ - คอมไพเลอร์จะใช้ประโยชน์จาก ข้อมูลการติดตั้งใช้งานเดียวของเมธอด หากเมธอด A.foo มี ตั้งค่าแฟล็กการใช้งานเดียว คอมไพเลอร์จะเปลี่ยนการเรียกเสมือน เป็นการเรียกโดยตรง และพยายามแทรกการเรียกโดยตรงเป็นผลลัพธ์เพิ่มเติม
  • การลบล้างโค้ดที่คอมไพล์แล้ว - เมื่อสิ้นสุดเวลาการลิงก์ชั้นเรียนด้วยเช่นกัน เมื่อมีการอัปเดตข้อมูลการใช้งานแบบเดี่ยว หากเมธอด A.foo ที่ก่อนหน้านี้มีการใช้งานแบบเดี่ยว แต่ตอนนี้สถานะดังกล่าวใช้ไม่ได้แล้ว โค้ดที่คอมไพล์แล้วทั้งหมดซึ่งขึ้นอยู่กับสมมติฐานที่ว่าเมธอด A.foo มีการใช้งานแบบเดี่ยวจะต้องลบล้างโค้ดที่คอมไพล์แล้ว
  • การเพิ่มประสิทธิภาพ - สำหรับโค้ดที่คอมไพล์แบบสดซึ่งอยู่ในสแต็ก ระบบจะเริ่ม การเพิ่มประสิทธิภาพเพื่อบังคับให้โค้ดที่คอมไพล์แล้วซึ่งไม่ถูกต้องเข้าสู่โหมดอินเทอร์พรีเตอร์ เพื่อรับประกันความถูกต้อง ระบบจะใช้กลไกการเพิ่มประสิทธิภาพใหม่ซึ่งเป็นการผสมผสานระหว่าง การเพิ่มประสิทธิภาพแบบพร้อมกันและแบบไม่พร้อมกัน

แคชแบบอินไลน์ในไฟล์ .oat

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

Dexlayout

Dexlayout เป็นไลบรารีที่เปิดตัวใน Android 8.0 เพื่อวิเคราะห์ไฟล์ dex และ จัดเรียงใหม่ตามโปรไฟล์ Dexlayout มีเป้าหมายที่จะใช้ข้อมูลการสร้างโปรไฟล์รันไทม์ เพื่อจัดเรียงส่วนต่างๆ ของไฟล์ DEX ใหม่ในระหว่างการบำรุงรักษาขณะไม่มีการใช้งาน การคอมไพล์ในอุปกรณ์ การจัดกลุ่มส่วนต่างๆ ของไฟล์ dex ที่มักมีการเข้าถึงร่วมกันจะช่วยให้โปรแกรมมีรูปแบบการเข้าถึงหน่วยความจำที่ดีขึ้นจากการปรับปรุงตำแหน่ง ซึ่งจะช่วยประหยัด RAM และลดเวลาเริ่มต้น

เนื่องจากขณะนี้ข้อมูลโปรไฟล์จะพร้อมใช้งานหลังจากที่แอปทำงานแล้วเท่านั้น จึงมีการผสานรวม dexlayout ไว้ในการคอมไพล์ในอุปกรณ์ของ dex2oat ระหว่างการบำรุงรักษาขณะที่ไม่มีการใช้งาน

การนำแคช Dex ออก

ใน Android 7.0 และเวอร์ชันก่อนหน้า ออบเจ็กต์ DexCache มีอาร์เรย์ขนาดใหญ่ 4 รายการ ซึ่งสอดคล้องกับ จำนวนองค์ประกอบบางอย่างใน DexFile ได้แก่

  • สตริง (การอ้างอิง 1 รายการต่อ DexFile::StringId)
  • types (การอ้างอิง 1 รายการต่อ DexFile::TypeId)
  • methods (ตัวชี้ดั้งเดิม 1 รายการต่อ DexFile::MethodId)
  • ฟิลด์ (ตัวชี้เนทีฟ 1 ตัวต่อ DexFile::FieldId)

อาร์เรย์เหล่านี้ใช้สำหรับการดึงข้อมูลออบเจ็กต์ที่เราแก้ไขก่อนหน้านี้อย่างรวดเร็ว ใน Android 8.0 ระบบได้นำอาร์เรย์ทั้งหมดออกแล้ว ยกเว้นอาร์เรย์เมธอด

ประสิทธิภาพของล่าม

ประสิทธิภาพของอินเทอร์พรีเตอร์ได้รับการปรับปรุงอย่างมากในรุ่น Android 7.0 ด้วยการเปิดตัว "mterp" ซึ่งเป็นอินเทอร์พรีเตอร์ที่มีกลไกการดึงข้อมูล/ถอดรหัส/ตีความหลักที่เขียนด้วยภาษาแอสเซมบลี Mterp สร้างขึ้นตามอินเทอร์พรีเตอร์ Dalvik ที่รวดเร็ว และรองรับ arm, arm64, x86, x86_64, mips และ mips64 สำหรับโค้ดการคำนวณ mterp ของ Art จะเทียบได้กับ อินเทอร์พรีเตอร์แบบเร็วของ Dalvik โดยประมาณ อย่างไรก็ตาม ในบางสถานการณ์ ความเร็วอาจช้าลงอย่างมาก

  1. เรียกใช้ประสิทธิภาพ
  2. การจัดการสตริงและผู้ใช้รายอื่นๆ ที่ใช้เมธอดที่ระบบรู้จักว่าเป็น Intrinsic ใน Dalvik อย่างหนัก
  3. การใช้งานหน่วยความจำสแต็กสูงขึ้น

Android 8.0 แก้ไขปัญหาเหล่านี้

การแทรกเพิ่มเติม

ตั้งแต่ Android 6.0 เป็นต้นมา ART สามารถแทรกการเรียกใดก็ได้ภายในไฟล์ dex เดียวกัน แต่จะแทรกได้เฉพาะเมธอดใบจากไฟล์ dex อื่นๆ เท่านั้น ข้อจำกัดนี้มีสาเหตุ 2 ประการ ดังนี้

  1. การแทรกจากไฟล์ DEX อื่นต้องใช้แคช DEX ของไฟล์ DEX อื่นนั้น ซึ่งต่างจากการแทรกไฟล์ DEX เดียวกันที่สามารถใช้แคช DEX ของผู้เรียกซ้ำได้ โค้ดที่คอมไพล์แล้วต้องใช้แคช dex สำหรับคำสั่งบางอย่าง เช่น การเรียกแบบคงที่ การโหลดสตริง หรือการโหลดคลาส
  2. แผนที่สแต็กจะเข้ารหัสเฉพาะดัชนีเมธอดภายในไฟล์ DEX ปัจจุบัน

Android 8.0 มีการเปลี่ยนแปลงต่อไปนี้เพื่อแก้ไขข้อจำกัดเหล่านี้

  1. นำสิทธิ์เข้าถึงแคช Dex ออกจากโค้ดที่คอมไพล์แล้ว (ดูส่วน "การนำแคช Dex ออก" ด้วย)
  2. ขยายการเข้ารหัสแผนที่สแต็ก

การปรับปรุงการซิงค์

ทีม ART ได้ปรับเส้นทางโค้ด MonitorEnter/MonitorExit และลดการพึ่งพาการแผงกั้นหน่วยความจำแบบเดิมใน ARMv8 โดยแทนที่ด้วยคำสั่งใหม่กว่า (acquire/release) เมื่อเป็นไปได้

วิธีการเนทีฟที่เร็วกว่า

การเรียกเนทีฟที่เร็วขึ้นไปยัง Java Native Interface (JNI) พร้อมใช้งานแล้วโดยใช้ คำอธิบายประกอบ @FastNative และ @CriticalNative การเพิ่มประสิทธิภาพรันไทม์ ART ในตัวเหล่านี้ จะช่วยเพิ่มความเร็วในการเปลี่ยน JNI และแทนที่สัญกรณ์ !bang JNI ที่เลิกใช้งานแล้ว คำอธิบายประกอบจะไม่มีผลกับเมธอดที่ไม่ใช่เนทีฟ และใช้ได้เฉพาะกับโค้ดภาษา Java ของแพลตฟอร์มใน bootclasspath (ไม่มีการอัปเดต Play Store)

คำอธิบายประกอบ @FastNative รองรับเมธอดที่ไม่ใช่แบบคงที่ ใช้คำนี้ หากเมธอดเข้าถึง jobject เป็นพารามิเตอร์หรือค่าที่ส่งคืน

คำอธิบายประกอบ @CriticalNative ช่วยให้คุณเรียกใช้เมธอดเนทีฟได้รวดเร็วยิ่งขึ้น โดยมีข้อจำกัดดังนี้

  • เมธอดต้องเป็นแบบคงที่ โดยไม่มีออบเจ็กต์สำหรับพารามิเตอร์ ค่าที่ส่งคืน หรือthisโดยนัย
  • ระบบจะส่งเฉพาะประเภทดั้งเดิมไปยังเมธอดดั้งเดิม
  • เมธอดเนทีฟไม่ได้ใช้พารามิเตอร์ JNIEnv และ jclass ในคำจำกัดความของฟังก์ชัน
  • ต้องลงทะเบียนเมธอดกับ RegisterNatives แทนการ ใช้การลิงก์ JNI แบบไดนามิก

@FastNative ช่วยปรับปรุงประสิทธิภาพของเมธอดเนทีฟได้สูงสุด 3 เท่า และ @CriticalNative ได้สูงสุด 5 เท่า เช่น การเปลี่ยน JNI ที่วัดได้ ในอุปกรณ์ Nexus 6P

การเรียกใช้ Java Native Interface (JNI) เวลาดำเนินการ (หน่วยเป็นนาโนวินาที)
JNI ปกติ 115
!bang JNI 60
@FastNative 35
@CriticalNative 25