หลีกเลี่ยงการเปลี่ยนลําดับความสําคัญ

บทความนี้อธิบายวิธีที่ระบบเสียงของ Android พยายามหลีกเลี่ยง การกลับลำดับความสำคัญ และเน้นเทคนิคที่คุณนำไปใช้ได้เช่นกัน

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

ฉากหลัง

เรากำลังปรับโครงสร้างเซิร์ฟเวอร์เสียง AudioFlinger ของ Android และการใช้งานไคลเอ็นต์ AudioTrack/AudioRecord เพื่อลดเวลาในการตอบสนอง การดำเนินการนี้เริ่มขึ้นใน Android 4.1 และมีการปรับปรุงเพิ่มเติม ใน 4.2, 4.3, 4.4 และ 5.0

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

การกลับลำดับความสำคัญ

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

ในระบบเสียง การกลับลำดับความสำคัญมักจะปรากฏเป็นเสียงรบกวน (เสียงคลิก เสียงป๊อป เสียงขาดหาย) เสียงซ้ำ เมื่อใช้บัฟเฟอร์แบบวงกลม หรือความล่าช้าในการตอบสนองต่อคำสั่ง

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

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

  • ระหว่างเธรดมิกเซอร์ปกติกับเธรดมิกเซอร์ด่วนใน AudioFlinger
  • ระหว่างเธรดการเรียกกลับของแอปพลิเคชันสำหรับ AudioTrack ที่รวดเร็วกับ เธรดมิกเซอร์ที่รวดเร็ว (ทั้ง 2 เธรดมีลำดับความสำคัญสูง แต่มีลำดับความสำคัญแตกต่างกันเล็กน้อย)
  • ระหว่างเธรดการเรียกกลับของแอปพลิเคชันสำหรับ AudioRecord ที่รวดเร็วและ เธรดการจับภาพที่รวดเร็ว (คล้ายกับก่อนหน้า)
  • ภายในเลเยอร์นามธรรมของฮาร์ดแวร์ (HAL) ของเสียง เช่น สำหรับการโทรหรือการตัดเสียงก้อง
  • ภายในไดรเวอร์เสียงในเคอร์เนล
  • ระหว่างเธรดการเรียกกลับ AudioTrack หรือ AudioRecord กับเธรดแอปอื่นๆ (เราควบคุมไม่ได้)

วิธีแก้ปัญหาทั่วไป

โซลูชันทั่วไป ได้แก่

  • การปิดใช้การขัดจังหวะ
  • Mutex การรับค่าลำดับความสำคัญ

การปิดใช้การขัดจังหวะไม่สามารถทำได้ในพื้นที่ผู้ใช้ของ Linux และใช้ไม่ได้กับ Symmetric Multi-Processors (SMP)

ระบบเสียงไม่ได้ใช้การรับค่าลำดับความสำคัญ futexes (fast user-space mutexes) เนื่องจากมีขนาดค่อนข้างใหญ่ และต้องอาศัยไคลเอ็นต์ที่เชื่อถือได้

เทคนิคที่ Android ใช้

การทดสอบเริ่มต้นด้วย "try lock" และล็อกด้วยการหมดเวลา ซึ่งเป็นตัวแปรการบล็อกแบบไม่บล็อกและแบบจำกัดของ การดำเนินการล็อก Mutex การลองใช้การล็อกและการล็อกที่มีการหมดเวลาได้ผลค่อนข้างดี แต่ก็ มีแนวโน้มที่จะเกิดโหมดการทำงานที่ล้มเหลวที่คลุมเครือ 2 อย่าง ได้แก่ เซิร์ฟเวอร์ไม่รับประกันว่าจะเข้าถึงสถานะที่แชร์ได้หาก ไคลเอ็นต์ไม่ว่าง และการหมดเวลาสะสมอาจ นานเกินไปหากมีการล็อกที่ไม่เกี่ยวข้องเป็นลำดับยาว ซึ่งทั้งหมดหมดเวลา

นอกจากนี้ เรายังใช้การดำเนินการแบบอะตอม เช่น

  • การเพิ่มขึ้น
  • bitwise "or"
  • "และ" ระดับบิต

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

หมายเหตุ: การดำเนินการแบบอะตอมและการโต้ตอบกับ Memory Barrier เป็นสิ่งที่มักถูกเข้าใจผิดและใช้ไม่ถูกต้อง เราได้รวมวิธีการเหล่านี้ไว้ที่นี่เพื่อให้ข้อมูลครบถ้วน แต่ขอแนะนำให้คุณอ่านบทความ ข้อมูลเบื้องต้นเกี่ยวกับ SMP สำหรับ Android เพื่อดูข้อมูลเพิ่มเติมด้วย

เรายังคงมีและใช้เครื่องมือส่วนใหญ่ข้างต้น และได้เพิ่มเทคนิคต่อไปนี้เมื่อเร็วๆ นี้

  • ใช้คิว FIFO แบบไม่บล็อกที่มีผู้อ่านและผู้เขียนรายเดียว สำหรับข้อมูล
  • พยายามคัดลอกสถานะแทนที่จะแชร์สถานะระหว่างโมดูลที่มีลำดับความสำคัญสูงและต่ำ
  • เมื่อจำเป็นต้องแชร์สถานะ ให้จำกัดสถานะไว้ที่ ขนาดสูงสุด คำ ที่เข้าถึงได้แบบอะตอมในการดำเนินการแบบบัสเดียว โดยไม่ต้องลองใหม่
  • สำหรับสถานะแบบหลายคำที่ซับซ้อน ให้ใช้คิวสถานะ คิวสถานะ โดยพื้นฐานแล้วเป็นคิว FIFO แบบอ่านคนเดียวเขียนคนเดียวที่ไม่บล็อก ซึ่งใช้สำหรับสถานะแทนข้อมูล ยกเว้นว่าผู้เขียนจะยุบ การพุชที่อยู่ติดกันเป็นการพุชเดียว
  • ให้ความสนใจกับ Memory Barrier เพื่อความถูกต้องของ SMP
  • เชื่อถือได้ แต่ต้องตรวจสอบ เมื่อแชร์ สถานะ ระหว่างกระบวนการ อย่า ถือว่าสถานะมีรูปแบบที่ถูกต้อง เช่น ตรวจสอบว่าดัชนี อยู่ในขอบเขต การยืนยันนี้ไม่จำเป็นต้องใช้ระหว่างเธรด ในกระบวนการเดียวกัน หรือระหว่างกระบวนการที่เชื่อถือซึ่งกันและกัน (ซึ่ง มักจะมี UID เดียวกัน) นอกจากนี้ยังไม่จำเป็นสำหรับข้อมูลที่แชร์ เช่น เสียง PCM ที่การเสียหายไม่มีผล

อัลกอริทึมที่ไม่บล็อก

อัลกอริทึมแบบไม่บล็อก เป็นหัวข้อที่ได้รับการศึกษาเมื่อเร็วๆ นี้เป็นอย่างมาก แต่เราพบว่าคิว FIFO แบบผู้อ่านคนเดียวและผู้เขียนคนเดียวมีความซับซ้อนและมีแนวโน้มที่จะเกิดข้อผิดพลาด

ตั้งแต่ Android 4.2 เป็นต้นไป คุณจะเห็นคลาสแบบไม่บล็อก แบบอ่าน/เขียนรายการเดียวของเราได้ในตำแหน่งต่อไปนี้

  • frameworks/av/include/media/nbaio/
  • frameworks/av/media/libnbaio/
  • frameworks/av/services/audioflinger/StateQueue*

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

สำหรับนักพัฒนาแอป โค้ดแอปพลิเคชัน OpenSL ES ตัวอย่างบางส่วนควรได้รับการอัปเดตเพื่อใช้อัลกอริทึมที่ไม่บล็อกหรืออ้างอิงไลบรารีโอเพนซอร์สที่ไม่ใช่ Android

เราได้เผยแพร่ตัวอย่างการติดตั้งใช้งาน FIFO แบบไม่บล็อกซึ่งออกแบบมาโดยเฉพาะสำหรับ โค้ดแอปพลิเคชัน ดูไฟล์ต่อไปนี้ที่อยู่ในไดเรกทอรีแหล่งที่มาของแพลตฟอร์ม frameworks/av/audio_utils

เครื่องมือ

เท่าที่เราทราบ ไม่มีเครื่องมืออัตโนมัติสำหรับ การค้นหาการกลับลำดับความสำคัญ โดยเฉพาะก่อนที่จะเกิดขึ้น เครื่องมือวิเคราะห์โค้ดแบบคงที่สำหรับการวิจัยบางอย่างสามารถค้นหาการกลับลำดับความสำคัญได้หากเข้าถึงฐานของโค้ดทั้งหมดได้ แน่นอนว่าหากมีโค้ดผู้ใช้ที่กำหนดเอง (ดังเช่นในที่นี้สำหรับแอปพลิเคชัน) หรือเป็นโค้ดเบสขนาดใหญ่ (เช่น สำหรับเคอร์เนล Linux และไดรเวอร์อุปกรณ์) การวิเคราะห์แบบคงที่อาจทำได้ยาก สิ่งสำคัญที่สุดคือการ อ่านโค้ดอย่างละเอียดและทำความเข้าใจระบบทั้งหมด และการโต้ตอบ เครื่องมือต่างๆ เช่น systrace และ ps -t -p มีประโยชน์ในการดูการกลับลำดับความสำคัญหลังจากที่เกิดขึ้น แต่ไม่ได้แจ้งให้คุณทราบล่วงหน้า

ข้อสรุปสุดท้าย

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