2015-01-02 10 views
5

オーディオソフトウェアを書くとき、インターネット上の多くの人が、メモリ割り当てやブロックコードの使用は特に重要でないと言います。事実、これらは非決定論的なので、出力バッファがアンダーフローし、オーディオに不具合が生じる可能性があります。マルチスレッドリアルタイムオーディオプログラミング - ブロックするかどうかをブロックする

Real Time Audio Progrmaming

Iは、ビデオソフトウェアを記述する場合、私は一般的に両方を使用する、すなわち、ヒープ上でビデオフレームを割り当て、ロックと条件変数(境界バッファ)を使用して、スレッド間を通過します。個別のスレッドを各操作に使用できるので、ソフトウェアがコアごとに最大限になり、最高のパフォーマンスが得られるので、これが提供するパワーが大好きです。

オーディオでは、スレッド間に多分100サンプルのフレームを渡して同様のことをしたいと思いますが、2つの問題があります。

  1. メモリ割り当てを使用せずにフレームを生成するにはどうすればよいですか?私はあらかじめ割り当てられたフレームのプールを使うことができると思うが、これは乱雑に思える。

  2. ロックフリーキューを使用できることを知っています。このブーストには、これを行ううえで素晴らしいライブラリがあります。これはスレッド間で共有するのに最適な方法ですが、データが利用可能かどうかを定期的に確認するためにキューをポーリングするのは、CPU時間の浪費のようです。私の経験で

ミューテックスを使用すると、実際にミューテックスがロックされている部分が短いことを提供し、すべてで多くの時間を取ることはありません。

レイテンシを最小限に抑えながら、リソースを無駄にすることなく、非決定的な動作を比較的少なくすることで、スレッド間でオーディオフレームを渡す最も良い方法は何ですか?

+0

オーディオに関することはよく分かりませんが、伝統的なプロデューサ/コンシューマの問題のように聞こえますが、セマフォを数えてみましたか? – smskelley

+0

スレッドが必要ですか?オーディオデータはビデオよりもかなり安いです。 – immibis

+0

@immibis、それはあなたのオーディオスレッドで何をするかによって異なります。ビデオは通常、1つのサンプルにつき非常に少数の操作しか行いませんが、オーディオ合成はサンプルごとに非常に厳しいものになります。 –

答えて

4

あなたの調査のようですね!オーディオの不具合の根本原因となる2つの主な問題を既に特定しています。問題は、10年前にこれがどれほど重要であったか、そして今日は民間伝承と貨物礼拝のプログラミングだけです。

私の2セント:レンダリングループ内

1.ヒープ割り当て:

これらはあなたの処理のチャンクはどのように小さなに応じて、非常に多くのオーバーヘッドを持つことができます。主な原因はスレッドごとのヒープを持つ実行時間はごくわずかなので、ヒープを混乱させるたびに、パフォーマンスはプロセス内の他のスレッドが何をするかによって決まります。たとえば、GUIスレッドが現在何千ものオブジェクトを削除しているときに、同時にオーディオレンダリングスレッドからヒープにアクセスすると、かなりの遅延が発生する可能性があります。

あらかじめ割り当てられたバッファを使用して独自のメモリ管理を作成すると、面倒なように聞こえるかもしれませんが、最終的には、ユーティリティソースのどこかに隠れることができる2つの機能です。あらかじめ割り当てサイズを知っているので、メモリ管理を微調整し最適化する機会がたくさんあります。たとえば、セグメントを単純なリンクリストとして保存することができます。これがうまくいけば、最後に使用されたバッファを再び割り当てる利点があります。このバッファは、キャッシュ内で非常に高い確率で動作します。

固定サイズのアロケータが動作しない場合は、リングバッファを参照してください。ストリーミングオーディオのユースケースによく合います。

2.ロックする、またはロックしない:

私はあなたが毎秒彼らの5000にあなたが1000未満を行うことを見積もることができる場合には、ミューテックスとセマフォロックを使用して、これらの日は大丈夫ですと言うだろう(PC上では、ラズベリーパイなどのものが違う)。この範囲を下回ると、パフォーマンスプロファイルにオーバーヘッドが表示されることはほとんどありません。

たとえば、48kHzのオーディオと100個のサンプルチャンクを使用する場合は、単純な2スレッドのコンシューマ/プロデューサパターンで約960回のロック/アンロック操作を生成します。それは範囲内にあります。レンダリングスレッドを完全に最大限に活用した場合、プロファイリングではロックが表示されません。一方、使用可能な処理能力の5%のような使用しかできない場合は、ロックが表示されることがありますが、パフォーマンスに問題はありません:-)

ロックインもオプションですが、最初にいくつかのロックレス試行を行い、次にハードロックに落ちるハイブリッドソリューションです。あなたはそのように両方の世界のベストを得るでしょう。ネット上でこのトピックについて読むには良いものがたくさんあります。いずれの場合においても

あなたは、彼らがロックに実行する場合、彼らはすぐにそれから抜け出すことを確認するために優しく非GUIスレッドのスレッドの優先度を上げる必要があります。

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

1

「私は私が事前に割り当てられたフレームのプールを使用することができたとしたが、これはそうです:それは何であるか優先順位逆転読み取るために、そしてあなたはそれを避けるために何ができるかにも良いアイデアです厄介な ' - 本当に。ループのフレームの配列、または新しいアップフレームを割り当てて、インデックス/ポインタをブロッキングキューに移動します。これで、自動管理されたフレームプールが完成しました。フレームが必要なときにはポップを1つ取り、それが終わったらもう一度押してください。プールがなくなった場合、フレームを要求するスレッドは、フレームがプールに戻されるまで待機しますが、すべてのmalloc/free/new/delete、no chanceまたはmemory-runaway、より簡単なデバッグ、およびフレームフロー制御はありません。

アレイを使用すると、新しいループよりも簡単に/安全に/高速に見えるかもしれませんが、新しいフレームごとに利点があります。実行時にプール内のフレーム数を簡単に変更できます。

0

um、なぜスレッド間に100サンプルのフレームを渡していますか?

公称サンプルレート44.1kHzで動作し、スレッド間で一度に100サンプルを渡すと仮定すると、スレッドスイッチングレートは100サンプル/(44100サンプル/秒* 2) 。 2は、プロデューサとコンシューマの両方を表します。つまり、送信する100サンプルごとに〜1.13ミリ秒のタイムスライスがあることを意味します。ほぼすべてのオペレーティングシステムは、10 msを超えるタイムスライスで動作します。したがって、最新のOSで44.1kHzのスレッド間でわずか100のサンプルを共有するオーディオエンジンを構築することは不可能です。

解決方法は、キューまたは大きなフレームを使用して、タイムスライスごとにさらに多くのサンプルをバッファリングすることです。最新のリアルタイムオーディオAPIのほとんどは、1チャンネルあたり128サンプル(専用オーディオハードウェア上)または1チャンネルあたり256サンプル(ゲームコンソール上)を使用します。

最終的には、あなたの質問に対する答えはほとんどあなたが期待する答えです。バッファ自体ではなく、バッファへのポインタの一意のキューを渡します。プログラムの開始時に割り当てられた固定プール内のすべてのオーディオバッファを管理する。すべてのキューを必要なだけ短時間ロックします。

興味深いことに、これは、アセンブリコードを破棄するのに明確なパフォーマンス上の利点があるオーディオプログラミングの数少ない良い状況の1つです。あなたは間違いなく、すべてのキューロックでmallocとfreeが発生しないようにします。あなたのCPUを知っているならば、オペレーティングシステムが提供するアトミックロック機能は、常に改良することができます。

最後に1つ:ロックフリーキューはありません。すべてのマルチスレッド "lockfree"キューの実装は、CPUバリアの組み込み関数または厳密な比較とスワップを使用して、メモリへの排他的アクセスがスレッドごとに保証されるようにします。

+0

あなたは "バッファあたりのサンプル数が増えている"と言いますが、サンプル数が少ない例を示します(128バイトは32サンプルあります) –

+0

D'oh、あなたは正しいです。 – johnwbyrd

+0

また、RAII形式のミューテックスは動的割り当てを必要としません。 –

関連する問題