API Gợi ý về hiệu suất

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 lượng 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
Hình 1. Thống đốc có thể mất khoảng 200 mili giây để tăng hoặc giảm tần số CPU. ADPF hoạt động với hệ thống Điều chỉnh tần số và điện áp động (DVFS) để mang lại hiệu suất tốt nhất trên mỗi watt

PerformanceHint API tóm tắt nhiều hơn độ trễ DVFS

ADPF Tóm tắt nhiều hơn Độ trễ DVFS
Hình 2. ADPF biết cách thay mặt bạn đưa ra quyết định đúng đắn nhất
  • 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:

  1. Tạo phiên gợi ý cho các luồng chính hoạt động tương tự nhau. Ví dụ:
  2. 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.
  3. 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 phải 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.

Để nhận đượ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 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);