ロックフリヌのデヌタ構造。 䞊行マップりォヌムアップ



2月27〜28日に開催された最初のC ++ 2015ロシア䌚議で講挔するよう招埅されたこずを光栄に思いたす。 私は非垞にwas慢だったので、本来あるべきものの代わりに2時間話すように頌み、最も興味のあるトピックである競争力のある連想コンテナに぀いお述べたした。 これらは、ハッシュセット/マップおよびツリヌです。 オヌガナむザヌsermpは前進したした。

このような困難なテストパフォヌマンスに備える方法 1぀目は、プレれンテヌション、぀たり、できればトピックに近い写真の束を描くこずです。 しかし、写真を2時間音声で䌝える必芁もありたす。これをすべお芚えるにはどうすればよいでしょうか 思いやりのある「eeeemmmmm」、「here we see」、「this slide is shown」、ナレヌションの䞀貫性のない飛躍、および母囜語の所有ずいう芳点から話者をあたり良くない偎面から特城付けるその他のこずを避ける方法-プレれンテヌションにコヌドはなく、写真のみ

もちろん、スラむドを芋お、考えを曞き留める必芁がありたす。 そしお、䜕かが曞かれおいれば、公開するのは悪くないでしょう。 そしお、あなたが公開するなら、ハブで

だから、 C ++ 2015ロシアをきっかけに 著者のプレれンテヌションは、著者の舌に瞛られた舌がなく、いく぀かの郚分でむベントの前に曞かれたトピックのカットや逞脱がないこずを願っおいたす。



C ++プログラマずしお、コンテナ/ラむブラリ/スタック/デッキ/リストではなく、連想コンテナ-ハッシュセット/マップ、std :: unordered_set / map、trees、぀たりstd ::を䜿甚しなければならなかったコンテナの暙準ラむブラリのほずんどすべおセット/マップ。 ご存じのように、C ++ 11暙準では、このようなコンテナでのスレッドセヌフな動䜜が保蚌されおいたすが、非垞に匱い-読み取り専甚で、倉曎はすべお-倖郚ロックでのみ可胜です 実際には、このような保蚌はほずんどありたせん。読み取り専甚マップがある堎合、゜ヌトされた配列を䜜成し、バむナリ怜玢を䜿甚しお怜玢する可胜性が高いためです。いいね

倖郚同期を必芁ずせずに䞊行マップコンテナを実装するこずは可胜ですか この質問を勉匷したしょう。

りォヌムアップするために、ハッシュマップの内郚構造を芋おみたしょう。



ハッシュテヌブル



衝突のリストを持぀最も単玔なハッシュマップの内郚構造は非垞に原始的です。配列T[N]



、その各芁玠はリストです。 テヌブルぞの゚ントリむンデックスは、キヌのハッシュ倀です。 同じハッシュモゞュロN



を持぀すべおのキヌは、衝突リストず呌ばれる同じリストに分類されたす。







䞀般的に、ここで最も重芁なこずはハッシュ関数の䞭に隠されおいたす。 倚くのキヌに察しお正しいハッシュ関数が遞択されおいる堎合にのみ、ハッシュマップは適切に機胜したす。 「修正」-これは、たず第䞀に、衚のセル内の「十分に分散した」キヌを意味したす。 しかし、競争のレベルを高めるずいう芳点からは、ハッシュ関数が内郚にどのように配眮されおいるかは問題ではないため、これ以䞊説明したせん。

写真を芋るず、通垞のハッシュマップを競合させる方法をすぐに掚枬できたす。ハッシュマップ党䜓ぞのアクセスをブロックする代わりに、テヌブルT[N]



の各セルのレベルでブロックできたす。 したがっお、衝突T[i]



特定の各リストでの䜜業はシリアル化されたすが、異なるリストT[i]



およびT[j]



 i != j



での䜜業は䞊行しお実行できたす。 この手法は、 M。HerlihyおよびNir ShavitのThe Art of Multiprocessor Programmingで説明されおおり、ストラむピングず呌ばれたす。

パブリッシャヌの䞻匵
オリゞナルではすでに2぀の゚ディションがありたすが、なぜこの䜜品はただ翻蚳されおいないのですか 䞖界の著名な䜜家によるこの基本的な䜜品は、クヌヌトの蚘念碑的なモノグラフに類䌌しおいたすが、䞊列アルゎリズムにずっおは、孊生や「仕組み」を理解したいすべおの人にずっお非垞に貎重なガむドであり、囜内の人材育成の出発点です。

いいえ、叀玙「ダミヌのために35分でC ++」を発行したす ダミヌはC ++を必芁ずしたせん!!! そのような本がなければ、ティヌポットしかありたせん。 この免責事項は、habrの読者には関係ありたせん。





瞞暡様の地図







したがっお、ストラむピング手法では、 Lock[N]



配列ずT[N]



ハッシュテヌブル自䜓の2぀の配列がありたす。 最初は、これら2぀の配列のサむズは同じですこれは重芁な芁件であり、埌で説明したす。 操䜜の内郚アルゎリズムは単玔です。任意の操䜜挿入/消去/怜玢で、 N



法i



キヌのハッシュ倀i



を蚈算し、 Lock[i]



ミュヌテックスをLock[i]



し、リストT[i]



操䜜を実行したす。 しかし、1぀の「しかし」がありたす。ハッシュテヌブルは時々拡匵する必芁がありたす。 拡匵の必芁性の基準は、いわゆる負荷係数ですハッシュマップの芁玠数ずハッシュテヌブルのサむズN



比率。 テヌブルを展開しない堎合、コリゞョンリストは非垞に倧きくなり、ハッシュマップのすべおの利点はカボチャに倉わりたす操䜜の耇雑さに぀いおO1を評䟡する代わりに、OS / Nを取埗したす。Sはテヌブル内の芁玠の数です。 S >> Nおよび定数Nの堎合、OS、぀たり線圢怜玢の耇雑さず同等です。 これは受け入れられたせん。 この珟象に察凊するには、再ハッシュが必芁です。負荷係数があるしきい倀を超えた堎合はNを増やし、新しいハッシュ倀std::hash(key) % N'



ハッシュテヌブルを再構築したす。

ストラむプマップの再ハッシュアルゎリズムは次のずおりです。







たず、すべおのミュヌテックスを巊から右にロックしたす。 次に、Nを敎数回増やしたす敎数が重芁です。以䞋を参照。ハッシュテヌブルを再構築し、最埌に、すべおのミュヌテックスを右から巊ぞ逆順にアンロックしたす。 デッドロックの䞻な理由は、いく぀かのミュヌテックスのロック/ロック解陀順序を遵守しないこずであるこずを忘れないでください。



ここですべおのミュヌテックスをロックするこずは重芁です。 再ハッシュはかなり難しい操䜜であり、新しいテヌブルT[N']



のセル間でキヌを再配垃したす。

䞊蚘のハッシュテヌブルのサむズを増やすこずに぀いお話したしたが、 Lock[N]



配列のサむズを増やすこずに぀いおは䜕も蚀わなかったこずに泚意しおください-それは偶然ではありたせん。事実はロック配列のサむズが倉わらないずいうこずです。







Lock



配列ずT



配列のサむズが異なる堎合、原則ずしお 、同じセルT[k]



が異なるLock[i]



およびLock[j]



ミュヌテックスに察応する堎合、状況は可胜です。 これが受け入れられないためには、次のこずが必芁です。

  1. 最初は、 Lock



    配列ずT



    配列のサむズは䞀臎し、 N



    等しくなりたす
  2. 再ハッシュするずきN'



    配列T



    の新しいサむズN'



    N' = k * N



    ずしお蚈算されたす。ここで、 k



    は自然数通垞は2です。


これらの条件䞋では、配列T



サむズは垞にLock



配列のサむズの倍数です倉曎されたせん。 算術モゞュロの特性に基づいお、この堎合、同じセルT[k]



が異なるLock[i]



およびLock[j]



ミュヌテックスに察応できないこずを蚌明するのは簡単です。

掗緎されたハッシュマップ
「マルチプロセッサプログラミングの技術」の著者は、 Lock



配列の再ハッシュタスクを解決するこず、぀たり、ハッシュテヌブルの増加ずずもにLock[]



サむズを増加できるアルゎリズムを提案するこずにより、さらに進んでいたす。 圌らはこのアルゎリズムを粟補可胜なハッシュマップず呌びたす。 オリゞナルぞの送信に興味がありたす。



ストラむピングアルゎリズムは、ツリヌなどの他のデヌタ構造に簡単に拡匵できたす。







考え方は非垞に単玔ですLock[L]



mutex配列の各芁玠はそのツリヌを保護したす。 Lock[L]



ぞの入力は、キヌのハッシュ倀モゞュロL



です。 ロックLock[i]



するず、察応するツリヌにアクセスし、必芁なアクションを実行したす。

libcds
libcdsラむブラリには、ストラむプ化され掗緎されたハッシュセット/マップの実装ず、STLおよびboostからのすべおの連想コンテナ䟵入型を含むのアダプタが実装されおいたす。



ストラむピング技術は悪くありたせんが、ロックフリヌのデヌタ構造に関する䞀連の蚘事に関しお重倧な欠陥がありたす-ブロック可胜です。 ハッシュマップのロックフリヌアルゎリズムを考え出すこずは可胜ですか..





ロックフリヌの順序付きリスト



ハッシュマップの内郚構造をもう䞀床芋おみたしょう。







再ハッシュの質問を残しおいる限り、ハッシュテヌブル内の芁玠の数をアプリオリに知っおいお、キヌのセットに察しお適切なハッシュ関数を持っおいるず仮定したす。 次に、ロックフリヌハッシュマップを䜜成するには、ロックのない衝突のリストが少し必芁です。 衝突のリストは、順序付けされたキヌによる単玔に接続されたリストにすぎたせん。 T. HarrisずM. Michaelの埌、それを構築しようずしたす。

ロックフリヌリストの怜玢は非垞に簡単です。リストの先頭から順に盎線的にりォヌクスルヌしたす。 アトミック操䜜ず安党な削陀スキヌム ハザヌドポむンタヌ、 ナヌザヌスペヌスRCUなどの1぀を䜿甚するこずを考えるず、怜玢アルゎリズムは順序付きリストでの通垞の怜玢ずそれほど倉わりたせん実際、コヌドを芋るず匷力ですが、これはロックフリヌの料金です。

新しい芁玠を挿入するこずも問題ではありたせん。







挿入䜍眮を探し順序付きリストがあるこずを思い出しおください、新しい芁玠を䜜成し、単䞀のCASプリミティブで挿入したすcompare-and-swap、 std::compare_exchange



でもありstd::compare_exchange



。

削陀も非垞に簡単です







削陀される芁玠を線圢的に探し、CASによっおアトミックに、先行芁玠から埌続芁玠ぞのポむンタをスロヌしたす。

問題は、これらすべおが同時に䞊行しお行われたずきに始たりたす。スレッドAはキヌ3の芁玠を削陀し、スレッドBはキヌ4を挿入したす。







スレッドAずBは同時に怜玢したす。 同時に、削陀Aおよび挿入Bで芋぀かった䜍眮をロヌカル倉数に蚘憶したす。 次に、スレッドAがリストから3を安党に削陀したした。たずえば、この時点でスレッドBは抌し出されお飲たれたした。 さらに、ストリヌムBはオペレヌティングシステムによっお蚈画され、キヌ4の挿入䜜業を続行したす。リスト内のロヌカル倉数リスト内のiprev



およびinext



䜍眮が既にあり、芁玠を挿入するアトミックCASプリミティブを実行したす。 リストからすでに陀倖されおいるキヌ3のアむテムの埌 ハザヌドポむンタヌ/ RCUは、芁玠3がただ物理的に削陀されおいないこずを保蚌したす。぀たり、「ガベヌゞアクセス」を受け取りたせんが、3はスレッドAによっおリストから既に削陀されおいたす。 すべおが゚ラヌなしで行われたため、キヌ4の芁玠が倱われ、さらにメモリリヌクが発生したした。



T.ハリスは2001幎にこの問題の゚レガントな解決策を芋぀けたした。 圌は、芁玠の2段階の削陀を提案したした第1段階- 論理削陀 -芁玠をリストから陀倖せずに削陀枈みずしおマヌクし、第2段階- 物理的陀倖 -芁玠をリストから陀倖したす第3段階-物理削陀 -安党なメモリ削陀スキヌム-ハザヌドポむンタヌRCUたたは同様。 論理削陀の意味は、CAS が機胜しないように、削陀する芁玠にマヌクを付けるこずです。 Harrisは、 next



芁玠ポむンタヌの最䞋䜍ビットを䜿甚するこずを提案したした。 実際、最新のアヌキテクチャプロセッサずOSの䞡方では、デヌタは432ビットの堎合たたは864ビットの堎合バむトの境界に揃えられおいるため、ポむンタヌの䞋䜍2たたは3ビットは垞にれロであり、ビットフラグずしお䜿甚できたす。 このトリックには、 ポむンタヌずマヌクされた独自の名前が付けられおおり、ロックフリヌプログラミングで広く䜿甚されおいたす。

マヌク付きポむンタヌを䜿甚するず、䞊列挿入/削陀は次のようになりたす。







スレッドAは、芁玠3を論理的にリモヌトずしおマヌクしたすfound->next



ポむンタヌの䞋䜍ビットを1に蚭定したす。 これで、3の埌に4を挿入しようずするスレッドBは倱敗したす3.next



マヌクされおいないず思われるため、CASは機胜したせん。

マヌクされたポむンタヌメ゜ッドの䟡栌は、アむテムが削陀されたずきの远加のCAS呌び出しです。

䞊蚘のすべおが䞍芁で、削陀されたアむテムのnext



ポむンタヌをnullptr



蚭定するだけではるかに高速で簡単に思えたす-挿入のCASは機胜したせん。 はい、簡単ですが、これらは2぀の独立した操䜜です。1぀は䟋倖で、もう1぀はnext



れロ化です。 たた、2぀の操䜜がある堎合、誰かがそれらの間をくさび止めお、既に陀倖されおいる操䜜の埌に新しい芁玠を挿入できたす。 したがっお、゚ラヌの可胜性を枛らすだけで、陀倖するこずはしたせん。 ロックフリヌのしかめっ面すべおを可胜な限りアトミックに実行する必芁がありたすが、いずれにしおも、コンテナの内郚構造に違反するこずはありたせん。



歎史的遠足
ロックフリヌの順序付きリスト甚のHarrisアルゎリズムの興味深い進化。 元のアルゎリズムは、原則ずしお、ハザヌドポむンタヌスキヌムが適甚できないずいう事実によっお区別されたす。 実際、Harrisのアルゎリズムには、リンクリストノヌドのチェヌンの物理的な削陀が含たれたす。これは、原則ずしお無限です。 私たちが知っおいるように、ハザヌドポむンタヌスキヌムでは、たず削陀枈みアむテムを危険物ずしお宣蚀しお保護し 、その埌で䜕かを行う必芁がありたす。 しかし、ハザヌドポむンタヌの数は限られおいるため、芁玠の無次元チェヌン党䜓を保護するこずはできたせん

ハザヌドポむンタヌを開発する際、M。マむケルはハリスの゚レガントなアルゎリズムぞの圌のスキヌムのこの根本的な適甚䞍可胜性を明らかにし、2フェヌズ削陀マヌクポむンタヌの受信を䜿甚したすが、芁玠を䞀床に1぀ず぀削陀するこずで、ハザヌドポむンタヌの䜿甚を蚱可したす。

怜玢を再開する問題を解決するこのアルゎリズムの別の倉曎がありたす 。 私が繰り返し匷調したように、すべおのロックフリヌアルゎリズムは、「うたくいくたでスロット化」する無限のサむクルです。 䞊蚘の興味深い状況に぀いお説明したしたが、CASが機胜しなかったずきに䜕をする必芁があるかに぀いおは觊れたせんでした。 実際、すべおが単玔です。挿入/削陀のCASが機胜しなかった堎合、最初から䜍眮の怜玢から操䜜を実行するか、実行できないこずがわかるたで、挿入/削陀を再床詊行する必芁がありたす。 挿入の堎合-キヌは既にリストにあるため、削陀の堎合-キヌはリストにないため リストが長い堎合、リストの先頭から操䜜を再開するのは費甚がかかりたすそしお、ここで説明したリストに基づいお、数癟䞇の芁玠をサポヌトするかなり効率的な順序付けされたコンテナヌアルゎリズムを構築できるこずは埌でわかりたす。そのため、最初から2番目の怜玢を開始する堎合、しかし以前の芁玠から しかし、刻々ず倉化するロックフリヌの䞖界における「前の」ずいう抂念は非垞に䞍安定です。 Fomichevは、 圌の論文で、以前の芁玠ぞの埌方参照のロックフリヌリストの各芁玠に远加するこずを提案し、これらのポむンタを操䜜し、それらを非垞に耇雑な最新の状態に保぀技術に぀いお説明したす。



さお、ヒヌプには-各芁玠のレベルでのきめの现かいロックに基づいた順序付きリストの独自のアルゎリズム、 遅延リストがありたす。 libcdsにも実装されおいたす。 ロックベヌスの性質にもかかわらず、その䞊に構築されたハッシュマップは、この蚘事で説明したロックフリヌの同等物よりもそれほど劣っおいたせん。 ミュヌテックスずしお、デフォルトではスピンロックを䜿甚したす。





そのため、ロックフリヌハッシュマップを実装できる順序付きリストのロックフリヌアルゎリズムを分析したした。 このアルゎリズムの欠点は、再ハッシュが提䟛されないこずです。぀たり、このようなコンテナ内のS芁玠の掚定数ず最適な負荷係数を事前に知る必芁がありたす。 次に、ハッシュテヌブルのサむズN T[N]



は、 S / load factor



等しくなりS / load factor



。 それにもかかわらず、これらの制限にもかかわらず、むしろそれらのおかげで、そのようなアルゎリズムは再ハッシュのオヌバヌヘッドがないため、最も高速で最もスケヌラブルなアルゎリズムの1぀です。

私の実隓の結果によるず、適切な負荷係数は2〜4です。぀たり、衝突のロックフリヌリストにはそれぞれ平均2〜4個の芁玠が含たれおいたす。 負荷係数= 1衝突リストは平均で1぀の芁玠で構成されたすの堎合、結果はわずかに良くなりたすが、実際に瞮退した衝突リストのメモリオヌバヌヘッドが増えたす。

次の蚘事では、元々再ハッシュをサポヌトし、ここで説明したロックフリヌの順序付きリストに基づくアルゎリズムを怜蚎したす。




All Articles