API คำแนะนำด้านประสิทธิภาพ

เปิดตัว:

Android 12 (API ระดับ 31) - PerformanceHintManager

Android 13 (API ระดับ 33) - Performance Hint Manager ใน NDK API

Android 15 (DP1) - reportActualWorkDuration()

คำแนะนำด้านประสิทธิภาพของ CPU ช่วยให้แอปมีอิทธิพลต่อลักษณะการทำงานด้านประสิทธิภาพของ CPU แบบไดนามิกเพื่อให้ตรงกับความต้องการของแอปได้ดียิ่งขึ้น ในอุปกรณ์ส่วนใหญ่ Android จะปรับความเร็วนาฬิกาและประเภทคอร์ของ CPU แบบไดนามิกสำหรับภาระงานโดยอิงตามความต้องการก่อนหน้านี้ หากภาระงานใช้ทรัพยากร CPU มากขึ้น ระบบจะเพิ่มความเร็วนาฬิกาและย้ายภาระงานไปยังคอร์ที่ใหญ่ขึ้นในที่สุด หากภาระงานใช้ทรัพยากรน้อยลง Android จะลดการจัดสรรทรัพยากร ADPF ช่วยให้แอปส่งสัญญาณเพิ่มเติมเกี่ยวกับประสิทธิภาพและกำหนดเวลาได้ ซึ่งจะช่วยให้ระบบเพิ่มความเร็วในการทำงานได้มากขึ้น (ปรับปรุงประสิทธิภาพ) และลดความเร็วนาฬิกาลงอย่างรวดเร็วเมื่อภาระงานเสร็จสมบูรณ์ (ประหยัดการใช้พลังงาน)

ความเร็วนาฬิกา

เมื่ออุปกรณ์ Android ปรับความเร็วนาฬิกาของ CPU แบบไดนามิก ความถี่อาจเปลี่ยนประสิทธิภาพของโค้ดได้ การออกแบบโค้ดที่จัดการความเร็วนาฬิกาแบบไดนามิกมีความสำคัญต่อการเพิ่มประสิทธิภาพให้สูงสุด รักษาสถานะความร้อนที่ปลอดภัย และใช้พลังงานอย่างมีประสิทธิภาพ คุณไม่สามารถกำหนดความถี่ของ CPU ในโค้ดแอปได้โดยตรง ด้วยเหตุนี้ วิธีทั่วไปที่แอปใช้เพื่อพยายามทำงานด้วยความเร็วนาฬิกาของ CPU ที่สูงขึ้นคือการเรียกใช้ลูปที่ทำงานตลอดเวลาในเธรดเบื้องหลังเพื่อให้ภาระงานดูเหมือนมีความต้องการทรัพยากรมากขึ้น ซึ่งเป็นแนวทางปฏิบัติที่ไม่ดีเนื่องจากทำให้สิ้นเปลืองพลังงานและเพิ่มภาระความร้อนให้กับอุปกรณ์เมื่อแอปไม่ได้ใช้ทรัพยากรเพิ่มเติมจริงๆ API PerformanceHint ของ CPU ได้รับการออกแบบมาเพื่อแก้ปัญหานี้ การแจ้งให้ระบบทราบระยะเวลาการทำงานจริงและระยะเวลาการทำงานเป้าหมายจะช่วยให้ Android ได้ภาพรวมของความต้องการ CPU ของแอปและจัดสรรทรัพยากรได้อย่างมีประสิทธิภาพ ซึ่งจะนำไปสู่ประสิทธิภาพสูงสุดที่ระดับการใช้พลังงานอย่างมีประสิทธิภาพ

ประเภทคอร์

ประเภทคอร์ของ CPU ที่แอปทำงานอยู่เป็นอีกปัจจัยสำคัญด้านประสิทธิภาพ อุปกรณ์ Android มักจะเปลี่ยนคอร์ของ CPU ที่กำหนดให้กับเธรดแบบไดนามิกโดยอิงตามลักษณะการทำงานของภาระงานล่าสุด การกำหนดคอร์ของ CPU มีความซับซ้อนมากยิ่งขึ้นใน SoC ที่มีคอร์หลายประเภท ในอุปกรณ์บางรุ่น คอร์ที่ใหญ่กว่าจะใช้ได้เพียงระยะเวลาสั้นๆ เท่านั้น หากใช้เป็นเวลานาน อุปกรณ์จะเข้าสู่สถานะความร้อนที่ไม่ยั่งยืน

แอปไม่ควรพยายามตั้งค่าความสัมพันธ์ของคอร์ของ CPU ด้วยเหตุผลต่อไปนี้

  • ประเภทคอร์ที่ดีที่สุดสำหรับภาระงานจะแตกต่างกันไปตามรุ่นของอุปกรณ์
  • ความยั่งยืนในการเรียกใช้คอร์ที่ใหญ่กว่าจะแตกต่างกันไปตาม SoC และโซลูชันด้านความร้อนต่างๆ ที่รุ่นของอุปกรณ์แต่ละรุ่นมีให้
  • ผลกระทบต่อสิ่งแวดล้อมต่อสถานะความร้อนอาจทำให้การเลือกคอร์มีความซับซ้อนมากยิ่งขึ้น เช่น สภาพอากาศหรือเคสโทรศัพท์อาจเปลี่ยนสถานะความร้อนของอุปกรณ์ได้
  • การเลือกคอร์ไม่สามารถรองรับอุปกรณ์ใหม่ที่มีประสิทธิภาพและความสามารถด้านความร้อนเพิ่มเติมได้ ด้วยเหตุนี้ อุปกรณ์จึงมักจะละเลยความสัมพันธ์ของโปรเซสเซอร์ของแอป

ตัวอย่างลักษณะการทำงานของตัวกำหนดตารางเวลา Linux เริ่มต้น

ลักษณะการทำงานของตัวจัดกำหนดการ Linux
รูปที่ 1 ตัวควบคุมอาจใช้เวลาประมาณ 200 มิลลิวินาทีในการเพิ่มหรือลดความถี่ของ CPU ADPF ทำงานร่วม กับระบบการปรับขนาดแรงดันไฟฟ้าและความถี่แบบไดนามิก (DVFS) เพื่อให้ได้ประสิทธิภาพ ที่ดีที่สุดต่อวัตต์

API PerformanceHint จะสรุปเวลาในการตอบสนองของ DVFS และอื่นๆ

ADPF สรุปค่าเวลาในการตอบสนองของ DVFS ได้มากกว่า
รูปที่ 2 ADPF ทราบวิธีตัดสินใจที่ดีที่สุดในนามของคุณ
  • หากงานต้องทำงานใน CPU ที่เฉพาะเจาะจง API PerformanceHint จะทราบวิธีตัดสินใจในนามของคุณ
  • ดังนั้น คุณจึงไม่จำเป็นต้องใช้ความสัมพันธ์
  • อุปกรณ์มาพร้อมกับโทโพโลยีที่หลากหลาย ลักษณะการทำงานด้านพลังงานและความร้อนมีความหลากหลายมากเกินกว่าที่จะเปิดเผยให้นักพัฒนาแอปทราบ
  • คุณไม่สามารถตั้งสมมติฐานใดๆ เกี่ยวกับระบบพื้นฐานที่คุณกำลังใช้งานอยู่ได้

โซลูชัน

ADPF มีคลาส PerformanceHintManager เพื่อให้แอปส่งคำแนะนำด้านประสิทธิภาพไปยัง Android สำหรับความเร็วนาฬิกาและ ประเภทคอร์ของ CPU ได้ จากนั้นระบบปฏิบัติการจะตัดสินใจว่าจะใช้คำแนะนำอย่างไรให้ดีที่สุดโดยอิงตาม SoC และโซลูชันด้านความร้อนของอุปกรณ์ หากแอปใช้ API นี้ร่วมกับการตรวจสอบสถานะความร้อน แอปจะให้คำแนะนำที่อิงตามข้อมูลมากขึ้นแก่ระบบปฏิบัติการแทนที่จะใช้ลูปที่ทำงานตลอดเวลาและเทคนิคการเขียนโค้ดอื่นๆ ที่อาจทำให้เกิดการควบคุมปริมาณ

วิธีนำทฤษฎีไปใช้จริง

เริ่มต้น PerformanceHintManager และสร้าง HintSession

รับตัวจัดการโดยใช้บริการของระบบและสร้างเซสชันคำแนะนำสำหรับเธรดหรือกลุ่มเธรดที่ทำงานในภาระงานเดียวกัน

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

ตั้งค่าเธรดหากจำเป็น

เปิดตัว:

Android 11 (API ระดับ 34)

ใช้ setThreads ฟังก์ชันของ PerformanceHintManager.Session เมื่อคุณมีเธรดอื่นๆ ที่ต้องเพิ่มในภายหลัง ตัวอย่างเช่น หากคุณสร้างเธรดฟิสิกส์ในภายหลังและต้องการเพิ่มเธรดลงในเซสชัน คุณสามารถใช้ 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);

หากกำหนดเป้าหมายเป็น API ระดับต่ำกว่า คุณจะต้องทำลายเซสชันและสร้างเซสชันใหม่ทุกครั้งที่ต้องการเปลี่ยนรหัสเธรด

รายงานระยะเวลาการทำงานจริง

ติดตามระยะเวลาจริงที่ต้องใช้ในการทำงานให้เสร็จสมบูรณ์เป็นหน่วยนาโนวินาทีและรายงานไปยังระบบเมื่อทำงานเสร็จสมบูรณ์ในทุกรอบ ตัวอย่างเช่น หากเป็นการติดตามสำหรับเธรดการแสดงผล ให้เรียกใช้ฟังก์ชันนี้ในทุกเฟรม

หากต้องการรับเวลาจริงอย่างน่าเชื่อถือ ให้ใช้โค้ดต่อไปนี้

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

ตัวอย่าง

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

อัปเดตระยะเวลาการทำงานเป้าหมายเมื่อจำเป็น

เมื่อใดก็ตามที่ระยะเวลาการทำงานเป้าหมายเปลี่ยนแปลง เช่น หากผู้เล่นเลือก FPS เป้าหมายอื่น ให้เรียกใช้updateTargetWorkDuration เมธอดเพื่อแจ้งให้ระบบทราบเพื่อให้ระบบปฏิบัติการปรับทรัพยากรตามเป้าหมายใหม่ได้ คุณไม่จำเป็นต้องเรียกใช้เมธอดนี้ในทุกเฟรมและต้องเรียกใช้เฉพาะเมื่อระยะเวลาเป้าหมายเปลี่ยนแปลงเท่านั้น

C++

APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

Java

hintSession.updateTargetWorkDuration(targetDuration);