σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyロードマップスキル
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップスキル
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyロードマップスキル
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップスキル
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Lock-free スキル›モジュール 2 · アトミック操作・順序・保証›トピック 2.2·~1 時間 15 分上級

メモリモデル:acquireとrelease

スキル上の位置モジュール 2/4 · トピック 2/3
2.2 メモリモデル:acquireとrelease
—ラボスコア— / 100合計XP—連続日数—

読了 6分コード例 4件

  1. 01プロセッサは並べ替える
  2. 02すべての順序を実行する
  3. 03releaseとacquireは対で使う
  4. 04これから作るもの

プロセッサは、独立した2つの読み書きをどちらの順で実行してもよく、別のコアはその選択の結果を見ることがあります。SPYの価格を公開してからフラグを立てても、フラグを見た読み手が価格0を読むことがあります。書き手と読み手の順序付け4通りのうち、安全なのは1つだけで、書き手のreleaseと読み手のacquireの組み合わせです。

01

プロセッサは並べ替える

ストアバッファ、キャッシュ、コンパイラは、1つのスレッドからは区別できない限り、後の書き込みや読み出しを先のものより先に終わらせます。別のスレッドからは区別できます。このマシンはそれを直接モデル化します。スレッドは操作のリストで、順序制約を満たす操作ならどれでも任意の順に実行できます。同じアドレスへの操作は常に順序を保ちます。そうしないとスレッドが自分の書き込みを見られなくなるからです。acquireロードは後続すべてを止め、releaseストアはそれ以前のすべてを待ちます。2つの関数がすべてを表します。must_wait は操作が待つものを、eligible は次に実行してよいものを挙げます。

main.pyPython
def must_wait(ops, j):    waits = []    for i in range(j):        same = ops[i][1] == ops[j][1]        after_acquire = ops[i][0] == "load" and ops[i][3] == "acquire"        release = ops[j][0] == "store" and ops[j][3] == "release"        if same or after_acquire or release:            waits.append(i)    return waitsdef eligible(ops, done):    return [j for j in range(len(ops)) if j not in done and all(i in done for i in must_wait(ops, j))]writer = [("store", "data", 1, "relaxed"), ("store", "flag", 1, "release")]reader = [("load", "flag", "r1", "acquire"), ("load", "data", "r2", "relaxed")]print(must_wait(writer, 1), eligible(writer, set()), eligible(writer, {0}))print(must_wait(reader, 1), eligible(reader, set()))print(eligible([("load", "flag", "r1", "relaxed"), ("load", "data", "r2", "relaxed")], set()))
[0] [0] [1]
[0] [0]
[0, 1]

フラグのreleaseストアは位置0のデータストアを待つので、書き手は操作を順番にしか実行できません。フラグのacquireロードは後ろのデータロードを止めるので、読み手も順番に実行します。通常のロードなら両方が同時に実行可能で、読み手はデータを先に読むことがあります。

02

すべての順序を実行する

outcomes は、これまでのトピックの探索器をこの新しいマシン向けにしたものです。状態は、各スレッドが終えた操作、メモリ、レジスタです。ある状態から、各スレッドの実行可能な操作をすべて試し、実行するものがなくなったらレジスタが1つの結果になります。多くの順序が同じ状態に至るので、見た状態を覚えておきます。メッセージパッシングのテストは、データ、次にフラグの2つのストアで公開し、フラグ、次にデータの2つのロードで消費します。消費側が r1 == 1(フラグ)かつ r2 == 0(データがまだない)を見たら誤りで、これを reads_stale が確認します。

main.pyPython
def outcomes(programs, memory=None):    found = set()    seen = set()    def visit(done, mem, regs):        state = (tuple(frozenset(d) for d in done), tuple(sorted(mem.items())), tuple(sorted(regs.items())))        if state in seen:            return        seen.add(state)        moved = False        for tid, ops in enumerate(programs):            for j in eligible(ops, done[tid]):                moved = True                kind, address, x, _ = ops[j]                mem2, regs2 = dict(mem), dict(regs)                if kind == "store":                    mem2[address] = x                else:                    regs2[x] = mem.get(address, 0)                done2 = [set(d) for d in done]                done2[tid].add(j)                visit(done2, mem2, regs2)        if not moved:            found.add(tuple(sorted(regs.items())))    visit([set() for _ in programs], dict(memory or {}), {})    return founddef mp_programs(write_order, read_order):    return [        [("store", "data", 1, "relaxed"), ("store", "flag", 1, write_order)],        [("load", "flag", "r1", read_order), ("load", "data", "r2", "relaxed")],    ]def reads_stale(results):    return any(dict(outcome).get("r1") == 1 and dict(outcome).get("r2") == 0 for outcome in results)for pair in (("relaxed", "relaxed"), ("release", "acquire")):    results = outcomes(mp_programs(*pair))    print(pair, sorted(results), "stale:", reads_stale(results))
('relaxed', 'relaxed') [(('r1', 0), ('r2', 0)), (('r1', 0), ('r2', 1)), (('r1', 1), ('r2', 0)), (('r1', 1), ('r2', 1))] stale: True
('release', 'acquire') [(('r1', 0), ('r2', 0)), (('r1', 0), ('r2', 1)), (('r1', 1), ('r2', 1))] stale: False

relaxed の順序では、消費側は (r1, r2) の4通りすべて、古いデータを読む (1, 0) も含めて取りえます。フラグのストアにrelease、ロードにacquireを付けると3通りだけが残ります。フラグを見た読み手はデータも見ます。どちら側もフラグをまたいで並べ替えられないからです。

03

releaseとacquireは対で使う

片方だけで足りるでしょうか。4つの組み合わせをすべて試します。書き手だけのreleaseは失敗します。読み手がフラグより先にデータをロードするかもしれません。読み手だけのacquireも失敗します。書き手がデータより先にフラグをストアするかもしれません。組み合わせたときだけ成立し、次の表がそれを示します。これはC++の std::atomic やRustのrelease-acquireの契約で、ロックフリーキューがスロットをインデックスのreleaseストアで公開し、acquireロードで読む理由です。

main.pyPython
PAIRS = [("relaxed", "relaxed"), ("release", "relaxed"), ("relaxed", "acquire"), ("release", "acquire")]def ordering_table():    return {pair: reads_stale(outcomes(mp_programs(*pair))) for pair in PAIRS}for pair, stale in ordering_table().items():    print("write", pair[0], "| read", pair[1], "->", "can read stale data" if stale else "safe")
write relaxed | read relaxed -> can read stale data
write release | read relaxed -> can read stale data
write relaxed | read acquire -> can read stale data
write release | read acquire -> safe
acquireとreleaseは対であり、おまじないではない

同じ変数に対応するacquireのないreleaseは何も守りません。C++の実際のメモリオーダー seq_cst はより強くコストも高く、relaxed は最も安い代わりに順序を何も保証しません。上のテストが通る最も弱い順序を選び、探索器で証明しましょう。

main.pyPython
price = int(round(float(load_bars()["close"].iloc[0]) * 100))programs = mp_programs("release", "acquire")programs[0][0] = ("store", "data", price, "relaxed")print(price, sorted(outcomes(programs)))
9369 [(('r1', 0), ('r2', 0)), (('r1', 0), ('r2', 9369)), (('r1', 1), ('r2', 9369))]
実データが示すもの最初のSPY終値を9,369セントとしてフラグ経由で公開
relaxed + relaxed4結果。古い価格を読む可能性あり
releaseのみ4結果。古い価格を読む可能性あり
acquireのみ4結果。古い価格を読む可能性あり
release + acquire3結果。古い価格は読まない

SPYの最初の終値9,369セントでも、4つの構成のうち3つで、読み手はフラグを見ながら価格0を読むことがあります。この種のバグは、たまたま順序を守るマシンではすべてのテストを通り、別のマシンで現れます。だから安全な組み合わせは、探索器のように許されたすべての順序で証明しなければなりません。

定義release-acquireの対

フラグのreleaseストアと、それを読むacquireロード。書き手がreleaseより前に行ったことは、読み手からacquireの後に見えます。フラグでデータを公開することを正しくする、最も安い順序付けです。

04

これから作るもの

ラボは講義の順に進みます。操作が待つべきもの、次に実行してよい操作、スレッド群のすべての結果、メッセージパッシングのテスト、古いデータの検査、4つの順序付けの表、そして実際のSPY価格を公開する研究です。

  • 関数 · 問い前提
  • must_wait(ops, j)この操作は何を待つべきか?単純な算術
  • eligible(ops, done)次に実行できる操作は?単純な算術
  • outcomes(programs, memory)スレッドは何で終われるか?eligible
  • mp_programs(write_order, read_order)メッセージパッシングはどんな形か?単純な算術
  • reads_stale(results)読み手はデータなしでフラグを見られるか?単純な算術
  • ordering_table()どの順序付けの組が安全か?outcomesmp_programs
  • study(bars)どの組が実際のSPY価格を守るか?outcomesmp_programs

重要ポイント

  • 規則で禁じられない限り並べ替えは許される。 同じアドレスへの操作は順序を保ち、acquireロードとreleaseストアが壁を加えます。
  • 公開には対が必要。 フラグのストアにrelease、ロードにacquireなら結果は3通りで古い価格はなし。他の組は4通りで0を読むことがあります。
  • すべての順序で証明する。 1台のマシンのテストは運で通ることがあります。探索器は規則が許すすべての順序を確かめます。
クイックチェック
書き手はデータをストアし、次にフラグをreleaseストアします。読み手はフラグを通常(relaxed)のロードで、次にデータを通常のロードで読みます。読み手は、フラグが立っているのにデータがない状態を見られますか。
答えを選んでください。

次のトピック:ロックフリーな構造を作る道具と、それが何を保証するかを表す語彙が揃いました。進行保証では、obstruction-free、lock-free、wait-free の3段階に名前を付け、これまで書いた各カウンタを、最悪のタイミングで永久に止められたスレッドに対して試します。

ラボ:採点される内容

7タスク · 100点
  • 操作が待つもの15点
  • 次に実行できるもの10点
  • すべての結果25点
  • メッセージパッシングのテスト10点
  • 古いデータ5点
  • 4つの順序付け10点
  • SPYの表での研究25点
エクスプローラー · lab-lf-04
▾ lab-lf-04
▾ data
…
bash · ブラウザ内
learner@quantcraft:~/lab-lf-04$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-lf-04 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ