回答:
スキップリストは、同時アクセス/変更の影響を受けやすくなっています。Herb Sutterは、並行環境でのデータ構造に関する記事を書きました。より詳細な情報があります。
二分探索木で最も頻繁に使用される実装は、赤黒木です。同時に問題が発生するのは、ツリーが変更されると、多くの場合、バランスを調整する必要があるためです。リバランス操作はツリーの大部分に影響を与える可能性があり、ツリーノードの多くでミューテックスロックが必要になります。ノードをスキップリストに挿入することは、はるかにローカライズされており、影響を受けるノードに直接リンクされているノードのみをロックする必要があります。
Jon Harropsコメントからの更新
私はFraserとHarrisの最新の論文「ロックなしの並行プログラミング」を読みました。ロックフリーのデータ構造に興味があるなら、本当に良いものです。このペーパーでは、トランザクションメモリと理論的な操作のマルチワード比較およびスワップMCASに焦点を当てています。これらの両方は、まだハードウェアでサポートされていないため、ソフトウェアでシミュレーションされます。彼らがソフトウェアでMCASを構築することができたことにかなり感銘を受けました。
トランザクショナルメモリはガベージコレクタを必要とするため、特に説得力のあるものは見つかりませんでした。また、ソフトウェアトランザクションメモリはパフォーマンスの問題に悩まされています。ただし、ハードウェアのトランザクションメモリが一般的になれば、非常に興奮します。結局のところ、それはまだ研究中であり、今後10年間は本番コードに使用されません。
セクション8.2では、いくつかの並行ツリー実装のパフォーマンスを比較します。調査結果をまとめます。50、53、54ページにいくつかの非常に有益なグラフがあるので、pdfをダウンロードする価値があります。
更新
ここにロックフリーツリーに関する論文があります:CASを使用したロックフリーの赤黒木。
深くは調べていませんが、表面的にはしっかりしているようです。
まず、ランダム化されたデータ構造を、最悪の場合の保証を提供するものと公平に比較することはできません。
スキップリストは、Dean and Jonesの「Exploring the Duality between Skip Lists and Binary Search Trees」で詳細に説明されているように、ランダムバランスバイナリ検索ツリー(RBST)と同等です。
逆に、最悪の場合のパフォーマンスを保証する確定的なスキップリストを使用することもできます。Munro et al。
上記の主張に反して、並行プログラミングでうまく機能するバイナリサーチツリー(BST)の実装を持つことができます。同時実行に重点を置いたBSTの潜在的な問題は、赤黒(RB)ツリーからの場合と同じように、バランスについて同じことが簡単に得られないことです。(ただし、「標準」、つまりランダム化されたスキップリストでは、これらの保証も得られません。)常にバランスを維持することと、良好な(そしてプログラムが容易な)同時アクセスとの間にはトレードオフがあるため、通常、リラックスした RBツリーが使用されます。優れた並行性が必要な場合。リラクゼーションでは、すぐにツリーのバランスを調整しません。やや日付が付けられた(1998年の)調査については、ハンケの '' The Performance of Concurrent Red-Black Tree Algorithms '' [ps.gz]を参照してください。
これらの最近の改良点の1つは、いわゆるクロマティックツリーです(基本的に、黒が1、赤が0になるような重みがありますが、その間の値も許可されます)。そして、クロマチックツリーはスキップリストにどのように対応しますか?ブラウンらが何を見てみましょう。「ノンブロッキングツリーの一般的な手法」(2014)は次のように述べています。
128スレッドで、このアルゴリズムはJavaのノンブロッキングスキップリストより13%から156%優れています。これは、BronsonらのロックベースのAVLツリーです。63〜224%、ソフトウェアトランザクションメモリ(STM)を13〜134倍使用するRBT
追加する編集:フレーザーとハリス(2007)の「ロックなしの並行プログラミング」でベンチマークされたPughのロックベースのスキップリストは、独自のロックフリーバージョンに近づいている(ここのトップアンサーで強く主張されている点)、また、適切な同時操作のために調整されています。Pughの"スキップリストの同時保守"ですが、かなり穏やかな方法です。それにもかかわらず、より新しい/ 2009年の論文「単純な楽観的スキップリストアルゴリズム」(Pughのよりも)おそらく単純なロックベースの同時スキップリストの実装を提案しているHerlihyらによると、十分に説得力のある正確さの証拠を提供していないとしてPughを批判しました。この(あまりにも知識が多すぎる)ことはさておき、Herlihy et al。スキップリストのより単純なロックベースの実装は、実際にはJDKのロックフリー実装と同様にスケーリングに失敗しますが、高競合(50%挿入、50%削除、0%ルックアップ)の場合のみ...どのFraserそして、ハリスはまったくテストしませんでした。FraserとHarrisは、75%のルックアップ、12.5%の挿入、12.5%の削除のみをテストしました(50万要素以下のスキップリストで)。Herlihyらのより単純な実装。また、テストした競合が少ない場合のJDKのロックフリーソリューションに近づきます(70%のルックアップ、20%の挿入、10%の削除)。彼らは、スキップリストを十分に大きく(つまり、200K要素から2M要素に)したときに、このシナリオのロックフリーソリューションを打ち破ったため、ロックの競合の確率は無視できるほどになりました。Herlihy et al。Pughの証明を越えてハングアップを乗り越えて、彼の実装もテストしていましたが、残念ながらそうしませんでした。
EDIT2:すべてのベンチマークの(2015年に公開された)マザーロードを見つけました:Gramoliの「同期について知りたい以上のこと。同期、同時アルゴリズムに対する同期の影響の測定」:この質問に関連する抜粋画像です。
「Algo.4」は、上記のブラウン他の前身(2011年以前のバージョン)です。(私は2014年版がどれほど良いか悪いかわかりません)。「Algo.26」は上記のHerlihyの説明です。あなたが見ることができるように、それはアップデートでごまかされ、元の論文のSun CPUよりもここで使用されているIntel CPUではるかに悪い。「Algo.28」は、JDKのConcurrentSkipListMapです。他のCASベースのスキップリスト実装と比較して、期待したほどの効果はありません。高争いの勝者は、「Algo.2」、Crain et al。によって記述されたロックベースのアルゴリズム(!!)です。で「A競合フレンドリーバイナリ検索ツリー」と「Algo.30」から「回転skiplist」である「マルチコアのための対数データ構造」。」。Gramoliは、これらの勝者アルゴリズムに関する3つの論文すべての共著者であることに注意してください。「Algo.27」は、FraserのスキップリストのC ++実装です。
Gramoliの結論は、同様のスキップリストを台無しにするよりも、CASベースの並行ツリー実装を台無しにする方がはるかに簡単であるということです。そして、図に基づいて、それを否定するのは難しいです。この事実についての彼の説明は次のとおりです。
ロックのないツリーを設計することの難しさは、複数の参照をアトミックに変更することの難しさに起因します。スキップリストは、後続ポインタを介して相互にリンクされたタワーで構成され、各ノードはそのすぐ下のノードを指します。各ノードにはサクセサタワーとその下にサクセサがあるため、それらはツリーに類似していると考えられますが、大きな違いは、下向きのポインタが不変であるため、ノードのアトミック変更が簡略化されることです。この違いが、図[上記]に見られるように、スキップリストが激しい競合の下でツリーよりも優れている理由です。
この困難を克服することは、ブラウンらの最近の研究における主要な懸念事項でした。彼らは、 (マシンレベルの)CASを使用して実装されたLLX / SCXと呼ばれるマルチレコードLL / SC複合「プリミティブ」の構築に関する完全に別の(2013)ペーパー「ノンブロッキングデータ構造の実用的なプリミティブ」を持っています。ブラウン等。このLLX / SCXビルディングブロックを2014年(2011年ではなく)の並行ツリー実装で使用しました。
ここでは、「ホットスポットなし」/コンテンションフレンドリー(CF)スキップリストの基本的なアイデアを要約する価値があると思います。。これは、リラックスしたRBツリー(および同様の並行性データ構造)からの本質的なアイデアを採用しています。タワーは、挿入時にすぐに構築されるのではなく、競合がなくなるまで遅延されます。逆に、高い塔を削除すると、多くの競合が発生する可能性があります。これは、Pughの1990年の同時スキップリストペーパーまでさかのぼります。そのため、Pughは削除時にポインターの反転を導入しました(スキップリストに関するWikipediaのページでは、今日もなお言及されていません)。CFスキップリストはこれをさらに一歩進め、高い塔の上のレベルの削除を遅らせます。CFスキップリストの両方の種類の遅延操作は、(CASベースの)別のガベージコレクターのようなスレッドによって実行されます。このスレッドは、その作成者が「適応スレッド」と呼んでいます。
Synchrobenchコード(テストされたすべてのアルゴリズムを含む)は、https://github.com/gramoli/synchrobenchで入手できます。最新のブラウンら。実装(上記には含まれていません)はhttp://www.cs.toronto.edu/~tabrown/chromatic/ConcurrentChromaticTreeMap.javaで入手できます。32+コアマシンを利用できる人はいますか?J / K私のポイントは、これらを自分で実行できることです。
また、与えられた答えに加えて(バランスの取れたツリーに匹敵するパフォーマンスと組み合わせた実装の容易さ)。スキップリストには、実装内にリンクリストが事実上含まれているため、順序どおりのトラバーサル(順方向および逆方向)を実装する方がはるかに簡単です。
def iterate(node): for child in iterate(left(node)): yield child; yield node; for child in iterate(right(node)): yield child;
?=)。非ローカルコントロールiz awesom .. @ジョン:CPSで書くのは面倒ですが、継続を意味するのでしょうか?ジェネレーターは基本的にPythonの継続の特別なケースです。
実際には、プロジェクトでのBツリーのパフォーマンスは、スキップリストよりも優れていることがわかりました。スキップリストは、理解しやすいように見えるが、Bツリーを実装することはないんという難しいです。
私が知っている1つの利点は、一部の賢い人々が、アトミック操作のみを使用するロックフリーの同時スキップリストを実装する方法を考え出したことです。たとえば、Java 6にはConcurrentSkipListMapクラスが含まれており、頭がおかしい場合は、ソースコードを読み込むことができます。
しかし、同時Bツリーバリアントを記述することもそれほど難しくありません-私はそれを他の誰かが行ったのを見てきました-ツリーに沿って歩くときに「念のため」先制的にノードを分割してマージする場合、あなたはする必要はありませんデッドロックを心配し、ツリーの2つのレベルで一度にロックを保持する必要があるだけです。同期オーバーヘッドは少し高くなりますが、Bツリーの方がおそらく高速です。
あなたが引用したウィキペディアの記事から:
n(n)操作(昇順ですべてのノードにアクセスすることを強制する(リスト全体を印刷するなど))は、最適な方法でスキップリストのレベル構造の舞台裏のランダム化を実行する機会を提供します。スキップリストをO(log n)検索時間にします。[...]最近実行していない[そのような]Θ(n)操作のスキップリストは、常に可能であるため、従来の平衡型ツリーデータ構造と同じ絶対的な最悪の場合のパフォーマンス保証を提供しません(確率は非常に低いものの)、スキップリストの作成に使用されたコインフリップにより、バランスの悪い構造が生成される
編集:だからトレードオフです:スキップリストは、不均衡なツリーに退化するリスクがあるため、使用するメモリが少なくなります。
スキップリストはリストを使用して実装されます。
ロックフリーソリューションは、1つまたは2つのリンクリストに存在しますが、O(logn)データ構造にCASのみを直接使用するロックフリーソリューションはありません。
ただし、CASベースのリストを使用してスキップリストを作成できます。
(CASを使用して作成されたMCASは、任意のデータ構造を許可し、概念実証の赤黒木はMCASを使用して作成されていたことに注意してください)。
それで、奇妙なことに、それらは非常に便利であることがわかります:-)
スキップリストには、ロックストリッピングの利点があります。ただし、ランタイムは新しいノードのレベルがどのように決定されるかによって異なります。通常、これはRandom()を使用して行われます。56000語の辞書では、スキップリストはスプレイツリーよりも時間がかかり、ツリーはハッシュテーブルよりも時間がかかりました。最初の2つはハッシュテーブルのランタイムと一致しませんでした。また、ハッシュテーブルの配列も同時にロック解除できます。
参照の局所性が必要な場合は、スキップリストと同様の順序付きリストが使用されます。例:アプリケーションで日付の前後のフライトを検索する。
インメモリバイナリ検索スプレイツリーは優れており、頻繁に使用されます。