รันไทม์ 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 อาจช้ากว่าอย่างมาก ดังนี้
- ประสิทธิภาพการเรียกใช้
- การจัดการสตริงและผู้ใช้เมธอดรายอื่นๆ ที่ระบบรู้จักว่าเป็น ฟังก์ชันภายในใน Dalvik
- การใช้งานหน่วยความจำสแต็กสูงขึ้น
Android 8.0 แก้ไขปัญหาเหล่านี้แล้ว
การแทรกมากขึ้น
ตั้งแต่ Android 6.0 เป็นต้นมา ART สามารถแทรกการเรียกใดก็ได้ภายในไฟล์ Dex เดียวกัน แต่จะ แทรกได้เฉพาะเมธอดใบจากไฟล์ Dex อื่นๆ ข้อจำกัดนี้มีสาเหตุ 2 ประการ
- การแทรกจากไฟล์ Dex อื่นต้องใช้แคช Dex ของไฟล์ Dex อื่นนั้น ซึ่งแตกต่างจากการแทรกไฟล์ Dex เดียวกันที่สามารถใช้แคช Dex ของผู้เรียกซ้ำได้ โค้ดที่คอมไพล์แล้วจำเป็นต้องใช้แคช Dex สำหรับคำสั่งบางอย่าง เช่น การเรียกแบบคงที่ การโหลดสตริง หรือการโหลดคลาส
- แผนที่สแต็กจะเข้ารหัสเฉพาะดัชนีเมธอดภายในไฟล์ Dex ปัจจุบัน
Android 8.0 แก้ไขข้อจำกัดเหล่านี้โดยดำเนินการดังนี้
- นำการเข้าถึงแคช Dex ออกจากโค้ดที่คอมไพล์แล้ว (ดูส่วน "การนำแคช Dex ออก" ด้วย)
- ขยายการเข้ารหัสแผนที่สแต็ก
การปรับปรุงการซิงค์
ทีม 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 |