3

偽共有のパフォーマンスへの影響をテストしようとしています。テストコードは以下のとおりです。

constexpr uint64_t loop = 1000000000;

struct no_padding_struct {
    no_padding_struct() :x(0), y(0) {}
    uint64_t x;
    uint64_t y;
};

struct padding_struct {
    padding_struct() :x(0), y(0) {}
    uint64_t x;
    char padding[64];
    uint64_t y;
};

alignas(64) volatile no_padding_struct n;
alignas(64) volatile padding_struct p;

constexpr core_a = 0;
constexpr core_b = 1;

void func(volatile uint64_t* addr, uint64_t b, uint64_t mask) {
    SetThreadAffinityMask(GetCurrentThread(), mask);
    for (uint64_t i = 0; i < loop; ++i) {
        *addr += b;
    }
}

void test1(uint64_t a, uint64_t b) {
    thread t1{ func, &n.x, a, 1<<core_a };
    thread t2{ func, &n.y, b, 1<<core_b };

    t1.join();
    t2.join();
}

void test2(uint64_t a, uint64_t b) {
    thread t1{ func, &p.x, a, 1<<core_a  };
    thread t2{ func, &p.y, b, 1<<core_b  };

    t1.join();
    t2.join();
}

int main() {
    uint64_t a, b;
    cin >> a >> b;


    auto start = std::chrono::system_clock::now();
    //test1(a, b);
    //test2(a, b);
    auto end = std::chrono::system_clock::now();
    cout << (end - start).count();
}

その結果、おおむね次のようになりました。

x86                                         x64             
cores    test1           test2              cores       test1        test2  
         debug  release  debug  release               debug release  debug  release
0-0      4.0s   2.8s     4.0s   2.8s        0-0       2.8s  2.8s     2.8s   2.8s
0-1      5.6s   6.1s     3.0s   1.5s        0-1       4.2s  7.8s     2.1s   1.5s
0-2      6.2s   1.8s     2.0s   1.4s        0-2       3.5s  2.0s     1.4s   1.4s
0-3      6.2s   1.8s     2.0s   1.4s        0-3       3.5s  2.0s     1.4s   1.4s
0-5      6.5s   1.8s     2.0s   1.4s        0-5       3.5s  2.0s     1.4s   1.4s

画像のテスト結果

私のCPUはintel core i7-9750hです。「core0」と「core1」は物理コアであり、「core2」と「core3」なども同様です。コンパイラは MSVC 14.24 を使用しました。

記録された時間は、多数のバックグラウンド タスクがあったため、複数回の実行での最高スコアのおおよその値です。結果は明確にグループに分けられ、0.1 秒~0.3 秒の誤差はグループ分けに影響を与えなかったので、これは十分公平だったと思います。

Test2 の説明は非常に簡単でした。xとは異なるキャッシュ ラインにあるためy、2 つの物理コアで実行するとパフォーマンスが 2 倍向上します (シングル コアで 2 つのスレッドを実行する場合のコンテキスト スイッチのコストはここでは無視できます)。物理コア、coffee-lake のスループットによって制限され (Ryzen の方がわずかに優れていると信じています)、一時的なマルチスレッドよりも効率的です。ここでは 64bit モードの方が効率が良いようです。

しかし、test1 の結果はわかりにくいです。まず、デバッグ モードでは、0-2、0-3、および 0-5 は 0-0 よりも遅く、これは理にかなっています。これは、特定のデータが L1 から L3 へ、L3 から L1 へと繰り返し移動されるためであると説明しました。これは、キャッシュが 2 つのコア間で一貫性を保つ必要があるためです。一方、単一のコアで実行している場合、キャッシュは常に L1 にとどまります。しかし、この理論は、0-1 ペアが常に最も遅いという事実と矛盾します。技術的には、2 つのスレッドは同じ L1 キャッシュを共有する必要があります。0-1 は 0-0 の 2 倍の速さで実行する必要があります。

次に、リリース モードでは、0-2、0-3、および 0-5 が 0-0 よりも高速であり、上記の理論が反証されました。

最後に、0-1 は64 ビット モードと 32 ビット モードの両方でreleaseより遅く実行されます。debugそれが一番理解できない。生成されたアセンブリ コードを読みましたが、何も役に立ちませんでした。

4

2 に答える 2