mcpField
M
memory-model

memory-model

github.com/mohitmishra786開発269647 回インストール18 週間前に更新
リポジトリ

memory-model について

memory-model は、C++ と Rust のメモリモデルを扱うエージェント向けスキルです。メモリ順序、happens-before、アトミック操作、フェンスを解説し、ロックフリー構造の実践パターンも示します。

主な機能

  1. 01C++ と Rust のメモリ順序解説
  2. 02happens-before 関係の説明
  3. 03アトミック操作とフェンスの案内
  4. 04ロックフリー構造の実践パターン
  5. 05よくある誤りと修正方法

活用シーン

  1. 01アトミック操作の順序選択
  2. 02acquire-release と seq_cst の理解
  3. 03スレッド間でのデータ公開
  4. 04ロックフリーキューやスピンロックの実装

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill memory-model

インストール後、Claude Code で/memory-modelを入力して読み込まれたか確認します。

同梱ファイルreferences/cpp-memory-ordering.md

詳細

メモリモデル

目的

C++ と Rust のメモリモデルについて、メモリ順序、happens-before 関係、アトミック操作、フェンス、ロックフリーデータ構造の実践的なパターンをエージェントに案内します。

トリガー

  • 「C++ のメモリモデルとは何ですか?」
  • 「アトミック操作にはどのメモリ順序を使えばよいですか?」
  • 「acquire-release と seq_cst の違いは何ですか?」
  • 「C++ で std::atomic を使うにはどうすればよいですか?」
  • 「Rust のアトミック操作における acquire/release とは何ですか?」
  • 「ロックフリーキューを実装するにはどうすればよいですか?」

ワークフロー

1. メモリ順序の概要

最新の CPU やコンパイラーは、性能向上のために操作の順序を入れ替えます。メモリモデルでは、許可される順序の入れ替えと、同期の実現方法を規定します。

Ordering strength (weakest to strongest):
Relaxed < Release/Acquire < AcqRel < SeqCst

Stronger ordering = more synchronization = more correct, but slower
Weaker ordering  = fewer barriers = faster, but needs careful analysis
2. メモリ順序
順序C++Rust意味
Relaxedmemory_order_relaxedOrdering::Relaxed順序の保証はなく、アトミック性のみ保証されます
Consumememory_order_consume(use Acquire)データ依存関係による順序付け
Acquirememory_order_acquireOrdering::Acquire対応する release より前のすべての書き込みが、このロードから見えます
Releasememory_order_releaseOrdering::Releaseこのストアより前のすべての書き込みが acquire から見えるようになります
AcqRelmemory_order_acq_relOrdering::AcqRelRMW 操作で acquire と release の両方を行います
SeqCstmemory_order_seq_cstOrdering::SeqCstすべての seq_cst 操作に対して全順序を保証します
3. C++ std::atomic
#include <atomic>
#include <thread>

std::atomic<int> counter{0};
std::atomic<bool> ready{false};

// Producer thread
void producer() {
    data = 42;                              // (1) write data
    ready.store(true, std::memory_order_release);  // (2) signal
}

// Consumer thread
void consumer() {
    while (!ready.load(std::memory_order_acquire));  // (3) wait
    assert(data == 42);                              // (4) guaranteed to see (1)
}

acquire-release の保証:スレッド A が X に release ストアを行い、スレッド B が A の値を読み取る acquire ロードを行った場合、release より前に A が行ったすべての書き込みは、acquire より後に B から見えるようになります。

4. 適切な順序の選択
Use case?
├── Counter (just needs atomicity, order irrelevant)    → Relaxed
├── Reference counting (decrement + final check)        → AcqRel (dec), Acquire (load 0 check)
├── Publish data from one thread to another             → Release (store), Acquire (load)
├── Mutual exclusion / mutex implementation             → AcqRel / SeqCst
├── Lock-free queue multiple producers/consumers        → SeqCst (safest to start)
└── Sequence number check (simple flag)                 → Release + Acquire
5. よくあるパターン
// Pattern 1: Spinlock
class Spinlock {
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
    void lock() {
        while (flag.test_and_set(std::memory_order_acquire))
            ; // spin
    }
    void unlock() {
        flag.clear(std::memory_order_release);
    }
};

// Pattern 2: Reference counting
class RefCounted {
    std::atomic<int> refcount{1};
public:
    void addref() {
        refcount.fetch_add(1, std::memory_order_relaxed);  // only need atomicity
    }
    void release() {
        if (refcount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
            // AcqRel ensures we see all writes from other releasers
            delete this;
        }
    }
};

// Pattern 3: One-time initialisation
class LazyInit {
    std::atomic<void*> ptr{nullptr};
    std::mutex mtx;
public:
    void* get() {
        void* p = ptr.load(std::memory_order_acquire);
        if (p == nullptr) {
            std::lock_guard lock(mtx);
            p = ptr.load(std::memory_order_relaxed);
            if (p == nullptr) {
                p = create();
                ptr.store(p, std::memory_order_release);
            }
        }
        return p;
    }
};
6. Rust のアトミック操作
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;

// Simple counter
let counter = Arc::new(AtomicUsize::new(0));

// Increment
counter.fetch_add(1, Ordering::Relaxed);

// Read
let val = counter.load(Ordering::Relaxed);

// Publish/subscribe pattern
static READY: AtomicBool = AtomicBool::new(false);

// Publisher thread
unsafe { DATA = 42; }  // Write data
READY.store(true, Ordering::Release);  // Signal

// Subscriber thread
while !READY.load(Ordering::Acquire) {}
let d = unsafe { DATA };  // Safe: guaranteed to see publisher's write
7. フェンス

フェンスは、特定の変数に対するアトミック操作を行わずに順序付けを提供します。

// C++ fence — equivalent to a global memory barrier
std::atomic_thread_fence(std::memory_order_acquire);  // Acquire fence
std::atomic_thread_fence(std::memory_order_release);  // Release fence

// Typical use: multiple atomic writes then one fence
relaxed_atomic_a.store(1, std::memory_order_relaxed);
relaxed_atomic_b.store(2, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);  // barrier for all above
sentinel.store(true, std::memory_order_relaxed);
8. よくある間違い
間違い修正方法
公開/購読に Relaxed を使うストアには Release、ロードには Acquire を使います
どこでも SeqCst を使うまずプロファイルを実行し、正しく動作する最も弱い順序を使います
非アトミックなロードはアトミックではないことを忘れる共有されるすべての変更可能なデータには、アトミックまたはミューテックスが必要です
C++ のスレッドセーフティに volatile を使うvolatile はメモリ順序を扱う機能ではありません。atomic を使います
SeqCst なしで逐次一貫性があると思い込むプラットフォームごとに既定の一貫性が異なります

メモリ順序の規則と happens-before のリファレンスについては、references/cpp-memory-ordering.md を参照してください。

関連スキル

  • skills/runtimes/sanitizers を使用 — TSan は、非アトミックアクセスを伴うデータ競合を検出します
  • Miri を使って Rust のメモリ順序違反を検出するには、skills/rust/rust-sanitizers-miri を使用します
  • 生成されたフェンス命令を理解するには、skills/low-level-programming/assembly-x86 を使用します
  • スレッドの検査を通じて並行プログラムをデバッグするには、skills/debuggers/gdb を使用します

コメント