Thermal API

תאריך הפרסום:

‫Android 11 (רמת API‏ 30) – Thermal API

‫Android 12 (רמת API‏ 31) – NDK API

(גרסת טרום-השקה) Android 15‏ (DP1) – getThermalHeadroomThresholds()

הביצועים הפוטנציאליים של האפליקציה מוגבלים על ידי המצב התרמי של המכשיר, שיכול להשתנות בהתאם למאפיינים כמו מזג האוויר, השימוש האחרון והעיצוב התרמי של המכשיר. מכשירים יכולים לשמור על רמת ביצועים גבוהה רק למשך זמן מוגבל לפני שהם מוגבלים בגלל חום. אחד היעדים העיקריים של ההטמעה צריך להיות השגת יעדי הביצועים בלי לחרוג ממגבלות הטמפרטורה. ה-API של Thermal מאפשר זאת בלי צורך באופטימיזציות ספציפיות למכשיר. בנוסף, כשמנסים לפתור בעיות בביצועים, חשוב לדעת אם המצב התרמי של המכשיר מגביל את הביצועים.

למנועי משחקים יש בדרך כלל פרמטרים של ביצועים בזמן ריצה, שאפשר לשנות כדי להתאים את עומס העבודה שהמנוע מטיל על המכשיר. לדוגמה, הפרמטרים האלה יכולים להגדיר את מספר ה-worker threads, את הקשר בין ה-worker threads לליבות גדולות וקטנות, את אפשרויות הדיוק של ה-GPU ואת הרזולוציות של ה-framebuffer. ב-Unity Engine, מפתחי משחקים יכולים לשנות את עומס העבודה על ידי שינוי הגדרות האיכות באמצעות הפלאגין Adaptive Performance. ב-Unreal Engine, משתמשים בהגדרות ההתאמה כדי לשנות את רמות האיכות באופן דינמי.

כשהמכשיר מתקרב למצב תרמי לא בטוח, המשחק יכול להימנע מהגבלת מהירות העיבוד על ידי הקטנת עומס העבודה באמצעות הפרמטרים האלה. כדי להימנע מהגבלת מהירות, כדאי לעקוב אחרי המצב התרמי של המכשיר ולהתאים באופן יזום את עומס העבודה של מנוע המשחק.

אם המכשיר מתחמם יתר על המידה, עומס העבודה חייב לרדת מתחת לרמת הביצועים הקבועה כדי שהחום יתפזר. אחרי שהמרווח התרמי יורד לרמות בטוחות יותר, המשחק יכול להעלות שוב את הגדרות האיכות, אבל חשוב למצוא רמת איכות ברת קיימא לזמן משחק אופטימלי.

אפשר לעקוב אחרי מצב הטמפרטורה של המכשיר באמצעות שיטת הסקר getThermalHeadroom. השיטה הזו מאפשרת לחזות כמה זמן המכשיר יכול לשמור על רמת הביצועים הנוכחית בלי להתחמם יתר על המידה. אם הזמן קצר יותר מהזמן שנדרש להפעלת עומס העבודה, המשחק צריך להקטין את עומס העבודה לרמה שניתן לשמור עליה. לדוגמה, המשחק יכול לעבור לליבות קטנות יותר, להפחית את קצב הפריימים או להפחית את רמת הדיוק.

ADPF Thermal API Pre-Integration
איור 1. מרווח תרמי בלי מעקב פעיל getThermalHeadroom
ADPF Thermal API Post-Integration
איור 2. מרווח תרמי עם מעקב פעיל אחרי `getThermalHeadroom`

רכישת Thermal Manager

כדי להשתמש ב-Thermal API, קודם צריך להשיג את Thermal Manager

C++‎

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

PowerManager powerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE);

חיזוי של מרווח הטמפרטורה המקסימלית x שניות מראש כדי לשפר את השליטה

אתם יכולים לבקש מהמערכת לחזות את הטמפרטורה x שניות קדימה עם עומס העבודה הנוכחי. כך תוכלו לשלוט בצורה מדויקת יותר ולצמצם את עומס העבודה כדי למנוע את הפעלת ההגבלה התרמית, וכך יהיה לכם יותר זמן להגיב.

התוצאה נעה בין 0.0f (ללא הגבלת מהירות, THERMAL_STATUS_NONE)

עד 1.0f (הגבלת מהירות חמורה, THERMAL_STATUS_SEVERE). אם יש לכם רמות שונות של איכות גרפית במשחקים, אתם יכולים לפעול לפי ההנחיות שלנו לגבי מרווח תרמי.

C++‎

float thermal_headroom = AThermal_getThermalHeadroom(0);
ALOGI("ThermalHeadroom: %f", thermal_headroom);

Java

float thermalHeadroom = powerManager.getThermalHeadroom(0);
Log.d("ADPF", "ThermalHeadroom: " + thermalHeadroom);

לחלופין, אפשר להסתמך על סטטוס הטמפרטורה כדי לקבל הבהרה

יכול להיות שכל דגם מכשיר מעוצב בצורה שונה. יכול להיות שמכשירים מסוימים יוכלו לפזר את החום בצורה טובה יותר, ולכן יוכלו לעמוד בטמפרטורה גבוהה יותר לפני שהביצועים שלהם יוגבלו. אם רוצים לקרוא קיבוץ פשוט של טווחי מרווחים תרמיים, אפשר לבדוק את הסטטוס התרמי כדי להבין את ערך המרווח התרמי במכשיר הנוכחי.

C++‎

AThermalStatus thermal_status = AThermal_getCurrentThermalStatus(thermal_manager);
ALOGI("ThermalStatus is: %d", thermal_status);

Java

int thermalStatus = powerManager.getCurrentThermalStatus();
Log.d("ADPF", "ThermalStatus is: " + thermalStatus);

קבלת התראות על שינויים בסטטוס הטמפרטורה

אפשר גם להימנע מבדיקת הסטטוס של thermalHeadroom עד שthermalStatus מגיע לרמה מסוימת (לדוגמה: THERMAL_STATUS_LIGHT). כדי לעשות זאת, אפשר לרשום קריאה חוזרת (callback) כדי שהמערכת תשלח לכם התראה בכל פעם שהסטטוס משתנה.

C++‎

int result = AThermal_registerThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
  // failed, check whether you have previously registered callback that
  // hasn’t been unregistered
}

Java

// PowerManager.OnThermalStatusChangedListener is an interface, thus you can
// also define a class that implements the methods
PowerManager.OnThermalStatusChangedListener listener = new
  PowerManager.OnThermalStatusChangedListener() {
    @Override
    public void onThermalStatusChanged(int status) {
        Log.d("ADPF", "ThermalStatus changed: " + status);
        // check the status and flip the flag to start/stop pooling when
        // applicable
    }
};
powerManager.addThermalStatusListener(listener);

חשוב לזכור להסיר את מאזין האירועים בסיום

C++‎

int result = AThermal_unregisterThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
  // failed, check whether the callback has been registered previously
}

Java

powerManager.removeThermalStatusListener(listener);

ניקוי

אחרי שמסיימים, צריך לנקות את thermal_manager שקיבלתם. אם אתם משתמשים ב-Java, אפשר לבצע איסוף אוטומטי של הפניה ל-PowerManager. אבל אם אתם משתמשים ב-Java API דרך JNI ושמרתם הפניה, אל תשכחו לנקות את ההפניה.

C++‎

AThermal_releaseManager(thermal_manager);

מדריך מלא להטמעה של Thermal API במשחק C++‎ מקורי באמצעות C++ API‏ (NDK API) ו-Java API (דרך JNI) זמין בקטע Integrate Thermal API ב-Adaptability codelab.

הנחיות לגבי מרווח תרמי

אפשר לעקוב אחרי מצב הטמפרטורה של המכשיר באמצעות שיטת getThermalHeadroom. בשיטה הזו מחושב משך הזמן שבו המכשיר יכול לשמור על רמת הביצועים הנוכחית לפני שהוא מגיע לTHERMAL_STATUS_SEVERE. לדוגמה, אם getThermalHeadroom(30) מחזירה 0.8, המשמעות היא שתוך 30 שניות, המרווח צפוי להגיע ל-0.8, כלומר יש מרחק של 0.2 מהגבלת קצב העברת נתונים חמורה, או 1.0. אם הזמן קצר מהזמן שנדרש להפעלת עומס העבודה, המשחק צריך להפחית את עומס העבודה לרמה שניתן לשמור עליה. לדוגמה, המשחק יכול להפחית את קצב הפריימים, להפחית את רמת הדיוק או להפחית את העומס על קישוריות הרשת.

המשמעות של סטטוסי הטמפרטורה

מגבלות של Thermal API במכשירים

יש כמה מגבלות ידועות או דרישות נוספות של Thermal API, בגלל הטמעות של Thermal API במכשירים ישנים יותר. אלה המגבלות והפתרונות האפשריים:

  • אל תבצעו קריאות ל-API של GetThermalHeadroom() בתדירות גבוהה מדי. אם תעשו זאת, ה-API יחזיר NaN. לא מומלץ להפעיל אותה יותר מפעם אחת כל 10 שניות.
  • לא מומלץ לבצע קריאות מכמה שרשורים, כי קשה יותר לוודא את תדירות הקריאות, וה-API עלול להחזיר את הערך NaN.
  • אם הערך ההתחלתי של GetThermalHeadroom() הוא NaN, ה-API לא זמין במכשיר
  • אם GetThermalHeadroom() מחזירה ערך גבוה (למשל: 0.85 ומעלה) ו-GetCurrentThermalStatus() עדיין מחזירה THERMAL_STATUS_NONE, סביר להניח שהסטטוס לא עודכן. אפשר להשתמש בהיוריסטיקה כדי להעריך את הסטטוס הנכון של ויסות התדרים התרמי, או פשוט להשתמש ב-getThermalHeadroom() בלי getCurrentThermalStatus().

דוגמה להיוריסטיקה:

  1. בודקים אם יש תמיכה ב-Thermal API. ‫isAPISupported() בודקת את הערך של הקריאה הראשונה אל getThermalHeadroom כדי לוודא שהוא לא 0 או NaN, ומדלגת על השימוש בממשק ה-API אם הערך הראשון הוא 0 או NaN.
  2. אם getCurrentThermalStatus() מחזיר ערך שונה מ-THERMAL_STATUS_NONE, המכשיר עובר ויסות תרמי.
  3. אם הפקודה getCurrentThermalStatus() מחזירה שוב ושוב את הערך THERMAL_STATUS_NONE, זה לא אומר בהכרח שהמכשיר לא עובר ויסות תרמי. יכול להיות שהמכשיר לא תומך ב-getCurrentThermalStatus(). בודקים את ערך ההחזרה של getThermalHeadroom() כדי לוודא את מצב המכשיר.
  4. אם הפונקציה getThermalHeadroom() מחזירה ערך גדול מ-1.0, יכול להיות שהסטטוס הוא THERMAL_STATUS_SEVERE או גבוה יותר. במקרה כזה, צריך להפחית את עומס העבודה באופן מיידי ולשמור על עומס עבודה נמוך יותר עד שהפונקציה getThermalHeadroom() תחזיר ערך נמוך יותר.
  5. אם הפונקציה getThermalHeadroom() מחזירה ערך של 0.95, יכול להיות שהסטטוס הוא THERMAL_STATUS_MODERATE או גבוה יותר. במקרה כזה, צריך לצמצם את עומס העבודה באופן מיידי ולהמשיך לעקוב כדי למנוע קריאה גבוהה יותר.
  6. אם הפונקציה getThermalHeadroom() מחזירה ערך של 0.85, יכול להיות שהסטטוס הוא THERMAL_STATUS_LIGHT. כדאי להמשיך לעקוב אחרי המצב ולצמצם את עומס העבודה אם אפשר.

קוד מדומה:

  bool isAPISupported() {
    float first_value_of_thermal_headroom = getThermalHeadroom();
    if ( first_value_of_thermal_headroom == 0 ||
      first_value_of_thermal_headroom == NaN ) {
        // Checked the thermal Headroom API's initial return value
        // it is NaN or 0,so, return false (not supported)
        return false;
    }
    return true;
  }

  if (!isAPISupported()) {
    // Checked the thermal Headroom API's initial return value, it is NaN or 0
    // Don’t use the API
  } else {
      // Use thermalStatus API to check if it returns valid values.
      if (getCurrentThermalStatus() > THERMAL_STATUS_NONE) {
          // The device IS being thermally throttled
      } else {
      // The device is not being thermally throttled currently. However, it
      // could also be an indicator that the ThermalStatus API may not be
      // supported in the device.
      // Currently this API uses predefined threshold values for thermal status
      // mapping. In the future  you may be able to query this directly.
      float thermal_headroom = getThermalHeadroom();
      if ( thermal_headroom > 1.0) {
            // The device COULD be severely throttled.
      } else  if ( thermal_headroom > 0.95) {
            // The device COULD be moderately throttled.
      } else if ( thermal_headroom > 0.85) {
            // The device COULD be experiencing light throttling.
      }
    }
  }

דיאגרמה:

דוגמה להיוריסטיקה של ADPF
איור 3. דוגמה להיוריסטיקה לקביעת התמיכה ב-Thermal API במכשירים ישנים