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

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

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

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

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

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

ART ใช้การเพิ่มประสิทธิภาพลูปที่หลากหลายใน Android เวอร์ชัน 8.0 ดังนี้

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

ตัวเพิ่มประสิทธิภาพลูปจะอยู่ในขั้นตอนการเพิ่มประสิทธิภาพของตัวเองในคอมไพเลอร์ 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)
  • ประเภท (การอ้างอิง 1 รายการต่อ DexFile::TypeId)
  • เมธอด (ตัวชี้เนทีฟ 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 อย่างไรก็ตาม ในบางสถานการณ์ mterp อาจช้ากว่าอย่างมาก ดังนี้

  1. ประสิทธิภาพการเรียกใช้
  2. การจัดการสตริงและผู้ใช้เมธอดรายอื่นๆ ที่ระบบรู้จักว่าเป็น ฟังก์ชันภายในใน 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 และลดการ พึ่งพา Read Barrier แบบเดิมใน 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