เผยแพร่:
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
PerformanceHint API แอบสแตรกต์มากกว่าเวลาในการตอบสนอง DVFS
- หากงานจำเป็นต้องทำงานด้วย CPU ที่เฉพาะเจาะจง PerformanceHint API จะรู้วิธี ตัดสินใจในนามของคุณ
- ดังนั้นคุณจึงไม่จำเป็นต้องใช้กลุ่มความสนใจ
- อุปกรณ์มาพร้อมกับโทโพโลยีที่หลากหลาย ลักษณะของพลังงานและความร้อน หลากหลายเกินกว่าที่จะแสดงต่อนักพัฒนาแอป
- คุณไม่สามารถคาดเดาเกี่ยวกับระบบพื้นฐานที่คุณกำลังใช้งานอยู่
โซลูชัน
ADPF จะจัดเตรียม PerformanceHintManager
คลาสเพื่อให้เกมสามารถส่งคำแนะนำด้านประสิทธิภาพไปยัง Android สำหรับความเร็วนาฬิกาของ CPU และ
ประเภทหลัก จากนั้นระบบปฏิบัติการจะเลือกวิธีที่ดีที่สุดในการใช้คำแนะนำตาม SoC และ
โซลูชันความร้อนของอุปกรณ์ หากแอปใช้ API นี้ร่วมกับความร้อน
สามารถให้คำแนะนำเกี่ยวกับระบบปฏิบัติการ
ได้ดียิ่งขึ้น แทนการใช้
ลูปที่ยุ่งเหยิงและเทคนิคการเขียนโค้ดอื่นๆ ที่อาจทำให้เกิดการควบคุม
ต่อไปนี้คือวิธีที่เกมใช้คำแนะนำด้านประสิทธิภาพ
- สร้างเซสชันคำแนะนำสำหรับชุดข้อความหลักที่ทำงานคล้ายกัน ดังตัวอย่างต่อไปนี้
- การแสดงผลชุดข้อความและทรัพยากร Dependency จะได้รับ 1 เซสชัน
- ใน Cocos เทรดเครื่องมือหลักและเทรดแสดงผลจะมี 1 เทรด เซสชัน
- ใน Unity ให้ผสานรวมปลั๊กอินผู้ให้บริการ Android สำหรับประสิทธิภาพที่ปรับอัตโนมัติ
- ใน Unreal ให้รวมปลั๊กอิน Unreal Adaptive Performance และใช้ ตัวเลือกความสามารถในการปรับขนาดเพื่อรองรับคุณภาพหลายระดับ
- ชุดข้อความ IO จะได้รับเซสชันอื่น
- ชุดข้อความเสียงจะได้รับเซสชันที่ 3
- การแสดงผลชุดข้อความและทรัพยากร Dependency จะได้รับ 1 เซสชัน
- เกมควรดำเนินการตั้งแต่เนิ่นๆ อย่างน้อย 2 มิลลิวินาที และควรมากกว่า 4 มิลลิวินาที ก่อนที่เซสชันจะต้องมีทรัพยากรระบบเพิ่มขึ้น
- ในเซสชันคำแนะนำแต่ละเซสชัน ให้คาดการณ์ระยะเวลาในการทำงานแต่ละเซสชัน ระยะเวลาโดยทั่วไปเทียบเท่ากับช่วงเวลาของเฟรม แต่แอปสามารถใช้ ช่วงเวลาที่สั้นลงหากปริมาณงานไม่แตกต่างกันอย่างมากในแต่ละเฟรม
ต่อไปนี้คือวิธีนำทฤษฎีนี้ไปใช้จริง
เริ่มต้น 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);