Ngày phát hành:
Android 12 (API cấp 31) – API Gợi ý về hiệu suất
Android 13 (API cấp 33) – Trình quản lý gợi ý về hiệu suất trong API NDK
(Xem trước) Android 15 (DP1) – reportActualWorkDuration()
Với các gợi ý về hiệu suất của CPU, trò chơi có thể tác động đến hiệu suất CPU động để phù hợp hơn với nhu cầu của công cụ đó. Trên hầu hết các thiết bị, Android sẽ tự động điều chỉnh tốc độ xung nhịp CPU và loại lõi cho khối lượng công việc dựa trên các nhu cầu trước đây. Nếu khối lượng công việc sử dụng nhiều tài nguyên CPU hơn thì tốc độ xung nhịp sẽ tăng lên và thì khối lượng công việc sẽ được chuyển sang lõi lớn hơn. Nếu khối lượng công việc sử dụng ít 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 tín hiệu về hiệu suất và thời hạn. Chiến dịch 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 xung nhịp nhanh khi hoàn thành khối lượng công việc (tiết kiệm điện năng sử dụng).
Tốc độ xung nhịp
Khi thiết bị Android tự động điều chỉnh tốc độ xung nhịp CPU, thì tần số này có thể
thay đổi hiệu suất của mã. Thiết kế mã xử lý đồng hồ động
tốc độ là yếu tố quan trọng để tối đa hoá hiệu suất, duy trì nhiệt độ an toàn
trạng thái và sử dụng điện hiệu quả. Bạn không thể trực tiếp chỉ định tần số của CPU
trong mã ứng dụng của bạn. Do đó, đây là cách phổ biến để các ứng dụng cố gắng chạy ở tốc độ cao hơn
Tốc độ xung nhịp của CPU là để chạy một vòng lặp bận trong một luồng ở chế độ nền để khối lượng công việc
có vẻ đòi hỏi cao hơn. Đây là một phương pháp không phù hợp vì vừa lãng phí năng lượng vừa tăng
tải nhiệt trên thiết bị khi ứng dụng không thực sự sử dụng
của chúng tôi. API PerformanceHint
của CPU được thiết kế để giải quyết sự cố này. Theo
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ể có đượ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ẽ mang lại hiệu suất tối ưu ở mức công suất hiệu quả
mức tiêu thụ.
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 biểu của Linux
![Hành vi của trình lập lịch biểu Linux](https://developer.android.com/static/games/optimize/adpf/images/adpf_linux_sched_behavior.png?authuser=19&hl=vi)
PerformanceHint API tóm tắt nhiều hơn độ trễ DVFS
![ADPF Tóm tắt nhiều hơn Độ trễ DVFS](https://developer.android.com/static/games/optimize/adpf/images/adpf_dvfs_latency.png?authuser=19&hl=vi)
- Nếu các tác vụ cần phải chạy trên một CPU cụ thể, PerformanceHint API sẽ biết cách đưa ra quyết định đó thay cho bạn.
- Do đó, bạn không cần phải sử dụng đối tượng tương đồng.
- Thiết bị đi kèm với những lời xin lỗi khác nhau; Đặc điểm về công suất và nhiệt quá đa dạng để nhà phát triển ứng dụng thấy.
- Bạn không thể đưa ra bất kỳ giả định nào về hệ thống cơ bản mà mình đang chạy.
Giải pháp
ADPF cung cấp PerformanceHintManager
để trò chơi có thể gửi gợi ý về hiệu suất cho Android về tốc độ xung nhịp CPU và
loại lõi. 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ự nhau. Ví dụ:
- Luồng kết xuất và các phần phụ thuộc của luồng kết xuất đó nhận được một phiên
- Trong Cocos, luồng công cụ chính và luồng kết xuất sẽ lấy một buổi tập luyện
- Trong Unity, hãy tích hợp trình bổ trợ Nhà cung cấp Android có hiệu suất thích ứng
- Trong Unreal, tích hợp trình bổ trợ Unreal Adaptive Performance và sử dụng Các lựa chọn về khả năng mở rộng để hỗ trợ nhiều mức 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 kết xuất đó nhận được 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ời lượng thông thường 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 khối lượng công việc 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
Yêu cầu trình quản lý sử dụng dịch vụ hệ thống và tạo phiên gợi ý cho luồng của bạn hoặc nhóm luồng làm việc trên 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 setThreads
hàm của PerformanceHintManager.Session
khi bạn có các luồng khác
mà bạn cần bổ sung sau này. Ví dụ: nếu bạn tạo luồng vật lý
sau này và cần thêm nó 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 một phiên mới mỗi khi bạn cần thay đổi mã nhận dạng chuỗi.
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 tính bằng nano giây và báo cáo nó vào hệ thống sau khi hoàn thành công việc trong mỗi chu kỳ. Ví dụ: nếu dành cho các luồng kết xuất hình ảnh, hãy gọi lệnh này trên mỗi khung hình.
Để biết 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 công việc mục tiêu của bạn thay đổi, chẳng hạn như khi người chơi chọn một
khung hình/giây mục tiêu khác nhau, hãy gọi updateTargetWorkDuration
thông báo cho hệ thống để hệ điều hành có thể điều chỉnh tài nguyên theo
cho mục tiêu mới. Bạn không phải gọi trên mọi khung hình và chỉ cần gọi
gọi nó khi thời lượng mục tiêu thay đổi.
C++
APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);
Java
hintSession.updateTargetWorkDuration(targetDuration);