API תרמי

תאריך פרסום:

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

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

(תצוגה מקדימה) Android 15 (DP1) – getThermalHeadroomThresholds()

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

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

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

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

שילוב מראש של API תרמי ADPF
איור 1. קיבולת תרמית בלי לעקוב באופן פעיל אחרי getThermalHeadroom
שילוב של ממשק API תרמי של ADPF
איור 2. שיפור תרמי עם ניטור פעיל של 'getThermalHeadroom'

רכישת מנהל תרמי

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

C++‎

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

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

תחזית של מרווח ההשפעה התרמי X שניות קדימה כדי להשיג שליטה רבה יותר

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

התוצאה נעה בין 0.0f (ללא ויסות נתונים, THERMAL_STATUS_NONE) ל-1.0f (ויסות נתונים (throttle) כבד, THERMAL_STATUS_SEVERE). אם במשחקים שלכם יש רמות איכות שונות של גרפיקה, תוכלו לפעול לפי הנחיות לגבי קיבולת תרמית.

C++‎

float thermal_headroom = AThermal_getThermalHeadroom(10);
ALOGI("ThermalHeadroom in 10 sec: %f", thermal_headroom);

Java

float thermalHeadroom = powerManager.getThermalHeadroom(10);
Log.d("ADPF", "ThermalHeadroom in 10 sec: " + 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). לשם כך, תוכלו לרשום קריאה חוזרת כדי שהמערכת תודיע לכם בכל פעם הסטטוס השתנה.

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), כדאי לעיין ב הקטע Thermal API ב-Adaptability Codelab .

הנחיות לגבי טמפרטורה קבועה

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

סטטוסים תרמיים ומשמעותם

מגבלות המכשיר של ה-API התרמי

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

  • חשוב לא לשלוח קריאה ל-API של GetThermalHeadroom() בתדירות גבוהה מדי. הפעולה הזו תגרום התוצאה היא שה-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, יש ויסות נתונים (throttle) במכשיר באופן תרמי.
  3. אם getCurrentThermalStatus() ימשיך להחזיר THERMAL_STATUS_NONE, לאו דווקא אומר שהמכשיר לא עובר ויסות נתונים (throttle) תרמי. ייתכן המשמעות היא שהמכשיר getCurrentThermalStatus() לא נתמך. יש לבדוק את הערך המוחזר של getThermalHeadroom() כדי לוודא את המצב של במכשיר.
  4. אם getThermalHeadroom() מחזירה ערך של > 1.0, הסטטוס יכול להיות THERMAL_STATUS_SEVERE ומעלה, צריך להפחית את עומס העבודה באופן מיידי לשמור על עומס עבודה נמוך יותר עד ש-getThermalHeadroom() יחזיר ערך נמוך יותר
  5. אם getThermalHeadroom() מחזירה ערך של 0.95, הסטטוס יכול להיות בפועל THERMAL_STATUS_MODERATE ומעלה, צריך לצמצם את עומס העבודה באופן מיידי ולשמור על מעקב כדי למנוע קריאה גבוהה יותר
  6. אם הפונקציה getThermalHeadroom() מחזירה ערך של 0.85, הסטטוס יכול להיות THERMAL_STATUS_LIGHT, לשמור על פיקוח ולהפחית את עומס העבודה אם אפשר

Pseudocode (קוד פסאודונימי):

  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 במכשירים ישנים