Các điểm cải tiến về ART trên Android 8.0

Môi trường thời gian chạy Android (ART) đã được cải thiện đáng kể trong bản phát hành Android 8.0. Danh sách dưới đây tóm tắt các tính năng nâng cao mà nhà sản xuất thiết bị có thể mong đợi trong ART.

Trình thu gom rác nén đồng thời

Như đã công bố tại Google I/O, ART có một trình thu gom rác (GC) nén đồng thời mới trong Android 8.0. Bộ thu gom này nén vùng nhớ khối xếp mỗi khi GC chạy và trong khi ứng dụng đang chạy, chỉ tạm dừng một lần ngắn để xử lý gốc luồng. Dưới đây là các lợi ích của tính năng này:

  • GC luôn nén vùng nhớ khối xếp: kích thước vùng nhớ khối xếp trung bình nhỏ hơn 32% so với Android 7.0.
  • Tính năng nén cho phép phân bổ đối tượng con trỏ cục bộ của luồng: Các lượt phân bổ nhanh hơn 70% so với trong Android 7.0.
  • Giảm 85% thời gian tạm dừng cho điểm chuẩn H2 so với GC Android 7.0.
  • Thời gian tạm dừng không còn tỷ lệ với kích thước vùng nhớ khối xếp; các ứng dụng có thể sử dụng vùng nhớ khối xếp lớn mà không lo bị giật.
  • Thông tin chi tiết về việc triển khai GC – Các rào cản đọc:
    • Rào cản đọc là một lượng nhỏ công việc được thực hiện cho mỗi lần đọc trường đối tượng.
    • Các hàm này được tối ưu hoá trong trình biên dịch, nhưng có thể làm chậm một số trường hợp sử dụng.

Tối ưu hoá vòng lặp

ART sử dụng nhiều phương pháp tối ưu hoá vòng lặp trong bản phát hành Android 8.0:

  • Loại bỏ việc kiểm tra giới hạn
    • Tĩnh: các dải được chứng minh là nằm trong giới hạn tại thời điểm biên dịch
    • Động: kiểm thử thời gian chạy đảm bảo các vòng lặp nằm trong giới hạn (nếu không, hãy tắt tính năng này)
  • Loại bỏ biến quy nạp
    • Xoá tính năng cảm ứng không hoạt động
    • Thay thế phương pháp quy nạp chỉ được sử dụng sau vòng lặp bằng biểu thức dạng đóng
  • Loại bỏ mã chết bên trong phần nội dung của vòng lặp, xoá toàn bộ vòng lặp đã chết
  • Giảm độ mạnh
  • Biến đổi vòng lặp: đảo ngược, hoán đổi, phân tách, mở rộng, đơn mô-đun, v.v.
  • SIMD hoá (còn gọi là vectơ hoá)

Trình tối ưu hoá vòng lặp nằm trong lượt tối ưu hoá riêng trong trình biên dịch ART. Hầu hết các hoạt động tối ưu hoá vòng lặp đều tương tự như các hoạt động tối ưu hoá và đơn giản hoá ở nơi khác. Một số phương pháp tối ưu hoá sẽ gây ra thách thức khi viết lại CFG theo cách phức tạp hơn bình thường, vì hầu hết các tiện ích CFG (xem nodes.h) tập trung vào việc tạo CFG, chứ không phải viết lại CFG.

Phân tích hệ phân cấp lớp

ART trong Android 8.0 sử dụng tính năng Phân tích hệ phân cấp lớp (CHA), một tính năng tối ưu hoá trình biên dịch giúp loại bỏ các lệnh gọi ảo thành lệnh gọi trực tiếp dựa trên thông tin được tạo bằng cách phân tích hệ phân cấp lớp. Lệnh gọi ảo tốn kém vì chúng được triển khai xung quanh một lệnh tra cứu vtable và thực hiện một số lượt tải phụ thuộc. Ngoài ra, bạn không thể đưa các lệnh gọi ảo vào cùng dòng.

Dưới đây là bản tóm tắt các điểm cải tiến liên quan:

  • Cập nhật trạng thái phương thức triển khai đơn động – Khi kết thúc thời gian liên kết lớp, khi vtable đã được điền sẵn, ART sẽ tiến hành so sánh từng mục với vtable của lớp cha.
  • Tối ưu hoá trình biên dịch – Trình biên dịch sẽ tận dụng thông tin triển khai một lần của một phương thức. Nếu một phương thức A.foo đã đặt cờ triển khai đơn, trình biên dịch sẽ huỷ ảo hoá lệnh gọi ảo thành lệnh gọi trực tiếp và cố gắng đưa lệnh gọi trực tiếp vào cùng dòng.
  • Mã đã biên dịch không hợp lệ – Cũng vào cuối thời gian liên kết lớp khi thông tin triển khai đơn được cập nhật, nếu phương thức A.foo trước đây đã triển khai đơn nhưng trạng thái đó hiện không hợp lệ, thì tất cả mã đã biên dịch phụ thuộc vào giả định rằng phương thức A.foo có phương thức triển khai đơn cần phải có mã đã biên dịch không hợp lệ.
  • Huỷ tối ưu hoá – Đối với mã đã biên dịch trực tiếp nằm trên ngăn xếp, quá trình huỷ tối ưu hoá sẽ được bắt đầu để buộc mã đã biên dịch không hợp lệ chuyển sang chế độ trình thông dịch nhằm đảm bảo tính chính xác. Một cơ chế mới của việc loại bỏ tối ưu hoá, là sự kết hợp của việc loại bỏ tối ưu hoá đồng bộ và không đồng bộ sẽ được sử dụng.

Bộ nhớ đệm nội tuyến trong tệp .oat

ART hiện sử dụng bộ nhớ đệm nội tuyến và tối ưu hoá các vị trí gọi có đủ dữ liệu. Tính năng bộ nhớ đệm nội tuyến ghi lại thông tin thời gian chạy bổ sung vào hồ sơ và sử dụng thông tin đó để thêm các tuỳ chọn tối ưu hoá động vào quá trình biên dịch trước.

Dexlayout

Dexlayout là một thư viện được giới thiệu trong Android 8.0 để phân tích các tệp dex và sắp xếp lại các tệp đó theo một hồ sơ. Dexlayout nhằm sử dụng thông tin phân tích thời gian chạy để sắp xếp lại các phần của tệp dex trong quá trình biên dịch bảo trì ở trạng thái rảnh trên thiết bị. Bằng cách nhóm các phần của tệp dex thường được truy cập cùng nhau, các chương trình có thể có các mẫu truy cập bộ nhớ tốt hơn nhờ cải thiện tính cục bộ, tiết kiệm RAM và rút ngắn thời gian khởi động.

Vì thông tin hồ sơ hiện chỉ có sau khi các ứng dụng đã chạy, nên dexlayout được tích hợp trong quá trình biên dịch trên thiết bị của dex2oat trong quá trình bảo trì khi ở trạng thái rảnh.

Xoá bộ nhớ đệm DEX

Cho đến Android 7.0, đối tượng DexCache sở hữu 4 mảng lớn, tương ứng với số lượng các phần tử nhất định trong DexFile, cụ thể là:

  • chuỗi (một tệp tham chiếu cho mỗi DexFile::StringId),
  • loại (một tệp tham chiếu cho mỗi DexFile::TypeId),
  • phương thức (một con trỏ gốc cho mỗi DexFile::MethodId),
  • trường (một con trỏ gốc cho mỗi DexFile::FieldId).

Các mảng này được dùng để truy xuất nhanh các đối tượng mà chúng ta đã giải quyết trước đó. Trong Android 8.0, tất cả các mảng đã bị xoá ngoại trừ mảng phương thức.

Hiệu suất của trình thông dịch

Hiệu suất của trình thông dịch được cải thiện đáng kể trong bản phát hành Android 7.0 nhờ việc ra mắt "mterp" – một trình thông dịch có cơ chế tìm nạp/giải mã/diễn giải cốt lõi được viết bằng ngôn ngữ tập hợp. Mterp được mô hình hoá theo trình thông dịch Dalvik nhanh và hỗ trợ arm, arm64, x86, x86_64, mips và mips64. Đối với mã tính toán, mterp của Art gần tương đương với trình thông dịch nhanh của Dalvik. Tuy nhiên, trong một số trường hợp, tốc độ có thể chậm hơn đáng kể – thậm chí là đáng kể:

  1. Gọi hiệu suất.
  2. Thao tác với chuỗi và các phương thức sử dụng nhiều phương thức khác được nhận dạng là nội tại trong Dalvik.
  3. Mức sử dụng bộ nhớ ngăn xếp cao hơn.

Android 8.0 giải quyết các vấn đề này.

Thêm nội dung nội tuyến

Kể từ Android 6.0, ART có thể cùng dòng bất kỳ lệnh gọi nào trong cùng một tệp dex, nhưng chỉ có thể cùng dòng các phương thức lá từ các tệp dex khác nhau. Có hai lý do dẫn đến giới hạn này:

  1. Việc nội tuyến từ một tệp dex khác yêu cầu sử dụng bộ nhớ đệm dex của tệp dex khác đó, không giống như việc nội tuyến cùng một tệp dex, chỉ có thể sử dụng lại bộ nhớ đệm dex của phương thức gọi. Bạn cần có bộ nhớ đệm dex trong mã đã biên dịch cho một số lệnh như lệnh gọi tĩnh, tải chuỗi hoặc tải lớp.
  2. Bản đồ ngăn xếp chỉ mã hoá chỉ mục phương thức trong tệp dex hiện tại.

Để giải quyết những hạn chế này, Android 8.0:

  1. Xoá quyền truy cập vào bộ nhớ đệm dex khỏi mã đã biên dịch (cũng xem phần "Xoá bộ nhớ đệm dex")
  2. Mở rộng mã hoá bản đồ ngăn xếp.

Cải tiến về tính năng đồng bộ hoá

Nhóm ART đã điều chỉnh các đường dẫn mã MonitorEnter/MonitorExit và giảm mức độ phụ thuộc vào các rào cản bộ nhớ truyền thống trên ARMv8, thay thế các rào cản này bằng các lệnh (thu nạp/giải phóng) mới hơn nếu có thể.

Các phương thức gốc nhanh hơn

Bạn có thể sử dụng chú thích @FastNative@CriticalNative để thực hiện các lệnh gọi gốc nhanh hơn đến Giao diện gốc Java (JNI). Các tính năng tối ưu hoá thời gian chạy ART tích hợp sẵn này giúp tăng tốc quá trình chuyển đổi JNI và thay thế ký hiệu !bang JNI hiện không còn được dùng nữa. Các chú giải này không ảnh hưởng đến các phương thức không phải gốc và chỉ dành cho mã Ngôn ngữ Java của nền tảng trên bootclasspath (không có bản cập nhật Cửa hàng Play).

Chú thích @FastNative hỗ trợ các phương thức không tĩnh. Sử dụng phương thức này nếu một phương thức truy cập vào jobject dưới dạng tham số hoặc giá trị trả về.

Chú thích @CriticalNative cung cấp một cách nhanh hơn nữa để chạy các phương thức gốc, với các hạn chế sau:

  • Phương thức phải tĩnh – không có đối tượng nào cho tham số, giá trị trả về hoặc this ngầm ẩn.
  • Chỉ các loại nguyên hàm mới được truyền đến phương thức gốc.
  • Phương thức gốc không sử dụng các tham số JNIEnvjclass trong phần khai báo hàm.
  • Bạn phải đăng ký phương thức này với RegisterNatives thay vì dựa vào tính năng liên kết JNI động.

@FastNative có thể cải thiện hiệu suất của phương thức gốc lên đến 3 lần và @CriticalNative lên đến 5 lần. Ví dụ: một quá trình chuyển đổi JNI được đo lường trên thiết bị Nexus 6P:

Lệnh gọi Giao diện gốc Java (JNI) Thời gian thực thi (tính bằng nano giây)
JNI thông thường 115
!bang JNI 60
@FastNative 35
@CriticalNative 25