บทความนี้อธิบายวิธีที่ระบบเสียงของ 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 และไม่ทำงานกับระบบที่มีหลายโปรเซสเซอร์แบบสมมาตร (SMP)
ระบบเสียงไม่ได้ใช้การสืบทอดลําดับความสําคัญ futex (Mutex พื้นที่ผู้ใช้ที่รวดเร็ว) เนื่องจากมีขนาดใหญ่เมื่อเทียบกับระบบอื่นๆ และเนื่องจากระบบเสียงต้องอาศัยไคลเอ็นต์ที่เชื่อถือได้
เทคนิคที่ Android ใช้
การทดสอบเริ่มต้นด้วย "try lock" และล็อกที่มีระยะหมดเวลา การดำเนินการเหล่านี้เป็นตัวแปรแบบไม่บล็อกและแบบบล็อกแบบจำกัดของการดำเนินการล็อกมิวเทค ลองใช้การล็อกและการล็อกที่มีระยะหมดเวลาทำงานได้ดีพอสมควร แต่อาจเกิดข้อผิดพลาดได้ 2 แบบ ซึ่งก็คือไม่มีการรับประกันว่าเซิร์ฟเวอร์จะเข้าถึงสถานะที่แชร์ได้หากไคลเอ็นต์ไม่ว่าง และระยะหมดเวลาที่สะสมอาจนานเกินไปหากมีการล็อกที่ไม่เกี่ยวข้องกันหลายรายการที่หมดเวลาพร้อมกัน
นอกจากนี้ เรายังใช้การดำเนินการแบบอะตอม เช่น
- การเพิ่มขึ้น
- "หรือ" แบบบิต
- "และ" แบบบิต
ทั้งหมดนี้จะแสดงค่าก่อนหน้าและรวมสิ่งกีดขวาง SMP ที่จำเป็น ข้อเสียคืออาจต้องมีการลองใหม่แบบไม่จำกัด จากการทดสอบ เราพบว่าการลองอีกครั้งไม่ใช่ปัญหา
หมายเหตุ: การดำเนินการแบบอะตอมและการโต้ตอบกับสิ่งกีดขวางหน่วยความจำมักถูกเข้าใจผิดและใช้งานอย่างไม่ถูกต้อง เราระบุวิธีการเหล่านี้ไว้เพื่อให้ครบถ้วน แต่ขอแนะนำให้คุณอ่านบทความ ข้อมูลเบื้องต้นเกี่ยวกับ SMP สําหรับ Android เพื่อดูข้อมูลเพิ่มเติมด้วย
เรายังคงมีและใช้เครื่องมือข้างต้นส่วนใหญ่อยู่ และเพิ่งเพิ่มเทคนิคต่อไปนี้
- ใช้คิว FIFO แบบอ่านอย่างเดียวเขียนอย่างเดียวแบบไม่บล็อกสำหรับข้อมูล
- ลองคัดลอกสถานะแทนแชร์สถานะระหว่างโมดูลที่มีลําดับความสําคัญสูงและต่ำ
- เมื่อจำเป็นต้องแชร์สถานะ ให้จำกัดสถานะเป็นคำที่มีขนาดสูงสุดซึ่งเข้าถึงแบบอะตอมในการดำเนินการแบบบัสเดียวได้โดยไม่ต้องลองใหม่
- สำหรับสถานะแบบหลายคำที่ซับซ้อน ให้ใช้คิวสถานะ โดยพื้นฐานแล้ว คิวสถานะเป็นเพียงคิว FIFO แบบอ่านอย่างเดียวและเขียนอย่างเดียวแบบไม่บล็อกที่ใช้สำหรับสถานะแทนข้อมูล ยกเว้นผู้เขียนจะรวมการพุชที่อยู่ติดกันเป็นพุชเดียว
- โปรดคำนึงถึงบัวเรียมหน่วยความจำเพื่อความถูกต้องของ SMP
- เชื่อถือ แต่ต้องยืนยัน เมื่อแชร์สถานะระหว่างกระบวนการต่างๆ อย่าคิดว่าสถานะดังกล่าวอยู่ในรูปแบบที่ถูกต้อง เช่น ตรวจสอบว่าดัชนีอยู่ภายในขอบเขต ไม่จำเป็นต้องทำการยืนยันนี้ระหว่างชุดข้อความในกระบวนการเดียวกัน ระหว่างกระบวนการที่เชื่อถือซึ่งกันและกัน (ซึ่งมักจะมี UID เดียวกัน) นอกจากนี้ การตรวจสอบความถูกต้องก็ไม่จำเป็นสำหรับข้อมูลที่แชร์ เช่น เสียง PCM ที่การเสียหายไม่สำคัญ
อัลกอริทึมแบบไม่บล็อก
อัลกอริทึมแบบไม่บล็อกเป็นหัวข้อที่มีการวิจัยกันมากเมื่อเร็วๆ นี้ แต่เราพบว่าคิว FIFO แบบผู้อ่านคนเดียวและผู้เขียนคนเดียวมีความซับซ้อนและเกิดข้อผิดพลาดได้ง่าย ยกเว้นคิว 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 Mutexes เป็นเครื่องมือที่มีประโยชน์สําหรับการใช้งานทั่วไป เมื่อใช้และติดตั้งใช้งานอย่างถูกต้องใน Use Case ทั่วไปที่ไม่จําเป็นต้องดำเนินการอย่างรวดเร็ว แต่ระหว่างงานที่มีลำดับความสำคัญสูงและต่ำ และในระบบที่คำนึงถึงเวลา Mutex มีแนวโน้มที่จะทำให้เกิดปัญหามากกว่า