ระบุการกระตุกที่เกี่ยวข้องกับการกระวนกระวาย

อาการกระตุกคือลักษณะการทํางานของระบบแบบสุ่มที่ทําให้งานไม่สามารถทํางานได้ หน้านี้จะอธิบายวิธีระบุและแก้ไขปัญหาการกระตุกที่เกี่ยวข้องกับความผันผวน

ความล่าช้าของเครื่องมือจัดตารางเวลาเธรดของแอป

ความล่าช้าของเครื่องมือจัดตารางเวลาเป็นอาการที่เห็นได้ชัดที่สุดของอาการกระตุก: กระบวนการที่ควรทำงานได้จะทำงานได้ แต่ไม่ทำงานเป็นระยะเวลาหนึ่ง ความสำคัญของความล่าช้าจะแตกต่างกันไปตามบริบท เช่น

  • เทรดเฮลเปอร์แบบสุ่มในแอปอาจมีความล่าช้าหลายมิลลิวินาทีโดยไม่มีปัญหา
  • เทรด UI ของแอปอาจยอมรับความผันผวนได้ 1-2 มิลลิวินาที
  • เทรด K ของไดรเวอร์ที่ทำงานเป็น SCHED_FIFO อาจทำให้เกิดปัญหาหากสามารถทํางานได้นาน 500us ก่อนทํางาน

คุณสามารถระบุเวลาที่เรียกใช้ได้ใน systrace จากแถบสีน้ำเงินที่อยู่หน้าส่วนที่กำลังทํางานของเธรด นอกจากนี้ คุณยังกำหนดเวลาที่สามารถเรียกใช้ได้จากระยะเวลาระหว่างเหตุการณ์ sched_wakeup สำหรับเธรดและเหตุการณ์ sched_switch ที่ส่งสัญญาณการเริ่มต้นการเรียกใช้เธรด

ด้ายที่ทำงานนานเกินไป

เทรด UI ของแอปที่ทำงานได้นานเกินไปอาจทำให้เกิดปัญหา โดยทั่วไปแล้ว เทรดที่ระดับล่างซึ่งมีรันไทม์นานจะมีสาเหตุแตกต่างกัน แต่การพยายามลดรันไทม์ของเทรด UI ให้เหลือ 0 อาจต้องแก้ไขปัญหาเดียวกันกับที่ทำให้เทรดที่ระดับล่างมีรันไทม์นาน วิธีลดความล่าช้า

  1. ใช้ cpusets ตามที่อธิบายไว้ในการควบคุมการระบายความร้อน
  2. เพิ่มค่า CONFIG_HZ
    • ที่ผ่านมา ค่านี้ได้รับการตั้งค่าไว้ที่ 100 ในแพลตฟอร์ม arm และ arm64 อย่างไรก็ตาม นี่เป็นค่าที่เกิดจากประวัติศาสตร์และไม่ใช่ค่าที่ดีสำหรับอุปกรณ์แบบอินเทอร์แอกทีฟ CONFIG_HZ=100 หมายความว่า Jiffy มีความยาว 10 มิลลิวินาที ซึ่งหมายความว่าการจัดสรรภาระงานระหว่าง CPU อาจใช้เวลา 20 มิลลิวินาที (2 Jiffy) ซึ่งอาจทําให้ระบบทำงานขัดข้องได้อย่างมาก
    • อุปกรณ์รุ่นล่าสุด (Nexus 5X, Nexus 6P, Pixel และ Pixel XL) จะมาพร้อมกับค่า CONFIG_HZ=300 ซึ่งควรมีต้นทุนพลังงานเพียงเล็กน้อยในขณะที่ปรับปรุงเวลาที่ใช้ทำงานได้อย่างมาก หากพบว่าการสิ้นเปลืองพลังงานหรือปัญหาด้านประสิทธิภาพเพิ่มขึ้นอย่างมากหลังจากเปลี่ยน CONFIG_HZ แสดงว่าหนึ่งในไดรเวอร์ของคุณอาจใช้ตัวจับเวลาตาม jiffies ดิบแทนมิลลิวินาทีและแปลงเป็น jiffies ปัญหานี้มักจะแก้ไขได้ง่ายๆ (ดูแพตช์ที่แก้ไขปัญหาตัวจับเวลา kgsl ใน Nexus 5X และ 6P เมื่อแปลงเป็น CONFIG_HZ=300)
    • สุดท้ายนี้ เราได้ทดสอบ CONFIG_HZ=1000 ใน Nexus/Pixel และพบว่าตัวเลือกนี้ช่วยเพิ่มประสิทธิภาพและลดการใช้พลังงานได้อย่างชัดเจนเนื่องจากมีค่าใช้จ่ายเพิ่มเติมของ RCU ลดลง

การเปลี่ยนแปลง 2 รายการนี้เพียงอย่างเดียวก็น่าจะทำให้อุปกรณ์ทำงานได้ดีขึ้นมากสำหรับเวลารันไทม์ของ UI Thread ภายใต้ภาระงาน

ใช้ sys.use_fifo_ui

คุณสามารถลองลดเวลาที่สามารถเรียกใช้ได้ของชุดข้อความ UI เป็น 0 ได้โดยการตั้งค่าพร็อพเพอร์ตี้ sys.use_fifo_ui เป็น 1

คําเตือน: อย่าใช้ตัวเลือกนี้กับการกำหนดค่า CPU ที่ไม่เหมือนกัน เว้นแต่คุณจะมีตัวจัดตารางเวลา RT ที่คำนึงถึงความสามารถ และขณะนี้ไม่มีเครื่องมือกำหนดเวลา RT ที่จัดส่งอยู่ซึ่งจะคำนึงถึงกำลังการผลิต เรากำลังพัฒนาแอปสำหรับ EAS แต่ยังไม่พร้อมให้บริการ ตัวจัดตารางเวลา RT เริ่มต้นจะอิงตามลําดับความสําคัญของ RT เท่านั้น และพิจารณาว่า CPU มีเธรด RT ที่มีลําดับความสําคัญเท่าหรือสูงกว่าอยู่แล้วหรือไม่

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

เมื่อเปิดใช้ sys.use_fifo_ui แล้ว ActivityManager จะติดตามเธรด UI และเธรด Render (เธรด 2 รายการที่สำคัญต่อ UI มากที่สุด) ของแอปที่ทำงานอยู่ด้านบน และกำหนดเธรดเหล่านั้นเป็น SCHED_FIFO แทน SCHED_OTHER วิธีนี้ช่วยขจัดความผันผวนจาก UI และ RenderThread ได้อย่างมีประสิทธิภาพ ร่องรอยที่เรารวบรวมเมื่อเปิดใช้ตัวเลือกนี้จะแสดงเวลาที่ใช้ทำงานในระดับไมโครวินาทีแทนมิลลิวินาที

อย่างไรก็ตาม เนื่องจากตัวกระจายภาระของ RT ไม่ได้คำนึงถึงขีดความสามารถ ประสิทธิภาพการเริ่มต้นของแอปจึงลดลง 30% เนื่องจากเธรด UI ที่รับผิดชอบการเริ่มต้นแอปจะย้ายจากแกน Kryo ทอง 2.1 GHz ไปยังแกน Kryo เงิน 1.5 GHz เมื่อใช้โหลดบาลานเซอร์ RT ที่คำนึงถึงขีดความสามารถ เราพบว่าประสิทธิภาพในการดำเนินการแบบเป็นกลุ่มเทียบเท่ากัน และเวลาเฟรมในเปอร์เซ็นต์ไทล์ 95 และ 99 ลดลง 10-15% ในหลายๆ การทดสอบประสิทธิภาพ UI

ขัดจังหวะการรับส่งข้อมูล

เนื่องจากแพลตฟอร์ม ARM จะส่งการขัดจังหวะไปยัง CPU 0 เท่านั้นโดยค่าเริ่มต้น เราจึงขอแนะนำให้ใช้ตัวปรับสมดุล IRQ (irqbalance หรือ msm_irqbalance ในแพลตฟอร์ม Qualcomm)

ในระหว่างการพัฒนา Pixel เราพบปัญหาการกระตุกที่อาจเกิดจาก CPU 0 ที่ถูกขัดจังหวะมากเกินไปโดยตรง ตัวอย่างเช่น หากมีการกำหนดเวลาให้กับเธรด mdss_fb0 ใน CPU 0 ก็จะมีแนวโน้มที่จะกระตุกมากกว่ามากเนื่องจากมีการขัดจังหวะที่เกิดจากจอแสดงผลเกือบจะทันทีก่อนการสแกนออก mdss_fb0 กำลังทำงานอยู่ท่ามกลางกำหนดเวลาที่กระชั้นชิดมาก และจะต้องเสียเวลาไปกับ MDSS ตัวแฮนเดิลการขัดจังหวะ ตอนแรกเราพยายามแก้ไขปัญหานี้โดยการตั้งค่าความเกี่ยวข้องของ CPU ของเธรด mdss_fb0 เป็น CPU 1-3 เพื่อหลีกเลี่ยงการแย่งชิงกับการขัดจังหวะ แต่แล้วเราก็พบว่าเรายังไม่ได้เปิดใช้ msm_irqbalance เมื่อเปิดใช้ msm_irqbalance อาการกระตุกจะดีขึ้นอย่างเห็นได้ชัด แม้ว่าทั้ง mdss_fb0 และสัญญาณรบกวน MDSS จะอยู่ใน CPU ตัวเดียวกันก็ตาม เนื่องจากมีการแย่งชิงจากสัญญาณรบกวนอื่นๆ น้อยลง

ซึ่งจะระบุได้ใน systrace โดยดูที่ส่วน sched และส่วน irq ส่วน sched จะแสดงสิ่งที่กำหนดเวลาไว้ แต่ส่วนที่ทับซ้อนกันในส่วน irq หมายความว่ามีการขัดจังหวะเกิดขึ้นในช่วงเวลานั้นแทนกระบวนการที่กำหนดเวลาไว้ตามปกติ หากเห็นการหยุดทำงานเป็นเวลานานระหว่างการขัดจังหวะ ตัวเลือกของคุณมีดังนี้

  • ทำให้ตัวแฮนเดิลการขัดจังหวะทำงานเร็วขึ้น
  • ป้องกันไม่ให้เกิดการขัดจังหวะตั้งแต่ต้น
  • เปลี่ยนความถี่ของการขัดจังหวะให้ไม่ตรงกับงานอื่นๆ ปกติที่อาจรบกวน (หากเป็นการขัดจังหวะปกติ)
  • ตั้งค่าการเชื่อมโยง CPU ของการขัดจังหวะโดยตรงและป้องกันไม่ให้มีการถ่วงน้ำหนัก
  • ตั้งค่าความเกี่ยวข้องของ CPU ของเธรดที่การขัดจังหวะรบกวนเพื่อหลีกเลี่ยงการขัดจังหวะ
  • ใช้ตัวจัดสรรการขัดจังหวะเพื่อย้ายการขัดจังหวะไปยัง CPU ที่มีภาระงานน้อยลง

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

Softirq ที่ทำงานนาน

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


softirq จะปรากฏในส่วน irq ของการติดตาม จึงตรวจพบได้ง่ายหากสามารถจำลองปัญหาได้ขณะติดตาม เนื่องจาก SoftIRQ สามารถทำงานภายในกระบวนการของผู้ใช้ SoftIRQ ที่ไม่ดีจึงอาจแสดงเป็นรันไทม์เพิ่มเติมภายในกระบวนการของผู้ใช้โดยไม่มีเหตุผลที่ชัดเจน หากเห็นเช่นนั้น ให้ตรวจสอบส่วน irq เพื่อดูว่า softirq เป็นสาเหตุหรือไม่

ไดรเวอร์ที่ปิดใช้การแย่งสิทธิ์หรือ IRQ เป็นเวลานานเกินไป

การปิดใช้การแย่งสิทธิ์หรือการตัดตอนเป็นเวลานานเกินไป (หลายสิบมิลลิวินาที) จะทําให้เกิดความกระตุก โดยปกติแล้ว อาการกระตุกจะแสดงเป็นชุดข้อความที่พร้อมใช้งานแต่ไม่ได้ทํางานบน CPU บางตัว แม้ว่าชุดข้อความที่พร้อมใช้งานจะมีลําดับความสําคัญสูงกว่า (หรือ SCHED_FIFO) มากเมื่อเทียบกับชุดข้อความอื่นๆ

หลักเกณฑ์บางส่วนมีดังนี้

  • หากเธรดที่ทำงานได้คือ SCHED_FIFO และเธรดที่ทำงานอยู่คือ SCHED_OTHER แสดงว่าเธรดที่ทำงานอยู่มีการปิดใช้การแย่งสิทธิ์หรือขัดจังหวะ
  • หากเธรดที่พร้อมใช้งานมีลําดับความสําคัญสูงกว่า (100) มากเมื่อเทียบกับเธรดที่กำลังทํางาน (120) เธรดที่กำลังทํางานมีแนวโน้มที่จะปิดใช้การแย่งสิทธิ์หรือการขัดจังหวะหากเธรดที่พร้อมใช้งานไม่ทํางานภายใน 2 jiffies
  • หากเธรดที่พร้อมใช้งานและเธรดที่ทำงานอยู่มีลําดับความสําคัญเดียวกัน เธรดที่ทำงานอยู่มีแนวโน้มที่จะปิดใช้การแย่งสิทธิ์หรือขัดจังหวะหากเธรดที่พร้อมใช้งานไม่ทํางานภายใน 20 มิลลิวินาที

โปรดทราบว่าการเรียกใช้ตัวแฮนเดิลการขัดจังหวะจะทําให้คุณไม่สามารถให้บริการการขัดจังหวะอื่นๆ ซึ่งจะปิดใช้การแย่งสิทธิ์ด้วย


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

ใช้คิวงานไม่ถูกต้อง

ตัวแฮนเดิลการขัดจังหวะมักต้องทํางานที่ทํางานนอกบริบทการขัดจังหวะได้ ซึ่งช่วยให้สามารถส่งงานไปยังเธรดต่างๆ ในเคอร์เนลได้ นักพัฒนาไดรเวอร์อาจสังเกตเห็นว่าเคอร์เนลมีฟังก์ชันการทำงานแบบอะซิงโครนัสของงานทั่วทั้งระบบที่สะดวกมากที่เรียกว่า workqueues และอาจใช้ฟังก์ชันดังกล่าวสำหรับงานที่เกี่ยวข้องกับการขัดจังหวะ

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

ไดร์เวอร์ที่ต้องจัดการงานแบบขัดจังหวะภายในเธรดแยกต่างหากควรสร้าง kthread SCHED_FIFO ของตนเองแทนที่จะใช้ workqueue หากต้องการความช่วยเหลือในการทำเช่นนี้ด้วยฟังก์ชัน kthread_work โปรดดูแพตช์นี้

การแย่งกันล็อกเฟรมเวิร์ก

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

การช่วงชิงการล็อก Binder

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

Android 6.0 มีแพตช์หลายรายการเพื่อปรับปรุงลักษณะการทำงานนี้ด้วยการปิดใช้การแย่งสิทธิ์ขณะที่ล็อก Binder การดำเนินการนี้ปลอดภัยก็เพราะ Binder ควรล็อกไว้เป็นเวลา 2-3 ไมโครวินาทีของรันไทม์จริง ซึ่งช่วยปรับปรุงประสิทธิภาพได้อย่างมากในสถานการณ์ที่ไม่มีการแข่งขัน และป้องกันการแย่งชิงโดยการป้องกันการสลับตัวจัดตารางเวลาส่วนใหญ่ขณะที่การล็อก Binder ค้างอยู่ อย่างไรก็ตาม ระบบปิดใช้การแย่งสิทธิ์ไม่ได้ตลอดรันไทม์ของการถือครองล็อก Binder ซึ่งหมายความว่ามีการเปิดใช้การแย่งสิทธิ์สำหรับฟังก์ชันที่อาจหยุดทำงานชั่วคราว (เช่น copy_from_user) ซึ่งอาจทำให้เกิดการแย่งสิทธิ์แบบเดียวกับเคสเดิม เมื่อเราส่งการแก้ไขไปยังทีมพัฒนาซอฟต์แวร์ ทางทีมก็บอกเราทันทีว่านี่เป็นแนวคิดที่แย่ที่สุดในประวัติศาสตร์ (เราเห็นด้วยกับพวกเขา แต่เราก็ไม่สามารถโต้แย้งประสิทธิภาพของการแพตช์ในการป้องกันการกระตุกได้เช่นกัน)

การแย่งชิง fd ภายในกระบวนการ

กรณีนี้เกิดขึ้นได้น้อยมาก แต่ปัญหาอาจไม่ได้เกิดจากเรื่องนี้

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

เราไม่สามารถทําให้เกิดปัญหานี้ซ้ำในสถานการณ์อื่นๆ แต่เราขอชี้ให้เห็นว่าเป็นสาเหตุที่อาจทำให้เกิดปัญหาด้านประสิทธิภาพขณะติดตาม

การเปลี่ยนสถานะ CPU ที่ไม่ได้ใช้งานที่ไม่จำเป็น

เมื่อจัดการกับ IPC โดยเฉพาะไปป์ไลน์แบบหลายกระบวนการ คุณมักจะเห็นลักษณะการทำงานรันไทม์ที่หลากหลายดังต่อไปนี้

  1. เทรด A ทำงานบน CPU 1
  2. เทรด A ปลุกเทรด B
  3. เทรด B เริ่มทํางานบน CPU 2
  4. เทรด A จะเข้าสู่โหมดสลีปทันทีเพื่อให้เทรด B ปลุกเมื่อเทรด B ทำงานปัจจุบันเสร็จแล้ว

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

ก่อนอื่น ให้ใช้ฟังก์ชัน wake_up_interruptible_sync() ในไดรเวอร์เคอร์เนลและรองรับสิ่งนี้จากเครื่องมือจัดตารางที่กำหนดเอง โปรดทำตามข้อกำหนดนี้ ไม่ใช่เพียงคำแนะนำ Binder ใช้วิธีนี้ในปัจจุบัน ซึ่งช่วยได้มากในเรื่องธุรกรรม Binder แบบซิงค์โครนัสเพื่อหลีกเลี่ยงการเปลี่ยนสถานะ CPU ที่ไม่ได้ใช้งานโดยไม่จำเป็น

ประการที่ 2 ตรวจสอบว่าเวลาการเปลี่ยนสถานะ cpuidle ของคุณเป็นจริงและ cpuidlegovernor พิจารณาเวลาเหล่านี้อย่างถูกต้อง หาก SOC ทำงานหนักในสถานะการทำงานแบบไม่ใช้พลังงานมากที่สุดและไม่ได้ทำงานแบบไม่ใช้พลังงานมากที่สุด คุณจะประหยัดพลังงานไม่ได้

การบันทึก

การบันทึกจะกินรอบของ CPU หรือหน่วยความจำ ดังนั้นอย่าสแปมบัฟเฟอร์บันทึก ค่าใช้จ่ายในการบันทึกจะวนรอบในแอป (โดยตรง) และในเดรัมบันทึก นำบันทึกการแก้ไขข้อบกพร่องออกก่อนจัดส่งอุปกรณ์

ปัญหาเกี่ยวกับ I/O

การดำเนินการ I/O เป็นแหล่งที่มาที่พบบ่อยของอาการกระตุก หากเธรดเข้าถึงไฟล์ที่แมปกับหน่วยความจำและหน้านั้นไม่ได้อยู่ในแคชหน้าเว็บ เธรดจะแสดงข้อผิดพลาดและอ่านหน้าเว็บจากดิสก์ ซึ่งจะบล็อกเธรด (โดยปกตินานกว่า 10 มิลลิวินาที) และหากเกิดขึ้นในเส้นทางที่สำคัญของการแสดงผล UI อาจส่งผลให้เกิดความกระตุก สาเหตุของการดำเนินการ I/O มีมากมายเกินกว่าที่จะกล่าวถึงในที่นี้ แต่โปรดดูที่ตำแหน่งต่อไปนี้เมื่อพยายามปรับปรุงลักษณะการทํางานของ I/O

  • PinnerService PinnerService ที่เพิ่มเข้ามาใน Android 7.0 จะช่วยให้เฟรมเวิร์กล็อกไฟล์บางไฟล์ในแคชหน้าเว็บได้ ซึ่งจะนําหน่วยความจําออกจากการใช้งานโดยกระบวนการอื่น แต่หากมีไฟล์บางไฟล์ที่ทราบล่วงหน้าว่าจะใช้เป็นประจำ การใช้ mlock กับไฟล์เหล่านั้นก็อาจมีประสิทธิภาพ

    ในอุปกรณ์ Pixel และ Nexus 6P ที่ใช้ Android 7.0 เราได้ล็อกไฟล์ 4 รายการต่อไปนี้
    • /system/framework/arm64/boot-framework.oat
    • /system/framework/oat/arm64/services.odex
    • /system/framework/arm64/boot.oat
    • /system/framework/arm64/boot-core-libart.oat
    แอปส่วนใหญ่และ system_server ใช้ไฟล์เหล่านี้อยู่ตลอดเวลา ดังนั้นไฟล์เหล่านี้จึงไม่ควรถูกย้ายออก โดยเฉพาะอย่างยิ่ง เราพบว่าหากมีการเลื่อนหน้ารายการใดรายการหนึ่งออก ระบบจะเลื่อนหน้ารายการนั้นกลับเข้ามาและทำให้เกิดอาการกระตุกเมื่อเปลี่ยนจากแอปที่มีขนาดใหญ่
  • การเข้ารหัส สาเหตุที่เป็นไปได้อีกประการหนึ่งของปัญหา I/O เราพบว่าการเข้ารหัสในบรรทัดมีประสิทธิภาพดีที่สุดเมื่อเทียบกับการเข้ารหัสที่ใช้ CPU หรือการใช้บล็อกฮาร์ดแวร์ที่เข้าถึงได้ผ่าน DMA ที่สำคัญที่สุดคือการเข้ารหัสในบรรทัดช่วยลดความผันผวนที่เกี่ยวข้องกับ I/O โดยเฉพาะเมื่อเปรียบเทียบกับการเข้ารหัสที่ใช้ CPU เนื่องจากการดึงข้อมูลไปยังแคชหน้าเว็บมักจะอยู่ในเส้นทางที่มีความสำคัญของการเรนเดอร์ UI การเข้ารหัสที่ใช้ CPU จึงเพิ่มภาระของ CPU ในเส้นทางที่สำคัญ ซึ่งจะเพิ่มความผันผวนมากกว่าการดึงข้อมูล I/O เพียงอย่างเดียว

    เครื่องมือการเข้ารหัสฮาร์ดแวร์ที่ใช้ DMA มีปัญหาคล้ายกัน เนื่องจากเคอร์เนลต้องใช้รอบการจัดการงานนั้นแม้ว่าจะมีงานสำคัญอื่นๆ ที่พร้อมทำงานก็ตาม เราขอแนะนําอย่างยิ่งให้ผู้ให้บริการ SOC ที่สร้างฮาร์ดแวร์ใหม่รวมการรองรับการเข้ารหัสในบรรทัด

การบรรจุงานขนาดเล็กอย่างถี่สูง

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

แคชหน้าเว็บทำงานหนัก

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

วิธีหนึ่งในการระบุปัญหานี้คือใช้การติดตามระบบโดยใช้แท็ก pagecache และส่งการติดตามนั้นไปยังสคริปต์ที่ system/extras/pagecache/pagecache.py pagecache.py จะแปลคำขอแต่ละรายการเพื่อแมปไฟล์ลงในแคชหน้าเว็บเป็นสถิติรวมต่อไฟล์ หากพบว่ามีจำนวนไบต์ของไฟล์ที่อ่านมากกว่าขนาดไฟล์ทั้งหมดบนดิสก์ แสดงว่าคุณกำลังประสบปัญหาแคชหน้าเว็บทำงานหนัก

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

ไม่มีการแก้ไขที่สมบูรณ์แบบสำหรับปัญหาการแคชหน้าเว็บที่ทำงานหนักเกินไป แต่มีวิธีบางอย่างที่อาจช่วยปรับปรุงปัญหานี้ในอุปกรณ์หนึ่งๆ ได้

  • ใช้หน่วยความจำน้อยลงในกระบวนการถาวร ยิ่งกระบวนการที่ทำงานอยู่ใช้หน่วยความจำน้อยเท่าใด แอปและแคชหน้าเว็บก็จะมีหน่วยความจำมากขึ้นเท่านั้น
  • ตรวจสอบการแยกพื้นที่สำหรับอุปกรณ์ของคุณเพื่อให้แน่ใจว่าไม่ได้นำหน่วยความจำออกจากระบบปฏิบัติการโดยไม่จำเป็น เราเคยเห็นกรณีที่การแยกส่วนที่ใช้สำหรับการแก้ไขข้อบกพร่องถูกทิ้งไว้โดยไม่ได้ตั้งใจในการกำหนดค่าเคอร์เนลที่ใช้งานจริง ซึ่งทำให้หน่วยความจำใช้ไปหลายสิบเมกะไบต์ ซึ่งอาจส่งผลให้แคชหน้าเว็บทำงานหนักหรือไม่ทำงานหนัก โดยเฉพาะในอุปกรณ์ที่มีหน่วยความจําน้อย
  • หากคุณเห็นแคชหน้าเว็บทำงานหนักใน system_server ในไฟล์ที่สำคัญ ให้ลองปักหมุดไฟล์เหล่านั้น ซึ่งจะเพิ่มภาระของหน่วยความจำในส่วนอื่นๆ แต่อาจแก้ไขลักษณะการทํางานได้มากพอที่จะหลีกเลี่ยงการกระชาก
  • ปรับ lowmemorykiller ใหม่เพื่อพยายามเพิ่มหน่วยความจำว่างให้มากขึ้น เกณฑ์ของ lowmemorykiller จะอิงตามทั้งหน่วยความจําว่างสัมบูรณ์และแคชหน้าเว็บ ดังนั้นการเพิ่มขึ้นของเกณฑ์ที่ระบบจะหยุดกระบวนการที่ระดับ oom_adj หนึ่งๆ อาจเป็นผลให้ระบบทำงานได้ดีขึ้น แต่แอปที่ทำงานอยู่เบื้องหลังจะหยุดทำงานมากขึ้น
  • ลองใช้ ZRAM เราใช้ ZRAM ใน Pixel แม้ว่า Pixel จะมี 4 GB ก็ตาม เนื่องจากอาจช่วยจัดการกับหน้าเว็บที่ไม่ค่อยได้ใช้