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

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