8

以下のコードを並列化したい:

for(int c=0; c<n; ++c) {
    Work(someArray, c);
}

私はそれをこのようにしました:

#include <thread>
#include <vector>

auto iterationsPerCore = n/numCPU;
std::vector<std::future<void>> futures;

for(auto th = 0; th < numCPU; ++th) {
    for(auto n = th * iterationsPerCore; n < (th+1) * iterationsPerCore; ++n) {
        auto ftr = std::async( std::launch::deferred | std::launch::async,
            [n, iterationsPerCore, someArray]()
            {
                for(auto m = n; m < n + iterationsPerCore; ++m)
                    Work(someArray, m);
            }
        );
        futures.push_back(std::move(ftr));
    }

    for(auto& ftr : futures)
        ftr.wait();
}

// rest of iterations: n%iterationsPerCore
for(auto r = numCPU * iterationsPerCore; r < n; ++r)
    Work(someArray, r);

問題は、Intel CPUでは50%しか高速に動作しないのに対し、AMDでは300%高速に動作することです。3つのIntelCPU(Nehalem 2core + HT、Sandy Bridge 2core + HT、Ivy Brigde 4core + HT)で実行します。AMDプロセッサはPhenomIIx2で、4コアのロックが解除されています。2コアのIntelプロセッサでは、4つのスレッドで50%高速に実行されます。4コアでは、4スレッドでも50%高速に実行されます。VS2012、Windows7でテストしています。

8スレッドで試してみると、Intelのシリアルループよりも8倍遅くなっています。HTが原因だと思います。

あなたはそれについてどう思いますか?そのような行動の理由は何ですか?たぶんコードが正しくありませんか?

4

3 に答える 3

5

偽の共有が疑われます。これは、2 つの変数が同じキャッシュ ラインを共有している場合に発生します。操作がよりきめ細かい場合でも、キャッシュは特定のサイズのキャッシュ ラインに関してのみ操作できるため、事実上、それらに対するすべての操作は、同時にアクセスされない場合でも、非常にコストのかかる同期を行う必要があります。AMD ハードウェアは単に回復力が高いか、これに対処するための異なるハードウェア設計を備えているのではないかと思います。

テストするには、各コアが 64 バイトの倍数であるチャンクでのみ動作するようにコードを変更します。Intel CPU には 64 バイトのキャッシュ ラインしかないため、これによりキャッシュ ラインの共有が回避されます。

于 2012-12-12T22:19:58.867 に答える
2

また、CPU キャッシュに注意する必要があります。これは、このトピックに関する良い記事です。

短いバージョン: ハードウェアはデータをキャッシュしますが、同じメモリ (SomeArray) で作業している場合、CPU のキャッシュ間で常に同期する必要があり、シングル スレッドの方法よりも実行が遅くなる可能性さえあります。

于 2012-12-12T22:15:26.320 に答える
2

コンパイルされたすべてのコードで分岐の数を最小限に抑えるには、コンパイラの設定を変更する必要があると思います。2 つの異なる CPU スタイルには、異なる操作先読み設定があります。コードがコンパイルされる CPU ではなく、ターゲットCPUに一致するようにコンパイラの最適化設定を変更する必要があります。

于 2012-12-12T21:10:51.650 に答える