Interfejs Performance Hint API

Data wydania:

Android 12 (poziom API 31) – interfejs Performance Hint API

Android 13 (poziom API 33) – menedżer wskazówek dotyczących wydajności w interfejsie NDK API

(Wersja testowa) Android 15 (DP1) – reportActualWorkDuration()

Dzięki wskazówkom dotyczącym wydajności procesora gra może wpływać na dynamiczne działanie procesora zachowanie, które lepiej pasuje do potrzeb. Na większości urządzeń Android dynamicznie dostosowuje taktowanie procesora oraz typ rdzeni zadania na podstawie poprzednich żądań. Jeśli zadanie używa większej liczby zasobów procesora, taktowanie jest zwiększone, a zadanie zostanie ostatecznie przeniesione do większego rdzenia. Jeśli zbiór zadań używa mniej zasobów, Android zmniejsza przydział zasobów. Dzięki ADPF aplikacja lub gra może wysłać dodatkowy sygnał dotyczący jej wyników i terminów. Ten sprawia, że system intensywniej przyspiesza (poprawia wydajność) i obniża szybko uruchamiają się po zakończeniu zadania (co pozwala zmniejszyć zużycie energii).

Taktowanie zegara

Gdy urządzenia z Androidem dynamicznie dostosowują taktowanie taktowania procesora, częstotliwość może żeby zmienić wydajność kodu. Projektowanie kodu odpowiadającego zegarowi dynamicznemu prędkości jest ważne dla maksymalizacji wydajności, utrzymania bezpiecznej temperatury i wydajne korzystanie z energii. Nie możesz bezpośrednio przypisać częstotliwości procesora w kodzie aplikacji. W efekcie aplikacje często próbują uruchamiać Częstotliwość taktowania procesora polega na uruchomieniu pętli zajętej w wątku w tle, i wydaje się bardziej wymagające. Nie jest to zła praktyka, ponieważ zużywa energię i zwiększa obciążenie cieplne urządzenia, gdy aplikacja nie używa dodatkowej i zasobami Google Cloud. Interfejs CPU PerformanceHint API został zaprojektowany, aby rozwiązać ten problem. Według poinformowanie systemu o rzeczywistym czasie trwania i docelowym czasie trwania pracy, Android będzie mógł uzyskać informacje o potrzebach aplikacji w zakresie CPU i przydzielić oszczędność zasobów. Pozwala to uzyskać optymalną wydajność przy wydajnym zasilaniu. poziom konsumpcji.

Typy podstawowe

Rodzaje rdzeni procesora, na których działa gra, to kolejna ważna wydajność współczynnik konwersji. Urządzenia z Androidem często zmieniają rdzeń procesora przypisany do wątku na podstawie najnowszego zachowania zbioru zadań. Przypisanie rdzeni procesora jest jeszcze większe układy SOC z kilkoma typami rdzeni. Na niektórych z tych urządzeń rdzenie mogą być używane tylko przez krótki czas, bez wykorzystania termicznych urządzeń stanu.

Gra nie powinna próbować ustawiać głównej koligacji procesora z tych powodów:

  • Najlepszy typ rdzeni dla zadania różni się w zależności od modelu urządzenia.
  • Zrównoważony rozwój większych rdzeni zależy od układu SOC i różnych rozwiązania termiczne dostępne w poszczególnych modelach urządzeń.
  • Wpływ na środowisko na temperaturę termiczne może dodatkowo komplikować rdzeń wyboru. Na przykład pogoda lub etui na telefon może zmienić stan temperatury. urządzenia.
  • Wybrane podstawowe funkcje nie obsługują nowych urządzeń o większej wydajności, właściwości termiczne. W związku z tym urządzenia często ignorują procesor gry. o podobnych zainteresowaniach.

Przykład domyślnego działania algorytmu szeregowania w systemie Linux

Działanie algorytmu szeregowania w systemie Linux
Rysunek 1. Zwiększenie lub zmniejszenie częstotliwości procesora może potrwać ok. 200 ms. ADPF współpracuje z systemem dynamicznego napięcia i częstotliwości (DVFS), aby zapewnić najlepszą wydajność na wat.

Interfejs PerformanceHint API pobiera więcej czasu niż czas oczekiwania w DVFS

Abstrakcje ADPF są większe niż opóźnienia w DVFS
Rysunek 2. ADPF wie, jak podjąć najlepszą decyzję w Twoim imieniu.
  • Jeśli zadania muszą działać na określonym procesorze, interfejs PerformanceHint API wie, jak podjąć decyzję w Twoim imieniu.
  • Nie musisz więc korzystać z podobnych zainteresowań.
  • Urządzenia mają różne topologie. Cechy mocy i temperatury zbyt zróżnicowane, by pokazać je deweloperowi.
  • Nie możesz zakładać żadnych założeń dotyczących systemu, w którym działasz.

Rozwiązanie

ADPF udostępnia PerformanceHintManager aby gry mogły wysyłać do Androida wskazówki dotyczące wydajności, aby uzyskać typ rdzenia. Dzięki temu system operacyjny może zdecydować, jak najlepiej wykorzystać wskazówki na podstawie układu SOC, roztworu termicznego urządzenia. Jeśli Twoja aplikacja korzysta zarówno z tego interfejsu API, jak i z Term stanu monitorowania, może przekazać systemowi operacyjnemu bardziej świadome wskazówki, zamiast używać zajęte pętle i inne techniki kodowania, które mogą powodować ograniczanie przepustowości.

Oto jak gra wykorzystuje wskazówki dotyczące wydajności:

  1. Utwórz sesje wskazówek dla głównych wątków, które zachowują się podobnie. Na przykład:
    • Wątek renderowania i jego zależności otrzymują 1 sesję
      1. W Cocos główny wątek silnika i wątek renderowania otrzymują jeden sesja
      2. Zintegruj w Unity wtyczkę dostawcy Androida Adaptive Performance
      3. W Unreal zintegruj wtyczkę Unreal Adaptive Performance i zastosuj Opcje skalowalności umożliwiające obsługę wielu poziomów jakości.
    • Wątki zamówienia reklamowego otrzymują kolejną sesję
    • Wątki audio otrzymują 3 sesję
  2. Gra powinna to zrobić wcześnie, co najmniej 2 ms, a najlepiej więcej niż 4 ms. przed rozpoczęciem sesji należy zwiększyć zasoby systemowe.
  3. W przypadku każdej sesji podpowiedzi przewiduj czas jej trwania. Typowy czas trwania jest odpowiednikiem interwału klatek, ale aplikacja może użyć funkcji jeśli zbiór zadań nie różni się znacznie między klatkami, a krótszy odstęp czasu.

Jak zastosować teorię w praktyce:

Inicjowanie PerformanceHintManager i createHintSession

Pobierz menedżera za pomocą usługi systemowej i utwórz sesję podpowiedzi dotyczącą wątku lub grupę wątków działającą na tym samym zadaniu.

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);

W razie potrzeby ustaw wątki

Data wydania:

Android 11 (poziom API 34)

Korzystanie z setThreads funkcji PerformanceHintManager.Session, gdy masz inne wątki które trzeba dodać później. Jeśli na przykład utworzysz wątek fizyki, gdy zechcesz dodać go do sesji, możesz użyć tego interfejsu API setThreads.

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);

Jeśli kierujesz reklamy na niższe poziomy interfejsu API, musisz zniszczyć sesję i za każdym razem, gdy chcesz zmienić identyfikatory wątków, twórz nową sesję.

Raportuj rzeczywisty czas trwania pracy

Możesz śledzić w nanosekundach rzeczywisty czas potrzebny do ukończenia pracy i generować raporty jest przesyłane do systemu po zakończeniu pracy w każdym cyklu. Na przykład, jeśli dla wątków renderowania. Wywołuj go w każdej klatce.

Aby uzyskać dokładny czas, użyj:

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();

Na przykład:

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);

W razie potrzeby zaktualizuj docelowy czas pracy

Gdy zmienia się docelowy czas trwania pracy, na przykład jeśli gracz wybierze dla różnych docelowych klatek na sekundę, wywołaj funkcję updateTargetWorkDuration powiadamia system, tak aby mógł dostosowywać zasoby na nową wartość docelową. Nie musisz wywoływać tej funkcji za każdym razem – wystarczy gdy zmieni się docelowy czas trwania.

C++

APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

Java

hintSession.updateTargetWorkDuration(targetDuration);