ぱと隊長日誌

ブログ運用もエンジニアとしての生き方も模索中

Hyper-V の仮想マシンで perf の "not supported" な項目を出力できるようにする

Hyper-V仮想マシンで perf の一部項目を出力できませんでした。これを構成バージョンの見直しとパフォーマンス監視コンポーネントを有効にすることで解決できました。解決までの流れを説明します。

プロセッサ Intel(R) Core(TM) i5-6600 CPU @ 3.30GHz
ホスト OS Windows 10 Pro 21H2
ゲスト OS Red Hat Enterprise Linux release 8.5

本記事での仮想マシン名と Hyper-V の構成バージョンの対応は以下の通りです。

仮想マシン 構成バージョン
MyOldVMName 9.0
MyNewVMName 9.2

コマンドの実行結果は一部省略しています。

構成バージョン:9.0 の仮想マシンで perf コマンドを実行しました。

# perf stat ls

 Performance counter stats for 'ls':

              1.00 msec task-clock                #    0.602 CPUs utilized
                 0      context-switches          #    0.000 K/sec
                 0      cpu-migrations            #    0.000 K/sec
               108      page-faults               #    0.108 M/sec
   <not supported>      cycles
   <not supported>      instructions
   <not supported>      branches
   <not supported>      branch-misses

       0.001657788 seconds time elapsed

       0.000804000 seconds user
       0.000835000 seconds sys

一部の項目が "not supported" になっています。これを解決できないかと調べを進めた結果、下記の記事にたどり着きました。
Intel のパフォーマンス監視ハードウェアを Hyper-V 仮想マシンで有効にする | Microsoft Docs

記事には「仮想マシンでパフォーマンス監視コンポーネントを有効にする」という節があり、これを適用することで改善する可能性がありそうです。

管理者権限で PowerShell を起動し、記事に記載のあったコマンドを実行しました。

PS> Set-VMProcessor MyOldVMName -Perfmon @("ipt", "pmu", "lbr", "pebs")
Set-VMProcessor : デバイス 'Processor' を変更できませんでした。
プロセッサの設定を変更できません。
'MyOldVMName' は、デバイス 'Processor' を変更できませんでした。(仮想マシン ID ~)
先に仮想マシンのバージョンをアップグレードしないと、プロセッサの設定 'EnablePerfmonIpt' を変更できません。(現在のバージョン: 9.0。必要なバージョン: 9.1)。

先の記事にもパフォーマンス監視コンポーネントを有効にするためのバージョン要件の記載がありました。

仮想マシンの世代は 9.1 以降でなければなりません。

Intel のパフォーマンス監視ハードウェアを Hyper-V 仮想マシンで有効にする | Microsoft Docs

ホストがサポートしている構成バージョンを確認します。

PS> Get-VMHostSupportedVersion

Name                                                  Version IsDefault
----                                                  ------- ---------
Microsoft Windows 10 Anniversary Update/Server 2016   8.0     False
Microsoft Windows 10 Creators Update                  8.1     False
Microsoft Windows 10 Fall Creators Update/Server 1709 8.2     False
Microsoft Windows 10 April 2018 Update/Server 1803    8.3     False
Microsoft Windows 10 October 2018 Update/Server 2019  9.0     True
Microsoft Windows 10 May 2019 Update/Server 1903      9.1     False
Microsoft Windows 10 May 2020 Update/Server 2003      9.2     False

構成バージョンとして 9.2 も利用可能と分かったため、構成バージョンのアップデートを試してみます。

PS> update-VMVersion MyOldVMName

警告: 仮想マシン "MyOldVMName" の構成バージョンが、既にこのサーバーでサポートされている最大レベルになっているため、更新は行われませんでした。クラスター化されたサーバーでは、サポートされる最大構成バージョンが、クラスター機能レベルによって制限される場合があります。

ですが、なぜかアップデートは失敗しました。

そこで、新規仮想マシンを構成バージョン 9.2 に指定して作成してみます。

PS> New-VM -Name "MyNewVMName" -Version 9.2 -Path "<path>" -Generation 2 -MemoryStartupBytes 8GB -NewVHDPath "<path>" -NewVHDSizeBytes 127GB

Name        State CPUUsage(%) MemoryAssigned(M) Uptime   Status     Version
----        ----- ----------- ----------------- ------   ------     -------
MyNewVMName Off   0           0                 00:00:00 正常稼働中 9.2

ホストのプロセッサである Intel Core i5-6600 はマイクロアーキテクチャが Skylake でした。
参考:インテル® Core™ i5-6600 プロセッサー

Skylake は PMU, PEBS, LBR をサポートしていることが下記の記事からわかります。
Configuring a Hyper-V* Virtual Machine for Hardware-Based Hotspots...

ホストの物理プロセッサでサポートされていないコンポーネントを有効にすると、仮想マシンの起動が失敗します。

Intel のパフォーマンス監視ハードウェアを Hyper-V 仮想マシンで有効にする | Microsoft Docs

記事にはこのような記載がありましたが、全部を有効にしてもこの後の仮想マシン起動は成功しました。

PS> Set-VMProcessor MyNewVMName -Perfmon @("ipt", "pmu", "lbr", "pebs")

新しい仮想マシンのセッティングを終えて perf を実行すると、先ほどは "not supported" と出力された項目も値が出力されるようになりました。

# perf stat ls

 Performance counter stats for 'ls':

              1.27 msec task-clock                #    0.392 CPUs utilized
                 1      context-switches          #    0.789 K/sec
                 0      cpu-migrations            #    0.000 K/sec
               106      page-faults               #    0.084 M/sec
         3,815,440      cycles                    #    3.009 GHz
         2,509,016      instructions              #    0.66  insn per cycle
           505,808      branches                  #  398.841 M/sec
            20,232      branch-misses             #    4.00% of all branches

       0.003231176 seconds time elapsed

       0.000941000 seconds user
       0.001247000 seconds sys

PostgreSQL のプリペアド文で汎用的な実行計画を選択するタイミング

PostgreSQL ではプリペアド文を繰り返し実行していると、汎用的な実行計画を作成することがあります。これにより、実行計画作成のオーバーヘッドを回避できます。

「汎用的な実行計画」と「独自の実行計画」のいずれを使うのかはマニュアルで説明されています。

デフォルト(すなわち、plan_cache_modeがautoに設定されている場合)では、パラメータのあるプリペアド文に対して、汎用的な計画を使うか独自の計画を使うかを、サーバは自動的に選択します。 これに対する現在の規則は、最初の5回が独自の計画で実行され、その計画の推定コストの平均が計算される、というものです。 それから汎用的な計画が作成され、その推定コストが独自の計画のコストの平均と比較されます。 そのコストが独自の計画のコストの平均よりもそれほど高くなければ、再計画を繰り返すことが望ましいと考えて、その後の実行は汎用的な計画を使います。

https://www.postgresql.jp/document/13/html/sql-prepare.html

汎用的な実行計画を選択されたかは EXPLAIN で確認できます。

プリペアド文に対してPostgreSQLが使用する問い合わせ計画を検証するためには、EXPLAIN、例えば

EXPLAIN EXECUTE stmt_name(parameter_values);

を使用してください。 汎用的な計画が使用される場合には、$nというパラメータ記号が含まれ、独自の計画が使用される場合は提供されたパラメータの値で置換されます。

https://www.postgresql.jp/document/13/html/sql-prepare.html

実行計画の選択について、マニュアルの説明をよりわかりやすく書き直した記事がありました。

プリペアド文は、汎用的な実行計画を作成し、その実行計画を利用するかどうかをPostgreSQL内部で自動的に判断しています。具体的には以下のように動作します。

1)プリペアド文で準備したSQLを繰り返し実行する時に、最初の5回目までは、毎回、パラメーターに合わせた実行計画(実行計画A1からA5とします)を作成します。

2)6回目以降に実行する時は、パラメーターの値が未設定である汎用的な実行計画(実行計画Bとします)を作成し、今まで作成した実行計画A1からA5までの平均コストと、実行計画Bのコストを比較して、今後、汎用的な実行計画を利用するか否かを判定します。

3)実行計画Bのコストが実行計画Aの平均コストよりもそれほど大きくない場合、それ以降のSQL実行で実行計画を毎回作成せず、汎用的な実行計画(実行計画B)を利用します。
あるいは、実行計画Bのコストが実行計画A1からA5までの平均コストよりもかなり大きい場合は、パラメーターに合わせた実行計画を毎回作成して利用します。

パフォーマンスチューニング9つの技 ~「探し」について~|PostgreSQLインサイド : 富士通

これらの説明から、6回目の実行でそれ以降に用いられる実行計画が確定するように読めるかもしれません。ですが、実際には7回目以降でも「独自の実行計画」から「汎用的な実行計画」に切り替わることがあります。

PostgreSQL 14.2 のソースコードから引用します(コメントの除外などを行っています)。
src/backend/utils/cache/plancache.c

static bool
choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
{
    double    avg_custom_cost;

    // 中略

    if (plansource->num_custom_plans < 5)
        return true;

    avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans;

    if (plansource->generic_cost < avg_custom_cost)
        return false;

    return true;
}

コードの custom は「独自の実行計画」、generic は「汎用的な実行計画」を指しています。

「独自の実行計画」が選択される度にコストと回数が total_custom_cost と num_custom_plans に加算されます。この平均コストが avg_custom_cost です。

5回目までは「独自の実行計画」が選択されます。

6回目以降は「独自の実行計画」の平均コストと「汎用的な実行計画」のコストを比較します。そこで「汎用的な実行計画」のコストのほうが低ければ、「汎用的な実行計画」が選択されます。この判定は6回目に限らず、7回目以降でも行われます。

一度でも「汎用的な実行計画」が選択されると「独自の実行計画」に戻ることはありません。なぜなら、「汎用的な実行計画」の選択では total_custom_cost と num_custom_plans に加算されず、条件判定で常に「汎用的な実行計画」が選択されるためです。

実際の挙動を確認します。PostgreSQL 14.2 で検証しました。

=# CREATE TABLE tab1 (id integer PRIMARY KEY);

=# INSERT INTO tab1 SELECT generate_series(1, 1000);

=# VACUUM ANALYZE;

=# PREPARE idplan (integer) AS SELECT * FROM tab1 WHERE id > $1;

=# EXPLAIN EXECUTE idplan(0); -- 5回繰り返し
                       QUERY PLAN
--------------------------------------------------------
 Seq Scan on tab1  (cost=0.00..17.50 rows=1000 width=4)
   Filter: (id > 0)
(2 rows)

=# EXPLAIN EXECUTE idplan(0); -- 6回目(汎用的な実行計画に切り替わる)
                                  QUERY PLAN
------------------------------------------------------------------------------
 Index Only Scan using tab1_pkey on tab1  (cost=0.28..14.10 rows=333 width=4)
   Index Cond: (id > $1)
(2 rows)

idplan(0) だけを繰り返すと、6回目で汎用的な実行計画($1 というパラメータ記号が含まれていることからわかります)に切り替わりました。

=# DEALLOCATE idplan;

=# PREPARE idplan (integer) AS SELECT * FROM tab1 WHERE id > $1;

=# EXPLAIN EXECUTE idplan(999); -- 10回繰り返し
                                QUERY PLAN
---------------------------------------------------------------------------
 Index Only Scan using tab1_pkey on tab1  (cost=0.28..4.29 rows=1 width=4)
   Index Cond: (id > 999)
(2 rows)

=# EXPLAIN EXECUTE idplan(0); -- 6回繰り返し
                       QUERY PLAN
--------------------------------------------------------
 Seq Scan on tab1  (cost=0.00..17.50 rows=1000 width=4)
   Filter: (id > 0)
(2 rows)

=# EXPLAIN EXECUTE idplan(0); -- 7回目(汎用的な実行計画に切り替わる)
                                  QUERY PLAN
------------------------------------------------------------------------------
 Index Only Scan using tab1_pkey on tab1  (cost=0.28..14.10 rows=333 width=4)
   Index Cond: (id > $1)
(2 rows)

idplan(999) だけを繰り返すと、独自の実行計画のままです。これは汎用的な実行計画よりも独自の実行計画のコストが低いためです。

idplan(999) を10回繰り返した後、 idplan(0) を繰り返すと7回目で汎用的な実行計画に切り替わりました。この結果より汎用的な実行計画の切り替わりは6回目に限らないとわかります。

なお、PostgreSQL 12 からプランナオプションの plan_cache_mode で「汎用的な実行計画」と「独自の実行計画」の選択を制御することもできます。

plan_cache_modeをforce_generic_planまたはforce_custom_planに設定して、サーバにそれぞれ汎用的な計画または独自の計画を使うように強制することで、この発見的手法を置き換えることができます。 汎用的な計画の実際のコストが独自の計画のものよりもずっと多い場合でも、汎用的な計画を選べるようになりますので、汎用的な計画のコスト推定が何らかの理由でひどく外れる場合に、この設定は主として有用です。

https://www.postgresql.jp/document/13/html/sql-prepare.html

DEIM2022 「近代的トランザクション処理技法」聴講メモ

本記事について

DEIM2022 (DEIM2022 第14回データ工学と情報マネジメントに関するフォーラム)
T3:近代的トランザクション処理技法
講演者:川島 英之(慶應義塾大学
の聴講メモです。

メモは口頭説明を中心にまとめています。資料を併せてご参照ください。

【公式サイト資料】
https://event.dbsj.org/deim2022/post/tutorial/deim2022_tutorial_T3_slides.pdf
※当日配布資料とは多少の差異がありました。本メモは当日配布資料をベースにしています。

概要

Silo(サイロ)は古い実装だが、その後に発表された技法とも引けを取らない。また、その後の技法も Silo をベースにしていることが多い。

Transaction

Transaction Processing System = Concurrency Control + Recovery

トランザクションは様々な場面で使われている。例えば、spotify で音楽を聴くときにも裏ではトランザクションが利用されている。

Why transaction?

ATM が以下の操作で構成されているとする。

  1. DB の値を Read でメモリ上に読み込む。
  2. メモリ上で値を操作する。
  3. メモリ上の値を Write で DB に書き戻す。

スライドの例では Company と You の操作が並行して行われている。3万円の口座から You が1万円を引き出し、Company が1万円を入金している。本来なら Company と You の操作完了後に口座は3万円となるべき。

だが、Company, You 共に初期値の3万円を Read したため、You 側の Write で2万円と書き込み、その後に Company 側の Write で4万円と書き込んでしまう。結果として口座が4万円になってしまった。

こういった事態を防ぐのがトランザクションである。

Lost Update

先ほどのような事象を Anomaly と呼ぶ。

Anomaly にもいろいろあるが、今回のは Lost Update と呼ばれる。

Isolation Levels & Anomalies

Serializable に実行できれば全ての Anomaly が発生しないとわかっている。
この Serializable な処理をいかに早く実行するかがポイントになる。

Serializability

Serializability は Serialize な実行と同等なもの。

Serialize な実行とは、トランザクション T1, T2 であれば T1 -> T2, T2 -> T1 のいずれかの順で実行されること。

Quiz How many serial orders?

トランザクションが N 個あったとすると、その組み合わせのパターンは N! となる。

Serializable schedules

実際のトランザクションは複数のトランザクションがもつれあいながら実行される。

Serializable

Serializable にはいくつかの種類がある。Conflict, Final State, View など。
今回は Conflict Serializable を取り上げる。

表記法の説明をする。
"r1(A)" は以下を意味する。

つまり、トランザクションID:1がデータID:Aをreadした、ということになる。

r1(A);w1(A);r2(A);w2(A);r1(B);w1(B);r2(B);w2(B);
このスケジュールはトランザクションID:1,2が混在しているので、Serial でないことは明らか。では、Serializable を満たせているのか?

Serial なスケジュールへ変換するために操作を交換していくが、スライドに "No!" と記載した操作は交換できない。

スライドに記載の青文字部分の操作は交換できる。そうすると、Serial なスケジュールに変換することができた。よって、Serializable といえる。

Quiz : Serializable?

Quiz-2 は "r2(y);w1(y);" と "w1(x);w2(x);" がある。
つまり、TX2 -> TX1, TX1 -> TX2 の関係があるため、これは Serializable でないといえる。

Concurrency Control Protocols

Pessimistic : 2PL
Optimistic : Silo

Strict 2 Phase Locking (S2PL)

2PL はシンプルだが、優れた設計といえる。Silo も 2PL の設計を参考にしている。Silo は 2PL の一部を楽観的にしたものといえる。

Kung-Robinson Model

Optimistic は commit コマンドを発行するタイミングで validate する。validate した結果、conflict がなければ良し!と判断する。

conflict は2つのケースで判断する。

  • 時間がずれている (Validation Case 1)
  • 異なるデータアイテムに対して read / write が行われる (Validation Case 2)
    • write x と read y なら問題ない

先の Quiz-2 の例は同じデータアイテムに対して操作を行っているため、conflict と判定される。

An Example

Validation Phase では誰と conflict する可能性があったかを調べる。この例では TX 4-6 と conflict する可能性がある。Validation Phase で conflict に問題がないと判断できてから TX に番号(この例では 7)を与える。

この手法は validation で GiantLock が必要になる。この GiantLock をなくしたのが Silo である。

Silo CC

Speedy transactions in multicore in-memory databases.

commit を発行すると Lock -> Validate -> Write が実行される。

プロダクションレベルの DB 実装としては、中園さんの LineairDB がある。
https://lineairdb.github.io/LineairDB/html/index.html

Silo commit protocol

スライドの例は Serializable でない。
よって、どちらかのトランザクションを abort しないといけない。

Silo commit protocol の例

write はローカルな領域に書き込んでいる。DB にはまだ書き込んでいない。

スライドの緑文字は T1、青文字は T2 の操作を示している。
L : Lock
V : Validate
W : Write

B が W1(B') で書き換えられたことを V2{B} の Validate で検知し、T2 を abort している。

このスライドの例で示しているように、commit protocol は複数のトランザクション(緑文字と青文字)がパラレルに実行される可能性がある。それでも正しく処理できることを保証している。

Silo Concurrency Control Protocol

"Lock records(DB). Sorting is for deadlock avoidance." を見ていく。

"sorted(W)" で write set を sort しているのは deadlock avoidance のため。

Deadlock

alice は x, y の順にアクセスし、bob は y, x の順にアクセスしたとする。

wl : write lock

alice の wla(y); は bob の wlb(y); にブロックされる。
bob の wlb(x); は alice の wla(x); にブロックされる。

つまり、2人ともロック待ちで止まってしまう。

Deadlock Detection

Deadlock の検出には Wait graph が用いられるが、この処理は重い。

Deadlock avoidance

そもそも Deadlock が発生しなければよい。

ロックする順序が同じであれば Deadlock にならない。
なので、Silo は write set をソートしている。

Silo Concurrency Control Protocol

"Record-TID and RS-TID are different? abort." を見ていく。

Phase 1 では write lock を獲得した。

Phase 2 では read したものが書き換えられていないか?を調べる。

Silo commit protocol

V2{B} で W1(B') が先行していることを検知し、abort する。

T2 が read したのは T0(初期値)。read すべき値は T1 の write した値だったので、T2 は abort になる。

Silo Concurrency Control Protocol

"Record is locked, abort." を見ていく。

Silo commit protocol

V2{B} の時点で L1(B) が lock を獲得しているので、T2 は abort になる。

なぜこれがダメなのかは 2PL として考える。

T2 は B の read lock を獲得しているつもりだったが、獲得できていなかった。となると、abort するしかないかもしれない(あくまで可能性)。そして、T2 は abort する。

Silo の論文では anti dependency をトラップしていない、という記述が頻出する。それは 2PL の一部を楽観的にしたということを意味している。

Quiz (Can a CC prevent following?)

S2PL も Silo も Non-Serializable Schedule を正しく判定できるか?
⇒いずれも正しく判定できる。

Quiz (Can Silo pass the following?)

スライドに記載された Serializable Schedule を Silo は False negative に判定してしまう。これをスケジュール空間が狭いという。

Why is Silo speedy?

スケジュール空間が狭いということから性能が悪いといえるのか?

1. Shorter blocking time.

典型的なベンチマークとして YCSB や TPC-E が用いられる。これらはショートトランザクションで評価している。ショートトランザクションで評価する場合、スケジュール空間よりもエンジニアリングとしてワークしているかが重要になる。

2PL は操作する前に lock を獲得するが、Silo は validation で lock を獲得している。これにより、Silo は lock する時間が短くて済む。

2. Native latch

latch は write mode しかない lock と考えて良い。

多くで使われる Centralized lock manager はハッシュテーブルで実装される。
これには以下の弱点がある。

  • ハッシュ構造全体に対してロックしなければいけないかもしれない。
  • 値の書き換えに CAS 操作が多発するため、キャッシュミスの確率が上がる。

この問題を解決するために "Native latch" を使う。レコード毎にラッチを設定し、CAS を使ってデータを切り替える。そうすると、ハッシュ構造のように複雑なデータ構造を使わなくて済む。

Figure1: Read-only, highly contended YCSB.

OCC と 2PL のスループットを比較したグラフ。OCC は Silo

Read-only なので conflict は発生しない。なのに、2PL は CPU core 数が増えると性能が落ちている。これは read lock の獲得・解放の度にカウンタ操作が必要になるため。異なる CPU Socket へのリモートアクセスが多発している。

Silo は read lock を獲得しないので性能が出る。

Concurrent index

Concurrent index をいかに高速に処理するかがトランザクション研究では極めて重要となっている。

B-link-tree

B-link-tree については「DBSJ最強データベース講義」で石川先生が講義されていた。

B-link-tree は leaf でしかロックを獲得しない。

TX1: insert (27), TX2: search (25) の状況を考える。

  1. TX1 が左のリーフノードのロックを獲得した。
  2. TX2 は左のリーフノードのロック待ちする。
  3. TX1 は 27 を insert した。この時、リーフノードはスプリットする。
  4. TX2 は左のリーフノードのロックを獲得する。
  5. TX2 は左のリーフノードから 25 を search するが見つからない。既にスプリットされているため。
  6. TX2 は右隣のリーフノード(中央のリーフノード)で 25 を search する。

Why deadlock? Two directions.

search は必ず右方向に行く。逆方向(左方向)に行くと deadlock が起きてしまう。

Hard to implement…Why are you there?

Concurrent index を実装するのはとても難しい。

正しく実装できていないとデータ順が "1,2,3" ではなく "1,3,2" のようになってしまう。デバッギングも困難。

PostgreSQL Code

PostgreSQL は実装で read lock を獲得している。これは実装が難しかったからか…?

Silo recovery

Motivation

リカバリのコンセプトは「ログを出力して歴史を繰り返せばよい」。

Performance of Parallel Write

スループットSSD (#thread:1) より SSD (#thread:8) のほうが高い。

マルチスレッドでランダムライトしたほうが速い。

P-WAL (2/3) Multiple WALfiles

従来のWALファイルは1つだけだった。
今後はWALファイルをN個用意して完全並列に出力する。

P-WAL (3/3) Early Lock Release

Early Lock Release (ELR) は Write log の前にデータを unlock する。単にこれを実装すると Dirty read するかもしれない。なので、Commit を遅らせるという手法を組み合わせる。

Coping with dirty read: notification controlThreads read data in secret to users…

T1 が Commit or Abort を発行した後に T2 が Commit を発行した。この時、T2 は競合するかもしれない T1 の結果が確定するまで、クライアントへの応答を待つ。

ログファイルが複数あるときの実現方法について。ワーカースレッド毎にカウンタ (F_LSN) を持たせる。スライド例では3つのワーカースレッドカウンタの最小値は 5 となっている。つまり、5 までの歴史は繰り返せる(戻せる)とわかる。これをもって、TX-5 までのクライアントには Commit の応答ができる。

Thinking about Locking

カウンタは処理が遅い。

Lock library の実装では遅すぎる。

Atomic Add だとまだましだが、50スレッド程度で性能が急激に落ちる。

Epoch based synchronization

Silo はシングルカウンタ (Epoch) を 40ms 毎に更新する。
Epoch は1スレッドだけが更新を行い、他のスレッドは読むだけ。

Epoch が切り替わるまで not durable な状態が続く。Epoch の更新が 40ms といっても、かなり多くのトランザクションがクライアントへの応答を待たされることになる。

Transaction Processing System = Concurrency Control + Recovery

Concurrency Control も Recovery もオーダー(順序)の概念がある。そして、Concurrency Control と Recovery は分離して動作している。

Place of the Epoch

Serialization point は安全な状態。

Silo は Phase 1 で lock を獲得した後に Epoch を読む。

Epoch は compiler-fence() に囲まれたタイミングで読まなければいけない。"Why not here?" と書かれたタイミングで読むと、Inverse Order が発生しうる。

これは single node なら問題にならない。HTAP で問題となる。

Modern CC and future directions

Really fast? (MOCC)

MOCC の速さ(スループットの高さ)には再現性があった。

Cache-Line-Conflicts

SI がとても遅い(スループットが低い)。SI はスケジュール空間が広いのに遅い理由は Centralized ordering が悪さをしているため。

Modern CC Research

Optimistic だと high contention で性能が出ないのでは?との指摘があり、調べてみた。Cicada は Silo に low contention のスループットで負けるが、high contention だと勝つ。

Future Directions

バッチは重要な分野だが、あまり研究されていない。今後の研究テーマとして面白いのではないか。