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

เผยแพร่:

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

Android 13 (API ระดับ 33) - ตัวจัดการคำแนะนำด้านประสิทธิภาพใน NDK API

(เวอร์ชันตัวอย่าง) Android 15 (DP1) - reportActualWorkDuration()

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

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

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

ประเภทหลัก

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

เกมของคุณไม่ควรพยายามตั้งค่ากลุ่มความสนใจหลักของ CPU ด้วยเหตุผลต่อไปนี้

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

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

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

PerformanceHint API แอบสแตรกต์มากกว่าเวลาในการตอบสนอง DVFS

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

โซลูชัน

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

ต่อไปนี้คือวิธีที่เกมใช้คำแนะนำด้านประสิทธิภาพ

  1. สร้างเซสชันคำแนะนำสำหรับชุดข้อความหลักที่ทำงานคล้ายกัน ดังตัวอย่างต่อไปนี้
  2. เกมควรดำเนินการตั้งแต่เนิ่นๆ อย่างน้อย 2 มิลลิวินาที และควรมากกว่า 4 มิลลิวินาที ก่อนที่เซสชันจะต้องมีทรัพยากรระบบเพิ่มขึ้น
  3. ในเซสชันคำแนะนำแต่ละเซสชัน ให้คาดการณ์ระยะเวลาในการทำงานแต่ละเซสชัน ระยะเวลาโดยทั่วไปเทียบเท่ากับช่วงเวลาของเฟรม แต่แอปสามารถใช้ ช่วงเวลาที่สั้นลงหากปริมาณงานไม่แตกต่างกันอย่างมากในแต่ละเฟรม

ต่อไปนี้คือวิธีนำทฤษฎีนี้ไปใช้จริง

เริ่มต้น PerformanceHintManager และ createHintSession

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

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