2015-01-02 27 views
5

Ses yazımı yazarken, internetteki birçok kişi bellek ayırma veya engelleme kodunu kullanmamanın, yani kilitlerin kullanılmaması gerektiğini belirtir. Bunlar deterministik olmayan bir durum olduğundan, çıktı tamponunun yetersiz kalmasına ve sesin bozulmasına neden olabilir.Multithreaded Realtime ses programlama - Engellemek veya engellemek için

Real Time Audio Progrmaming

I video yazılımı geç zaman, genel olarak, her iki kullanmak, yani öbek video çerçeveleri ayrılması ve kilit ve koşullu değişkenleri (sınırlı tamponlar) kullanılarak parçacıkları arasında geçen. Her işlem için ayrı bir iş parçacığı olarak kullanılabilen gücü seviyorum, yazılımın en iyi performansı veren çekirdeklerin her birini maksimize etmesini sağlarım.

Ses ile benzer bir şey yapmak istiyorum, ancak iş parçacıkları arasında belki 100 örnek kareler geçiyor, ancak iki sorun var.

  1. Bellek ayırma kullanmadan çerçeveleri nasıl oluştururum? Önceden tahsis edilmiş bir çerçeve havuzunu kullanabileceğimi sanıyorum, ama bu karışık görünüyor.

  2. Kilit serbest kuyruğu kullanabileceğinizi biliyorum ve bu desteğin bunu yapmak için güzel bir kütüphanesi vardır. Bu, iş parçacıkları arasında paylaşım yapmanın harika bir yoludur, ancak verilerin kullanılabilir olup olmadığını görmek için sıraya sürekli bir şekilde oylama yapmak, CPU zamanının büyük bir beline benziyor.

Muteks kullanımıyla ilgili deneyimimde, muteksin kilitlendiği bölümün kısa olması şartıyla, aslında çok fazla zaman almaz.

İş parçacıkları arasında geçen ses çerçevelerini elde etmenin en iyi yolu, gecikmeyi minimumda tutmak, kaynakları israf etmemek ve göreceli olarak az belirleyici olmayan davranış kullanmaktır?

+0

Ses konusunda pek bir şey bilmiyorum, fakat geleneksel bir üretici/tüketici problemi gibi geliyor, semafor saymaya baktınız mı? – smskelley

+0

Konuya ihtiyacınız var mı? Ses verileri videodan çok daha ucuzdur. – immibis

+0

@immibis, iş parçacığınızda ne yaptığınıza bağlı. Video genellikle piksel başına çok az işlem gerçekleştirirken, ses sentezi örnek başına çok zorlu olabilir. –

cevap

4

Araştırmanızı yaptığınız gibi görünüyor! Ses bozukluklarının kök nedeni olabilecek iki ana problemi zaten belirlediniz. Soru şu: 10 yıl önce bunun ne kadar önemli olduğu ve bu günlerde sadece folklor ve kargo-kült programlama.

Fikrimi söyleyeyim: render döngüde

1. Öbek tahsisleri:

Bunlar işleme parçaları kadar küçük bağlı oldukça çok yükü olabilir. Ana suçlu, çok az çalışma süresinin her iplik başına bir yığına sahip olması, dolayısıyla, yığınla karışıklık her zaman, performansınız, işleminizdeki diğer iş parçacıklarına bağlıdır. örneğin bir GUI iplik şu anda nesneleri binlerce siliyor ve varsa - Aynı anda - Ses gerçekleme iplikten yığın erişmek Eğer önemli bir gecikme olabilir.

dağınık gelebilir ön tahsis tampon ile kendi hafıza yönetimi Yazma, ama sonunda o bir yarar kaynağı yerde gizleyebilirsiniz sadece iki fonksiyon var. Tahsis boyutlarınızı önceden bildiğiniz için, bellek yönetiminize ince ayar yapmak ve optimize etmek için çok fazla fırsat vardır. Segmentlerinizi örneğin basit bir bağlantılı liste halinde saklayabilirsiniz. Doğru yapıldığında, bu, son kullanılan arabelleği yeniden ayırmanızın yararına sahiptir. Bu tamponun önbellekte çok yüksek bir olasılıkla arı oluşturma ihtimali vardır.

sabit boyutlu ayırıcılarına sizin için işe yaramazsa

halka tamponları de bakabilirsiniz. Akış sesinin kullanım durumlarını çok iyi uyuyorlar.

2. kilitlemek için veya kilitlemek için değil: Eğer saniyede bunlardan 5000 Eğer az 1000 do tahmin eğer

söyleyebilirim

muteksi ve semafor kilitleri kullanarak bu gün gayet (PC'de, bir şeyler Raspberry Pi gibi bir şey üzerinde farklıdır). Bu aralığın altında kalırsanız, başlığın bir performans profilinde görünmesi olası değildir.

Kullanım çantasına çevrilir: Örneğin, 48kHz ses ve 100 örnek parça ile çalışıyorsanız, basit bir iki iplikli tüketici/üretici kalıbıyla yaklaşık 960 kilitleme/kilit açma işlemi gerçekleştirirsiniz. Bu aralıkta iyi. Oluşturma iş parçacığını tamamen kapamanız durumunda, kilitleme bir profil oluşturmada gösterilmeyecektir. Öte yandan sadece mevcut işlem gücünün% 5'i gibi kullanın, ancak kilitler ortaya çıkabilir, ancak bir sorununuz olmayacaktır :-)

Kilitleme daha az da bir seçenek değil, aynı zamanda İlk önce bazı kilitsiz denemeler yapan ve daha sonra sabit kilitlemeye geri dönen hibrit çözümler. Her iki dünyanın en iyilerini bu şekilde alacaksınız. İnternette bu konu hakkında okumak için çok iyi şeyler var. Her durumda

:

Onların bir kilide çalıştırırsanız, hızla bunun dışında olsun emin olmak için nazikçe olmayan GUI iş parçacığı parçacığının önceliğini yükseltmek gerekir.

https://en.wikipedia.org/wiki/Priority_inversion

1

'Ben önceden tahsis edilmiş çerçevelerin bir havuzu kullanmak herhalde ancak bu görünüyor: Bu ne Öncelik İnversiyon okumak ve bunu önlemek için neler yapabileceğini de iyi bir fikirdir dağınık '- gerçekten değil. Bir dizi kare veya bir döngüde yeni bir kare ayırın ve ardından dizinleri/işaretçileri bir engelleme kuyruğuna sürükleyin. Artık otomatik yönetilen bir çerçeve havuzunuz var. Bir kareye ihtiyaç duyduğunuzda bir tanesini açın, işiniz bittiğinde tekrar açın. Sürekli malloc/free/new/delete, şans veya hafızadan kaçma, daha basit hata ayıklama ve çerçeve akış denetimi, (havuz biterse, çerçeveler soran çerçeveler havuza geri bırakılana kadar bekler)

Bir diziyi kullanmak yeni bir döngüden daha kolay/daha güvenli/daha hızlı görünebilir, ancak tek tek karelerin yeniden düzenlenmesi avantaj sağlar - çalışma zamanında havuzdaki çerçeve sayısını kolayca değiştirebilirsiniz.

0

Um, neden iş parçacıkları arasında 100 örnek kareleri geçiyorsunuz?

44.1kHz nominal örnek hızında çalıştığınızı varsayarak ve dişler arasındaki bir seferde 100 örnek geçirdiğinizde, bu, diş değiştirme oranınızın en az 100 örnek olması gerektiğini varsayar/(44100 örnek/s * 2) . 2, hem üretici hem de tüketiciyi temsil etmektedir. Bu, gönderdiğiniz her 100 örnek için ~ 1.13 ms'lik bir zaman dilimine sahip olduğunuz anlamına gelir. Neredeyse tüm işletim sistemleri 10 ms'den daha büyük zaman dilimlerinde çalışır. Dolayısıyla, modern bir işletim sisteminde 44.1kHz'de sadece 100 örnek paylaştığınız bir ses motoru oluşturmak imkansız. Çözüm, bir sıra aracılığıyla veya daha büyük çerçeveler kullanarak, zaman dilimi başına daha fazla numuneyi tamponlamaktır. Çoğu modern gerçek zamanlı ses API'si, kanal başına 128 adet (özel ses donanımı üzerinde) veya kanal başına 256 örnek (oyun konsollarında) kullanır.

Nihayetinde, sorunuzun cevabı çoğunlukla beklediğiniz cevaptır ... Tamponların kendilerine değil, arabelleklere benzersiz şekilde sahip olunan sıralar etrafından geçin; TÜM ses arabelleklerini, program başlangıcında ayrılmış sabit bir havuzda yönetin; ve tüm kuyrukları gerektiği kadar kısa bir süre için kilitleyin.

İlginçtir ki, bu, derleme kodunu bozmak için ayrı bir performans avantajının olduğu ses programlamasındaki birkaç iyi durumdan biridir. Kesinlikle her kuyruk kilidi ile meydana gelen bir malloc ve ücretsiz istemiyorum. İşlemci sistemi tarafından sağlanan atomik kilitleme işlevleri, CPU'nuzu biliyorsanız, HER ZAMAN geliştirilebilir.

Son bir şey: Lockfree kuyruğu diye bir şey yoktur. Tüm multithread "lockfree" kuyruk uygulamaları, belleğe özel erişimin her iş parçacığı için garanti edildiğinden emin olmak için bir yerdeki bir işlemci bariyerine veya zor bir karşılaştırma ve takas sistemine güvenir.

+0

"Tampon başına daha fazla örnek" diyorsunuz ama daha az örnek örnekleri veriniz (128 byte belki 32 stereo örneğidir) –

+0

D'oh, haklısınız, sabit, teşekkürler. – johnwbyrd

+0

Ayrıca, RAII tarzı muteksler dinamik ayırma gerektirmez. –

İlgili konular