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 những điểm cải tiến mà nhà sản xuất thiết bị có thể thấy trong ART.
Trình thu gom rác nén đồng thời
Như đã công bố tại hội nghị 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 này nén heap mỗi khi GC chạy và trong khi ứng dụng đang chạy, chỉ có một khoảng tạm dừng ngắn để xử lý các gốc của luồng. Sau đây là những 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 nhỏ hơn 32% so với Android 7.0.
- Việc nén cho phép phân bổ đối tượng con trỏ tăng cục bộ theo luồng: Việc phân bổ nhanh hơn 70% so với trên 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ệ thuận với kích thước vùng nhớ khối xếp nữa; các ứng dụng có thể sử dụng vùng nhớ khối xếp lớn mà không phải lo lắng về tình trạng giật.
- Thông tin chi tiết về việc triển khai GC – 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 trường hợp 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 loại hoạt động tối ưu hoá vòng lặp trong bản phát hành Android 8.0:
- Loại bỏ kiểm tra ranh giới
- Tĩnh: các dải được chứng minh là nằm trong giới hạn tại thời gian biên dịch
- Động: các 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 huỷ tối ưu hoá)
- Loại bỏ biến cảm ứng
- Loại bỏ hiện tượng cảm ứng không hoạt động
- Thay thế phương pháp quy nạp chỉ được dùng sau vòng lặp bằng các biểu thức dạng đóng
- Loại bỏ mã không dùng đến bên trong phần nội dung vòng lặp, xoá toàn bộ vòng lặp không dùng đến
- Giảm sức mạnh
- Biến đổi vòng lặp: đảo ngược, hoán đổi, phân tách, mở, đơn phương thức, v.v.
- SIMD hoá (còn gọi là vectơ hoá)
Trình tối ưu hoá vòng lặp nằm trong quy trình 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á ở những nơi khác. Một số hoạt động tối ưu hoá sẽ 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) đều 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 Phân tích hệ thống phân cấp lớp (CHA), một phương pháp tối ưu hoá trình biên dịch giúp huỷ ảo hoá các lệnh gọi ảo thành lệnh gọi trực tiếp dựa trên thông tin do việc phân tích hệ thống phân cấp lớp tạo ra. Các lệnh gọi ảo tốn kém vì chúng được triển khai xung quanh một hoạt động tra cứu vtable và chúng mất một vài tải phụ thuộc. Ngoài ra, bạn không thể đặt các cuộc gọi ảo ở chế độ nội tuyến.
Sau đây là nội dung tóm tắt về các điểm cải tiến có liên quan:
- Cập nhật trạng thái phương thức triển khai duy nhất một cách linh động – Vào cuối 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 siêu lớp.
- 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 duy nhất của một phương thức. Nếu 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à tiếp tục tìm cách nội tuyến lệnh gọi trực tiếp.
- Việc làm mất hiệu lực mã đã biên dịch – Cũng vào cuối thời gian liên kết lớp khi thông tin về một lần triển khai được cập nhật, nếu phương thức A.foo trước đây có một lần triển khai 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ó một lần triển khai cần phải làm mất hiệu lực mã đã biên dịch.
- Huỷ tối ưu hoá – Đối với mã được 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ế huỷ tối ưu hoá mới (kết hợp giữa huỷ 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 bổ sung về thời gian chạy vào hồ sơ và sử dụng thông tin đó để thêm các hoạt động tối ưu hoá động vào quá trình biên dịch trước thời hạn.
Dexlayout
Dexlayout là một thư viện được ra mắt 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 hướng đến việc sử dụng thông tin lập hồ sơ 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ì ở chế độ chờ 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ó sẵn sau khi các ứng dụng đã chạy, dexlayout được tích hợp trong quá trình biên dịch trên thiết bị của dex2oat trong thời gian bảo trì khi thiết bị ở 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ỷ lệ thuận với số lượng một số phần tử trong DexFile, cụ thể là:
- các chuỗi (mỗi DexFile::StringId một giá trị tham chiếu),
- types (một tham chiếu cho mỗi DexFile::TypeId),
- phương thức (một con trỏ gốc cho mỗi DexFile::MethodId),
- các 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 đã phân giải trước đó. Trong Android 8.0, tất cả mảng đều đã bị xoá, ngoại trừ mảng phương thức.
Hiệu suất của phiên dịch viên
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 giới thiệu "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ữ hợp ngữ. 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 có thể so sánh 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 độ này có thể chậm hơn đáng kể, thậm chí là chậm hơn rất nhiều:
- Hiệu suất gọi.
- Thao tác chuỗi và những người dùng khác sử dụng nhiều phương thức được nhận dạng là các hàm nội tại trong Dalvik.
- Mức sử dụng bộ nhớ ngăn xếp cao hơn.
Android 8.0 giải quyết những vấn đề này.
Nội dung khác được chèn
Kể từ Android 6.0, ART có thể chèn bất kỳ lệnh gọi nào trong cùng một tệp dex, nhưng chỉ có thể chèn các phương thức lá từ các tệp dex khác nhau. Có 2 lý do dẫn đến hạn chế này:
- 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ộ nhớ đệm dex là cần thiết trong mã đã biên dịch cho một số hướng dẫn như lệnh gọi tĩnh, tải chuỗi hoặc tải lớp.
- Các bản đồ ngăn xếp chỉ mã hoá một 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:
- Xoá quyền truy cập vào bộ nhớ đệm dex khỏi mã đã biên dịch (xem thêm phần "Xoá bộ nhớ đệm dex")
- Mở rộng quá trình 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 sự phụ thuộc vào các rào cản bộ nhớ truyền thống trên ARMv8, thay thế chúng bằng các chỉ dẫn mới hơn (thu nhận/phát hành) nếu có thể.
Phương thức gốc nhanh hơn
Bạn có thể sử dụng các lệnh gọi gốc nhanh hơn đến Giao diện gốc Java (JNI) bằng chú thích @FastNative
và @CriticalNative
. Những hoạt động tối ưu hoá thời gian chạy ART tích hợp này giúp tăng tốc các quá trình chuyển đổi JNI và thay thế ký hiệu !bang JNI hiện không được dùng nữa. Các chú giải không ảnh hưởng đến các phương thức không phải phương thức gốc và chỉ có sẵn 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 chú giải 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ú giải @CriticalNative
cung cấp một cách chạy phương thức gốc nhanh hơn nữa, với những hạn chế sau:
-
Các phương thức phải là phương thức tĩnh – không có đối tượng cho các tham số, giá trị trả về hoặc
this
ngầm. - Chỉ các loại nguyên hàm được truyền đến phương thức gốc.
-
Phương thức gốc không dùng các tham số
JNIEnv
vàjclass
trong định nghĩa hàm. -
Phương thức này phải được đăng ký bằng
RegisterNatives
thay vì dựa vào việc 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 |