Ngày phát hành:
Android 12 (API cấp 31) – Performance Hint API
Android 13 (API cấp 33) – Trình quản lý gợi ý về hiệu suất trong API NDK
(Bản dùng thử) Android 15 (DP1) – reportActualWorkDuration()
Với các gợi ý về hiệu suất của CPU, trò chơi có thể can thiệp vào hành vi hiệu suất CPU động để phù hợp hơn với nhu cầu của trò chơi. Trên hầu hết thiết bị, Android tự động điều chỉnh tốc độ xung nhịp của CPU và loại nhân cho mức tải dựa trên nhu cầu trước đó. Nếu mức tải sử dụng nhiều tài nguyên CPU hơn, tốc độ xung nhịp sẽ tăng lên; và cuối cùng thì mức tải đó sẽ được chuyển sang nhân xử lý lớn hơn. Nếu mức tải sử dụng ít tài nguyên hơn, thì Android sẽ giảm mức phân bổ tài nguyên. Với ADPF, ứng dụng hoặc trò chơi có thể gửi thêm một tín hiệu về hiệu suất và thời hạn của ứng dụng hoặc trò chơi đó. Điều này giúp hệ thống tăng tốc mạnh mẽ hơn (cải thiện hiệu suất) và giảm tốc độ xung nhịp nhanh chóng khi khối lượng công việc hoàn tất (tiết kiệm điện năng).
Tốc độ xung nhịp
Khi thiết bị Android tự động điều chỉnh tốc độ xung nhịp CPU, tần số có thể thay đổi hiệu suất của mã. Việc thiết kế mã nhằm giải quyết tốc độ xung nhịp động là yếu tố quan trọng để tối đa hoá hiệu suất, duy trì trạng thái nhiệt an toàn và sử dụng điện năng hiệu quả. Bạn không thể trực tiếp chỉ định tần số của CPU trong mã của ứng dụng. Do đó, các ứng dụng thường cố gắng chạy ở tốc độ xung nhịp CPU cao thay vì chạy một vòng lặp liên tục trong một luồng ở chế độ nền, vì vậy mức tải có vẻ như đòi hỏi nhiều hơn. Đây là một phương pháp không hay vì nó làm lãng phí điện năng và tăng lượng nhiệt sinh ra trên thiết bị trong khi ứng dụng không thực sự sử dụng tài nguyên bổ sung. API PerformanceHint
của CPU được thiết kế để giải quyết vấn đề này. Bằng cách cho hệ thống biết thời lượng công việc thực tế và thời lượng công việc mục tiêu, Android sẽ có thể nắm được thông tin tổng quan về nhu cầu CPU của ứng dụng và phân bổ tài nguyên một cách hiệu quả. Điều này sẽ giúp đạt được hiệu suất tối ưu ở mức tiêu thụ điện năng hiệu quả.
Các loại nhân
Một yếu tố hiệu suất quan trọng khác là: loại nhân CPU mà trò chơi chạy trên đó. Các thiết bị Android thường thay đổi nhân CPU được chỉ định cho một luồng động dựa trên hành vi của mức tải gần đây. Việc chỉ định nhân CPU thậm chí còn phức tạp hơn trên các hệ thống SoC có nhiều loại nhân. Trên một số thiết bị, bạn chỉ có thể sử dụng các nhân xử lý lớn hơn trong thời gian ngắn trước khi thiết bị rơi vào trạng thái không ổn định về nhiệt.
Trò chơi của bạn không nên tìm cách chỉ định luồng công việc (CPU core affinity) cho các nhân CPU vì những lý do sau:
- Loại nhân phù hợp nhất cho một mức tải cụ thể còn tuỳ theo kiểu thiết bị.
- Độ ổn định khi chạy trên các nhân lớn hơn còn tuỳ theo hệ thống SoC và các giải pháp nhiệt do từng mẫu thiết bị cung cấp.
- Tác động của yếu tố môi trường đối với trạng thái nhiệt có thể khiến việc chọn nhân trở nên phức tạp hơn. Ví dụ: thời tiết hoặc ốp lưng điện thoại có thể thay đổi trạng thái nhiệt của thiết bị.
- Việc chọn nhân xử lý không thể đáp ứng các thiết bị mới với hiệu suất và năng lực xử lý nhiệt cao hơn. Do đó, các thiết bị thường bỏ qua việc chỉ định đối tượng tương đồng cho nhân xử lý của trò chơi.
Ví dụ về hành vi mặc định của trình lập lịch Linux

API PerformanceHint trừu tượng hoá nhiều độ trễ DVFS

- Nếu các tác vụ cần chạy trên một CPU cụ thể, thì PerformanceHint API sẽ biết cách đưa ra quyết định đó thay cho bạn.
- Do đó, bạn không cần sử dụng mối liên kết.
- Các thiết bị có nhiều cấu trúc liên kết; Đặc điểm về nguồn điện và nhiệt độ quá đa dạng để nhà phát triển ứng dụng có thể biết được.
- Bạn không thể đưa ra bất kỳ giả định nào về hệ thống cơ bản mà bạn đang chạy.
Giải pháp
ADPF cung cấp lớp PerformanceHintManager
để các trò chơi có thể gửi gợi ý hiệu suất cho Android về loại nhân và tốc độ xung nhịp CPU. Sau đó, hệ điều hành có thể quyết định cách sử dụng các gợi ý đó một cách tối ưu dựa trên hệ thống SoC và giải pháp tản nhiệt của thiết bị. Nếu kết hợp API này với tính năng theo dõi trạng thái nhiệt, ứng dụng có thể đưa ra các gợi ý phù hợp hơn cho hệ điều hành thay vì sử dụng các vòng lặp liên tục cũng như kỹ thuật lập trình khác có thể gây ra tình trạng điều tiết.
Sau đây là cách trò chơi sử dụng gợi ý về hiệu suất:
- Tạo phiên gợi ý cho các luồng chính hoạt động tương tự. Ví dụ:
- Luồng kết xuất và các phần phụ thuộc của luồng này nhận một phiên
- Trong Cocos, luồng công cụ chính và luồng kết xuất sẽ nhận được một phiên
- Trong Unity, hãy tích hợp trình bổ trợ Adaptive Performance Android Provider
- Trong Unreal, hãy tích hợp trình bổ trợ Unreal Adaptive Performance và sử dụng Scalability options (Các lựa chọn về khả năng mở rộng) để hỗ trợ nhiều cấp chất lượng
- Luồng IO nhận một phiên khác
- Luồng âm thanh nhận phiên thứ ba
- Luồng kết xuất và các phần phụ thuộc của luồng này nhận một phiên
- Trò chơi nên thực hiện việc này sớm, ít nhất 2 mili giây và tốt nhất là trên 4 mili giây trước khi phiên cần tăng tài nguyên hệ thống.
- Trong mỗi phiên gợi ý, hãy dự đoán thời lượng cần thiết mà mỗi phiên sẽ chạy. Thường thì thời lượng này tương đương với khoảng thời gian của một khung hình, nhưng ứng dụng có thể sử dụng khoảng thời gian ngắn hơn nếu mức tải không thay đổi đáng kể giữa các khung hình.
Sau đây là cách áp dụng lý thuyết vào thực tế:
Khởi chạy PerformanceHintManager và createHintSession
Lấy trình quản lý bằng cách sử dụng dịch vụ hệ thống và tạo một phiên gợi ý cho luồng hoặc nhóm luồng của bạn đang xử lý cùng một khối lượng công việc.
C++
int32_t tids[1];
tids[0] = gettid();
int64_t target_fps_nanos = getFpsNanos();
APerformanceHintManager* hint_manager = APerformanceHint_getManager();
APerformanceHintSession* hint_session =
APerformanceHint_createSession(hint_manager, tids, 1, target_fps_nanos);
Java
int[] tids = {
android.os.Process.myTid()
};
long targetFpsNanos = getFpsNanos();
PerformanceHintManager performanceHintManager =
(PerformanceHintManager) this.getSystemService(Context.PERFORMANCE_HINT_SERVICE);
PerformanceHintManager.Session hintSession =
performanceHintManager.createHintSession(tids, targetFpsNanos);
Đặt luồng nếu cần
Ngày phát hành:
Android 11 (API cấp 34)
Sử dụng hàm setThreads
của PerformanceHintManager.Session
khi bạn có các luồng khác cần được thêm sau. Ví dụ: nếu tạo luồng vật lý sau và cần thêm luồng đó vào phiên, bạn có thể sử dụng API setThreads
này.
C++
auto tids = thread_ids.data();
std::size_t size = thread_ids_.size();
APerformanceHint_setThreads(hint_session, tids, size);
Java
int[] tids = new int[3];
// add all your thread IDs. Remember to use android.os.Process.myTid() as that
// is the linux native thread-id.
// Thread.currentThread().getId() will not work because it is jvm's thread-id.
hintSession.setThreads(tids);
Nếu đang nhắm đến các Cấp độ API thấp hơn, bạn sẽ cần huỷ phiên và tạo lại phiên mới mỗi khi cần thay đổi mã nhận dạng luồng.
Báo cáo thời lượng công việc thực tế
Theo dõi thời lượng thực tế cần thiết để hoàn thành công việc theo nano giây và báo cáo thời lượng đó cho hệ thống sau khi hoàn thành công việc ở mỗi chu kỳ. Ví dụ: nếu đây là cho các luồng kết xuất, hãy gọi phương thức này trên mọi khung hình.
Để có được thời gian thực tế một cách đáng tin cậy, hãy sử dụng:
C++
clock_gettime(CLOCK_MONOTONIC, &clock); // if you prefer "C" way from <time.h>
// or
std::chrono::high_resolution_clock::now(); // if you prefer "C++" way from <chrono>
Java
System.nanoTime();
Ví dụ:
C++
// All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
auto start_time = std::chrono::high_resolution_clock::now();
// do work
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count();
int64_t actual_duration = static_cast<int64_t>(duration);
APerformanceHint_reportActualWorkDuration(hint_session, actual_duration);
Java
long startTime = System.nanoTime();
// do work
long endTime = System.nanoTime();
long duration = endTime - startTime;
hintSession.reportActualWorkDuration(duration);
Cập nhật Thời lượng công việc mục tiêu khi cần
Bất cứ khi nào thời lượng làm việc mục tiêu của bạn thay đổi (ví dụ: nếu trình phát chọn một fps mục tiêu khác), hãy gọi phương thức updateTargetWorkDuration
để hệ thống biết. Nhờ đó, hệ điều hành có thể điều chỉnh tài nguyên theo mục tiêu mới. Bạn không cần gọi hàm này trên mọi khung hình và chỉ cần gọi khi thời lượng mục tiêu thay đổi.
C++
APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);
Java
hintSession.updateTargetWorkDuration(targetDuration);