0

マルチ プロデューサーと x86 Linux で実行されている単一のコンシューマーを持つリング バッファーのような lmax ディスラプターでコンシューマーが遅い場合の対処法について質問があります。リング バッファ パターンのような lmax を使用すると、常にデータを上書きしますが、消費者が遅い場合はどうなりますか。したがって、たとえば 10 サイズのリング バッファ 0 ~ 9 のリング スロットでコンシューマがスロット 5 にあり、ライターがバッファのスロット 5 でもあるスロット 15 の書き込みを開始する準備ができている場合をどのように処理しますか (つまり、スロット5 = 15 % 10 )? ライターが入ってきた順にデータを生成し、クライアントが同じ順序でデータを受け取るように、これを処理する一般的な方法は何ですか? それは本当に私の質問です。以下は私の設計に関する詳細であり、問​​題なく動作しますが、現在この問題を処理する良い方法がありません。

設計仕様

リング バッファーがあり、現在、設計には複数のプロデューサー スレッドと 1 つのコンシューマー スレッドがあります。デザインのこの部分は既存のものであり、現在変更することはできません。ロック フリー リング バッファを使用して、既存のキューイング システムを削除しようとしています。私が持っているものは次のとおりです。

コードは x86 Linux で実行され、ライター用に複数のスレッドが実行され、リーダー用に 1 つのスレッドが実行されます。リーダーとライターは 1 スロット離れて開始するstd::atomic<uint64_t>ため、リーダーはスロット 0 から開始し、ライターはスロット 1 から開始し、各ライターは最初に、以下に示すfetch_add(1, std::memory_order::memory_order_acq_rel)呼び出しによってライター シーケンスでアトミックを実行してスロットを要求incrementSequenceし、その後、compare_and_swap ループを使用します。このスロットが利用可能であることをクライアントに知らせるためにリーダー シーケンスを更新するには、 を参照してくださいupdateSequence

 inline data_type incrementSequence() {                                                                                       
        return m_sequence.fetch_add(1,std::memory_order::memory_order_seq_cst);                                                  
    }   


void updateSequence(data_type aOld, data_type aNew) {                                                                        
        while ( !m_sequence.compare_exchange_weak(aOld, aNew, std::memory_order::memory_order_release, std::memory_order_relaxed)
            if  (sequence() < aNew)  {                                                                                           
                continue;                                                                                                        
            }                                                                                                                    
            break;                                                                                                               
        }                                                                                                                        
    }                   
 inline data_type sequence() const {                                                                                          
        return m_sequence.load(std::memory_order::memory_order_acquire);                                                         
    }       
      
4

1 に答える 1

4

リング バッファ (または一般的な FIFO -- リング バッファとして実装する必要はありません) は、トラフィックのバーストをスムーズにすることを目的としています。プロデューサがバーストでデータを生成する場合でも、コンシューマは入力の安定したフローを処理できます。

FIFO がオーバーフローしている場合は、次の 2 つのいずれかを意味します。

  1. バーストが予定よりも大きくなっています。FIFO サイズを増やす (またはそのサイズを動的にする) ことで、これを修正します。
  2. 生産者は消費者を追い越しています。これを修正するには、データの消費に専念するリソースを増やします (おそらくより多くのスレッド)。

私には、あなたは現在 2 番目に当たっているように思えます。あなたの単一のコンシューマーは、プロデューサーに追いつくのに十分な速さではありません。その場合の唯一の現実的な選択肢は、単一のコンシューマーを最適化するか、より多くのコンシューマーを追加して、消費を高速化することです。

また、コンシューマがデータの処理中に入力データを FIFO に残している可能性があるため、コンシューマがその入力の処理を終了するまで FIFO 内のスポットが占有されたままになる可能性があるようにも聞こえます。その場合、コンシューマーが処理を開始したらすぐに FIFO から入力データを削除するだけで、問題を解決できる場合があります。これにより、そのスロットが解放されるため、プロデューサーは入力をバッファーに配置し続けることができます。

もう 1 つのポイント: FIFO サイズを動的にすると、問題が発生する可能性があります。問題は非常に単純です。消費者側でデータを処理するために必要なリソースがないという 2 番目の問題が実際にあるという事実を覆い隠すことができます。

プロデューサとコンシューマの両方がスレッド プールであると仮定すると、システムのバランスを取る最も簡単な方法は、多くの場合、固定サイズの FIFO を使用することです。プロデューサがコンシューマより先に進み始めて FIFO がオーバーフローすると、プロデューサはブロックを開始します。これにより、コンシューマー スレッド プールはより多くの計算リソースを消費し (より多くのコアで実行するなど)、プロデューサーに追いつくことができます。ただし、これは、システムを単一のコンシューマーに制限するのではなく、より多くのコンシューマーを追加できるかどうかに依存します。

于 2014-06-21T17:14:45.383 に答える