บทความนี้อธิบายวิธีที่ระบบเสียงของ 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 มีแนวโน้มที่จะทำให้เกิดปัญหามากกว่า