Öncelik tersine çevirme

Bu makalede, Android'in ses sisteminin öncelik ters çevirmeyi nasıl önlemeye çalıştığı açıklanmakta ve sizin de kullanabileceğiniz teknikler vurgulanmaktadır.

Bu teknikler, yüksek performanslı ses uygulamaları geliştirenler, OEM'ler ve ses HAL'si uygulayan SoC sağlayıcılar için faydalı olabilir. Bu tekniklerin uygulanmasının, özellikle ses bağlamı dışında kullanıldığında aksaklıkları veya diğer hataları önleyeceğini garanti etmediğini lütfen unutmayın. Sonuçlarınız farklılık gösterebilir. Bu nedenle, kendi değerlendirmenizi ve testinizi yapmanız gerekir.

Arka plan

Gecikmeyi azaltmak için Android AudioFlinger ses sunucusu ve AudioTrack/AudioRecord istemci uygulaması yeniden yapılandırılıyor. Bu çalışma Android 4.1'de başladı ve 4.2, 4.3, 4.4 ve 5.0 sürümlerinde daha da geliştirildi.

Bu daha düşük gecikme süresine ulaşmak için sistemde birçok değişiklik yapılması gerekti. Önemli bir değişiklik, CPU kaynaklarını zaman açısından kritik olan iş parçacıklarına daha öngörülebilir bir planlama politikasıyla atamaktır. Güvenilir planlama, ses arabellek boyutlarının ve sayılarının azaltılmasına olanak tanırken yetersiz ve aşırı arabellek kullanımını önlemeye devam eder.

Öncelik ters çevirme

Önceliğin ters dönmesi, gerçek zamanlı sistemlerin klasik bir hata modudur. Bu modda, daha yüksek öncelikli bir görev, daha düşük öncelikli bir görevin bir kaynağı (ör. mutex ile korunan paylaşılan durum) serbest bırakmasını beklerken sınırsız bir süre boyunca engellenir.

Ses sisteminde öncelik ters çevirme genellikle aksaklık (tıklama, patlama, kesinti), dairesel arabellekler kullanıldığında tekrarlanan ses veya komuta yanıt vermede gecikme olarak kendini gösterir.

Öncelik ters çevirme için yaygın bir geçici çözüm, ses arabelleği boyutlarını artırmaktır. Ancak bu yöntem gecikmeyi artırır ve sorunu çözmek yerine yalnızca gizler. Aşağıda gösterildiği gibi öncelik ters çevirmeyi anlamak ve önlemek daha iyidir.

Android ses uygulamasında öncelik tersine çevrilmesi en çok bu yerlerde meydana gelir. Bu nedenle, dikkatinizi şu noktalara odaklamanız gerekir:

  • AudioFlinger'daki normal karıştırıcı iş parçacığı ile hızlı karıştırıcı iş parçacığı arasında
  • Hızlı bir AudioTrack için uygulama geri çağırma iş parçacığı ile hızlı karıştırıcı iş parçacığı arasında (her ikisi de yüksek önceliğe sahiptir ancak öncelikleri biraz farklıdır)
  • Hızlı AudioRecord için uygulama geri çağırma iş parçacığı ile hızlı yakalama iş parçacığı arasında (öncekiyle benzer)
  • Ses donanımı soyutlama katmanı (HAL) uygulamasında (ör. telefon veya yankı giderme için)
  • çekirdekteki ses sürücüsünde
  • AudioTrack veya AudioRecord geri çağırma iş parçacığı ile diğer uygulama iş parçacıkları arasında (bu bizim kontrolümüz dışındadır)

Sık karşılaşılan çözümler

Genel çözümler şunlardır:

  • kesintileri devre dışı bırakma
  • öncelik devralma mutex'leri

Kesmeleri devre dışı bırakmak Linux kullanıcı alanında mümkün değildir ve Simetrik Çoklu İşlemciler (SMP) için çalışmaz.

Öncelik devralma futexes (hızlı kullanıcı alanı mutex'leri), nispeten ağır oldukları ve güvenilir bir istemciye bağlı oldukları için ses sisteminde kullanılmaz.

Android tarafından kullanılan teknikler

"Try lock" ile başlayan ve zaman aşımıyla kilitlenen denemeler. Bunlar, karşılıklı dışlama kilidi işleminin engellemeyen ve sınırlı engelleme varyantlarıdır. Zaman aşımıyla kilitleme ve kilidi açma yöntemi oldukça iyi çalışıyordu ancak birkaç belirsiz hata moduna karşı hassastı: İstemci meşgul olduğunda sunucunun paylaşılan duruma erişebileceği garanti edilmiyordu ve birbiriyle alakasız, zaman aşımına uğrayan uzun bir kilit dizisi varsa kümülatif zaman aşımı çok uzun olabiliyordu.

Ayrıca aşağıdaki gibi atomik işlemleri de kullanırız:

  • artır
  • bit düzeyinde "veya"
  • Bit düzeyinde "ve"

Bunların tümü önceki değeri döndürür ve gerekli SMP engellerini içerir. Dezavantajı, sınırsız yeniden deneme gerektirebilmeleridir. Pratikte, yeniden denemelerin sorun olmadığını gördük.

Not: Atomik işlemler ve bunların bellek bariyerleriyle etkileşimleri kötü bir şekilde yanlış anlaşılır ve yanlış kullanılır. Bu yöntemleri eksiksiz olması için buraya ekliyoruz ancak daha fazla bilgi için Android için SMP Primer başlıklı makaleyi de okumanızı öneririz.

Yukarıdaki araçların çoğunu hâlâ kullanıyoruz ve kısa süre önce şu teknikleri ekledik:

  • Veriler için engellemeyen tek okuyuculu tek yazıcılı FIFO kuyruklarını kullanın.
  • Yüksek ve düşük öncelikli modüller arasında durumu paylaşmak yerine durumu kopyalamayı deneyin.
  • Durumun paylaşılması gerektiğinde, durumu yeniden deneme olmadan tek veri yolu işleminde atomik olarak erişilebilen maksimum boyuttaki kelimeyle sınırlayın.
  • Karmaşık çok kelimeli durum için durum sırası kullanın. Durum kuyruğu, temelde bitişik gönderimleri tek bir gönderimde daraltan yazıcı dışında, veriler yerine durum için kullanılan, engellemeyen tek okuyuculu tek yazıcılı FIFO kuyruğudur.
  • SMP doğruluğu için bellek bariyerlerine dikkat edin.
  • Güvenin ancak doğrulayın. İşlemler arasında durum paylaşırken durumun iyi biçimlendirilmiş olduğunu varsaymayın. Örneğin, dizinlerin sınırlar içinde olup olmadığını kontrol edin. Bu doğrulama, aynı işlemdeki iş parçacıkları arasında ve karşılıklı güvenen işlemler (genellikle aynı UID'ye sahip olanlar) arasında gerekli değildir. Ayrıca, bozulmanın önemsiz olduğu PCM ses gibi paylaşılan verilerde de gereksizdir.

Engellemeyen algoritmalar

Engellemeyen algoritmalar son zamanlarda çok fazla çalışmaya konu olmuştur. Ancak tek okuyuculu tek yazıcılı FIFO kuyrukları hariç olmak üzere bunların karmaşık ve hataya açık olduğunu gördük.

Android 4.2'den itibaren, engellemeyen, tek okuyuculu/yazıcılı sınıflarımızı şu konumlarda bulabilirsiniz:

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

Bunlar AudioFlinger için özel olarak tasarlanmıştır ve genel amaçlı değildir. Engellemeyen algoritmaların hata ayıklaması zordur. Bu kodu model olarak kullanabilirsiniz. Ancak hatalar olabileceğini ve sınıfların başka amaçlar için uygun olacağının garanti edilmediğini unutmayın.

Geliştiriciler için, örnek OpenSL ES uygulama kodunun bir kısmı, engellemeyen algoritmalar kullanacak veya Android dışı bir açık kaynak kitaplığına referans verecek şekilde güncellenmelidir.

We have published an example non-blocking FIFO implementation that is specifically designed for application code. Platform kaynak dizininde bulunan bu dosyalara göz atın frameworks/av/audio_utils:

Araçlar

Bildiğimiz kadarıyla, özellikle gerçekleşmeden önce öncelik ters çevirme sorununu bulmaya yönelik otomatik araçlar yoktur. Bazı araştırma statik kod analizi araçları, kod tabanının tamamına erişebiliyorsa öncelik ters çevirmelerini bulabilir. Elbette, rastgele kullanıcı kodu söz konusuysa (burada uygulamada olduğu gibi) veya büyük bir kod tabanı varsa (Linux çekirdeği ve cihaz sürücülerinde olduğu gibi) statik analiz pratik olmayabilir. En önemli şey, kodu çok dikkatli bir şekilde okuyup tüm sistemi ve etkileşimleri iyi anlamaktır. systrace ve ps -t -p gibi araçlar, öncelik tersine çevrilmesi gerçekleştikten sonra bunu görmek için kullanışlıdır ancak önceden bilgi vermez.

Son olarak

Tüm bu tartışmalardan sonra mutex'lerden korkmayın. Mutex'ler, normal kullanımda ve zaman açısından kritik olmayan kullanım alanlarında doğru şekilde kullanılıp uygulandığında en iyi yardımcınızdır. Ancak yüksek ve düşük öncelikli görevler arasında ve zamana duyarlı sistemlerde karşılıklı dışlamalar sorunlara neden olma olasılığı daha yüksektir.