8

私は(学習目的で)c++0xスレッドを使用して配列の最大値を検索するためのプログラムを作成しました。実装には、標準スレッド将来のクラスを使用しました。ただし、並列化された関数は、並列化されていない関数と同じかそれよりも悪い実行時間を常に示します。

コードは以下のとおりです。データを1次元配列、多次元配列に格納しようとしましたが、最終的にはいくつかの配列になりました。ただし、良い結果が得られたオプションはありません。Eclipseとコマンドラインからコードをコンパイルして実行しようとしましたが、それでも成功しませんでした。アレイを使用せずに同様のテストも試しました。並列化では、20%の速度しか得られませんでした。私の観点からは、ロックがなく、リソース共有がほとんどない、非常に単純な並列プログラムを実行します(各スレッドは独自の配列で動作します)。ボトルネックとは何ですか?

私のマシンには、Ubuntu12.04を実行する8GBのRAMを搭載した2.2GHzのIntelCorei7プロセッサが搭載されています。

const int n = 100000000;

int a[n], b[n], c[n], d[n];

int find_max_usual() {
    int res = 0;
    for (int i = 0; i < n; ++i) {
        res = max(res, a[i]);
        res = max(res, b[i]);
        res = max(res, c[i]);
        res = max(res, d[i]);
    }
    return res;
}

int find_max(int *a) {
    int res = 0;
    for (int i = 0; i < n; ++i)
        res = max(res, a[i]);
    return res;
}

int find_max_parallel() {
    future<int> res_a = async(launch::async, find_max, a);
    future<int> res_b = async(launch::async, find_max, b);
    future<int> res_c = async(launch::async, find_max, c);
    future<int> res_d = async(launch::async, find_max, d);
    int res = max(max(res_a.get(), res_b.get()), max(res_c.get(), res_d.get()));
    return res;
}

double get_time() {
    timeval tim;
    gettimeofday(&tim, NULL);
    double t = tim.tv_sec + (tim.tv_usec / 1000000.0);
    return t;
}

int main() {
    for (int i = 0; i < n; ++i) {
        a[i] = rand();
        b[i] = rand();
        c[i] = rand();
        d[i] = rand();
    }
    double start = get_time();
    int x = find_max_usual();
    cerr << x << " " << get_time() - start << endl;
    start = get_time();
    x = find_max_parallel();
    cerr << x << " " << get_time() - start << endl;
    return 0;
}

タイミングは、find_max_parralelのほぼすべての時間がによって消費されることを示しました

int res = max(max(res_a.get(), res_b.get()), max(res_c.get(), res_d.get()));

コンパイルコマンドライン

g++ -O3 -std=c++0x -pthread x.cpp

アップデート。問題は解決しました。同じテストで望ましい結果が得られました。4スレッドは約3.3スピードアップ、3スレッドは約2.5スピードアップ、2スレッドは1.9スピードアップでほぼ理想的に動作します。いくつかの新しいアップデートでシステムを再起動しました。CPUの負荷と実行中のポーグラムに大きな違いは見られませんでした。

助けてくれてありがとう。

4

2 に答える 2

14

明示的に設定する必要がありますstd::launch::async

future<int> res_c = async(std::launch::async, find_max, c);

フラグを省略した場合、フラグstd::launch::async | std::launch::deferredは仮定され、タスクを非同期で開始するか延期するかを選択するのは実装に任されます。

gccの現在のバージョンではstd::launch::deferred、MSVCには、タスクの実行方法を実行時に決定するランタイムスケジューラがあります。

また、試してみたい場合は、次の点にも注意してください。

std::async(find_max, c);

std::futureのデストラクタはタスクが終了するのを待つため、これもブロックされます。

于 2012-11-30T15:17:46.913 に答える
3

gcc-4.7.1で同じテストを実行したところ、スレッドバージョンは約4倍高速です(4コアサーバーの場合)。したがって、問題は明らかにstd :: futureの実装ではなく、環境に最適ではないスレッド設定の選択にあります。上で述べたように、テストはCPUではなく、メモリを大量に消費するため、ボトルネックは間違いなくメモリアクセスです。スレッドを適切にベンチマークするために、CPUを集中的に使用するテスト(PI番号を高精度で計算するなど)を実行することをお勧めします。

さまざまなスレッド数とさまざまなアレイサイズを試してみないと、ボトルネックがどこにあるかを正確に言うのは難しいですが、おそらくいくつかの問題があります。-2チャネルのメモリコントローラー(2または3のいずれか)を使用している可能性があります。したがって、2スレッドを超えると、メモリアクセスに関する追加の競合が発生します。したがって、ロックやリソース共有がないというあなたの主張は正しくありません。ハードウェアレベルでは、同時メモリアクセスに関する競合があります。-非並列バージョンは、データをキャッシュにプリフェッチすることで効率的に最適化されます。一方、並列バージョンでは、集中的なコンテキストスイッチングが発生し、その結果、CPUキャッシュがスラッシングする可能性があります。

両方の要因について、スレッド数を2に減らすと、スピードアップが見られる可能性があります。

于 2012-12-01T01:53:36.957 に答える