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

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

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

พื้นหลัง

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

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

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

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

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

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

ในการใช้งานเสียงของ Android การผกผันของลำดับความสำคัญมักเกิดขึ้นในสถานที่เหล่านี้ ดังนั้นคุณควรให้ความสนใจที่นี่:

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

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

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

  • ปิดการใช้งานการขัดจังหวะ
  • mutexes การสืบทอดลำดับความสำคัญ

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

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

เทคนิคที่ใช้โดย Android

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

เรายังใช้ การดำเนินการปรมาณู เช่น:

  • เพิ่มขึ้น
  • ระดับบิต "หรือ"
  • ระดับบิต "และ"

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

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

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

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

อัลกอริธึมที่ไม่ปิดกั้น

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

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

  • กรอบ/av/รวม/สื่อ/nbaio/
  • เฟรมเวิร์ก/av/media/libnbaio/
  • เฟรมเวิร์ก/av/services/audioflinger/StateQueue*

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

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

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

เครื่องมือ

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

คำสุดท้าย

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