13

プロセッサ x86/x86_64 で命令 LFENCE を行う意味はありますか?に対する以前の回答からわかるように、Sequential ConsistencySFENCEの代わりに使用することはできません。MFENCE

MFENCEそこの答えは、 = SFENCE+ LFENCE、つまり、LFENCEそれなしでは Sequential Consistency を提供できないことを示唆しています。

LFENCE並べ替えを不可能にします:

SFENCE
LFENCE
MOV reg, [addr]

-- へ -->

MOV reg, [addr]
SFENCE
LFENCE

たとえば、メカニズムによって提供されるMOV [addr], reg LFENCE--> の並べ替え - Store Bufferは、パフォーマンス向上のために Store - Loads を並べ替えますが、それを妨げないためです。そして、このメカニズムを無効にしますLFENCE MOV [addr], regLFENCESFENCE

LFENCE並べ替えを不可能にするメカニズムは何ですか(x86 にはメカニズムがありません - Invalidate-Queue)?

また、SFENCE MOV reg, [addr]-->の並べ替えはMOV reg, [addr] SFENCE理論上のみ可能ですか、それとも実際には可能ですか? そして、可能であれば、実際には、どのようなメカニズムで、どのように機能するのでしょうか?

4

3 に答える 3

5

一般的に MFENCE != SFENCE + LFENCE です。たとえば、次のコードを でコンパイルすると-DBROKEN、一部の Westmere および Sandy Bridge システムでは失敗しますが、Ryzen では動作するように見えます。実際、AMD システムでは SFENCE だけで十分なようです。

#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
using namespace std;

#define ITERATIONS (10000000)
class minircu {
        public:
                minircu() : rv_(0), wv_(0) {}
                class lock_guard {
                        minircu& _r;
                        const std::size_t _id;
                        public:
                        lock_guard(minircu& r, std::size_t id) : _r(r), _id(id) { _r.rlock(_id); }
                        ~lock_guard() { _r.runlock(_id); }
                };
                void synchronize() {
                        wv_.store(-1, std::memory_order_seq_cst);
                        while(rv_.load(std::memory_order_relaxed) & wv_.load(std::memory_order_acquire));
                }
        private:
                void rlock(std::size_t id) {
                        rab_[id].store(1, std::memory_order_relaxed);
#ifndef BROKEN
                        __asm__ __volatile__ ("mfence;" : : : "memory");
#else
                        __asm__ __volatile__ ("sfence; lfence;" : : : "memory");
#endif
                }
                void runlock(std::size_t id) {
                        rab_[id].store(0, std::memory_order_release);
                        wab_[id].store(0, std::memory_order_release);
                }
                union alignas(64) {
                        std::atomic<uint64_t>           rv_;
                        std::atomic<unsigned char>      rab_[8];
                };
                union alignas(8) {
                        std::atomic<uint64_t>           wv_;
                        std::atomic<unsigned char>      wab_[8];
                };
};

minircu r;

std::atomic<int> shared_values[2];
std::atomic<std::atomic<int>*> pvalue(shared_values);
std::atomic<uint64_t> total(0);

void r_thread(std::size_t id) {
    uint64_t subtotal = 0;
    for(size_t i = 0; i < ITERATIONS; ++i) {
                minircu::lock_guard l(r, id);
                subtotal += (*pvalue).load(memory_order_acquire);
    }
    total += subtotal;
}

void wr_thread() {
    for (size_t i = 1; i < (ITERATIONS/10); ++i) {
                std::atomic<int>* o = pvalue.load(memory_order_relaxed);
                std::atomic<int>* p = shared_values + i % 2;
                p->store(1, memory_order_release);
                pvalue.store(p, memory_order_release);

                r.synchronize();
                o->store(0, memory_order_relaxed); // should not be visible to readers
    }
}

int main(int argc, char* argv[]) {
    std::vector<std::thread> vec_thread;
    shared_values[0] = shared_values[1] = 1;
    std::size_t readers = (argc > 1) ? ::atoi(argv[1]) : 8;
    if (readers > 8) {
        std::cout << "maximum number of readers is " << 8 << std::endl; return 0;
    } else
        std::cout << readers << " readers" << std::endl;

    vec_thread.emplace_back( [=]() { wr_thread(); } );
    for(size_t i = 0; i < readers; ++i)
        vec_thread.emplace_back( [=]() { r_thread(i); } );
    for(auto &i: vec_thread) i.join();

    std::cout << "total = " << total << ", expecting " << readers * ITERATIONS << std::endl;
    return 0;
}
于 2018-05-14T01:57:53.973 に答える
3

LFENCE を無効にして並べ替えを不可能にするメカニズムは何ですか (x86 にはメカニズムがありません - Invalidate-Queue)?

Intel のマニュアル、ボリューム 2A、ページ 3-464 のLFENCE手順については、次のドキュメントを参照してください。

LFENCE は、前のすべての命令がローカルで完了するまで実行されず、LFENCE が完了するまで後の命令は実行を開始しません。

はい、あなたの例の並べ替えは、LFENCE命令によって明示的に妨げられています。命令のみを含む 2 番目の例は、ロード操作に影響を与えないSFENCEため、有効な並べ替えです。SFENCE

于 2015-04-10T02:16:08.163 に答える