3

私はC++で書いた素数ジェネレーターのマルチスレッド化について研究しようとしてきましたが、私がやりたいことは「並列処理」と呼ばれていることを発見しました。私は過去約45分間これを研究してきましたが、私はそれを理解できないようです。

これを実行したいコードは約95行で、ここに投稿するには長すぎますが、これが基本的な概念です。

unsigned long long i, total;

for(i;true;i++){
    total = total + i;
    cout << "Your new total is " << total << endl;
}

これを2つのプロセッサにストリーミングして、競合するのではなく連携させる方法はありますか?もしそうなら、私はそれをどのようにコーディングしますか?私はC++にある程度精通していますが、まだわからないことがたくさんあるので、詳細な回答をいただければ幸いです。

編集:最初は間違った種類のアルゴリズム。これだと思います。

編集2:多くの答えが私のアルゴリズムに依存すると言っているので、95行しかないのでコードを投稿します。

/*Generic GPL stuff, coded by me */

#include <iostream>
#include <list>
#include <fstream>
using namespace std;

int main(){
    //Declare some variables and what not.
    unsigned long long count = 0, misc = 0, length = 0, limit = 0;
    list <long long> primes;
    ifstream inFile;
    ofstream outFile;

    cout << "Initializing starting values based on your existing file of generated prime numbers.\n";

    //Now let's get our starting values;
    inFile.open("/home/user/Desktop/primes.txt");

    //First, we need to find the prime generator thus far
    for(unsigned long long x=0;inFile.good();x++){
        inFile >> count;

        if(!(bool)(x%100000000) && x!=0){
            misc = x/100000000;

            cout << misc << "00000000 primes read so far...\n";
        }
    }

    inFile.close();

    cout << "Highest generated prime found.\n";

    //Now, as much as I hate to say it, we need to parse part of the file again now that we have the largest prime.
    inFile.open("/media/ssd/primes_src.txt");

    for(length; limit < count; length++){
        inFile >> misc;
    }

    inFile.close();

    limit = misc * misc;

    cout << "Initialization complete. Now generating primes.\n";

    //Loop time
    l:

    //We're just going to flat-out skip even numbers
    count++;
    count++;

    //This checks to see if the number it's trying to test is beyond the current limit of accuracy.
    if(count >= limit){

        // Now if we are, we have 1 more possible prime factor
        length++;

        inFile.open("/media/ssd/primes_src.txt");

        for(unsigned long long x=0; x < length; x++){
            inFile >> misc;
        }

        inFile.close();

        limit = misc * misc;
    }

    inFile.open("/media/ssd/primes_src.txt");
    inFile >> misc; //We don't care about 2

    for(unsigned long long x=1; x < length; x++){
        inFile >> misc;

        if(!(bool)(count%misc)){
            inFile.close();

            goto l;
        }
    }

    inFile.close();

    outFile.open("/home/user/Desktop/primes.txt", ios::out | ios::app);

    //Now if we haven't been "goto"d, we add it to the file.
    outFile << count << endl;

    outFile.close();

    goto l;

    return 0;
}

/home/user/Desktop/primes.txtは、生成されたすべての素数を保持する私のファイルです。
/media/ssd/primes_src.txtは、2^32までのすべての素数と1つの素数を適切に保持するファイルです。

4

4 に答える 4

1

と仮定するとi = iterator、示されているコードは、の値がtotalforループの以前の反復に依存しないようなものです。あなたのアルゴリズムは、多くの努力なしに並列化できるようです。

これを行う最も簡単な方法は、コンパイラオプションでOpenMPを有効にしてから、forループの直前に次のコードを追加することです。

#pragma omp parallel for
for(...)

この回答は、アルゴリズムの各反復が前の反復に依存していないことを前提としていることに注意してください(そうでない場合は、競合状態を防ぐためにコードを入力する必要があります)。

編集:アルゴリズムは簡単に並列化できなくなりました。いくつかのメモ:

  • 計算を独立したチャンクに分割できる場合、アルゴリズムは簡単に並列化できます(チャンクごとに1つのスレッド)
  • アルゴリズムが古いデータを変更せずに、また新しいデータの状態を読み取らずに新しいデータを作成する場合、それも並列化可能です
  • n - 1反復を実行できるようにするために反復の結果が必要な場合は、失敗しnます。ここでの最良のオプションは、紙と鉛筆を取り、数学的に(または論理的に)別の方法でアルゴリズムをフォーマットしようとすることです(つまり、アルゴリズムを変更します!)。
于 2012-12-25T21:31:17.397 に答える
1

あなたのアルゴリズムがこの方法に適しているかどうかはわかりませんが、並列作業を行った1つの方法は、「次の候補」を更新する1つのポイントを除いて、すべてが完全に独立して実行される複数のスレッドを作成することです(私は計算していました)奇妙な数字なので、私の更新はi = __sync_fetch_and_add(&current, 2);-現在は「これまでの数字プロセス」です。__sync_fetch_and_add()はg ++の標準関数ですが、Microsoftコンパイラには同じ種類のものがありInterLockedAdd()ます。

「ベンチマーク」を実行したとき、マシンの4コア(100%= 1コア)から400%の改善がわずかにありました。

プレーンなpthread_create()を使用し、入力から指定された範囲の「最大」に達すると、各スレッドが終了します。

約束通り:単純な素数ファインダー:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <pthread.h>

using namespace std;

static int current;
static int max_value = 7780;

static void *find_prime(void *)
{
    for(;;)
    {
        int i = __sync_fetch_and_add(&current, 2);
        bool prime = true;

        if (i > max_value)
        {
            pthread_exit(NULL);
        }
        for(int j = 2; j < i && prime; j++)
        {
            if (!(i % j))
            {
                prime = false;
            }
        }
        if (prime)
        {
            cout << i << " " << flush;
        }
    }
}


int main(int argc, char **argv)
{
    int    start = 3;
    int    threads = 1;
    pthread_t *thread_id;

    for(int i = 1; i < argc; i++)
    {
        if (strcmp(argv[i], "-t") == 0 && argc > i+1)
        {
            i++;
            threads = strtol(argv[i], NULL, 0);
        }
        if (strcmp(argv[i], "-e") == 0 && argc > i+1)
        {
            i++;
            max_value = strtol(argv[i], NULL, 0);
        }
    }

    current = start;

    cout << "1 2 " << flush;

    thread_id = new pthread_t[threads-1];
    for(int i = 0; i < threads; i++)
    {
        int rc = pthread_create(&thread_id[i], NULL, find_prime, NULL);
        if (rc != 0)
        {
            cerr << "Huh? Pthread couldn't be created. rc=" << rc << endl;
        }
    }
    for(int i = 0; i < threads; i++)
    {
        pthread_join(thread_id[i], NULL);
    }
    cout << endl;
}

コメント:メインはスレッドの「スレッド」数を開始します(-t numコマンドラインで指定されます- -e num「最大」を定義するもあります)。各スレッドは、__ sync_fetch_and_add()関数を使用して数値を「選択」します。スレッドはそれが素数であるかどうかをチェックし、次にjを繰り返して数を除算しようとします。数値が素数の場合は印刷され、そうでない場合は次の数値を選択します。

必要に応じて、数値を出力する代わりに[十分に大きな数値を指定するとcout <<、スレッド内からの呼び出しで問題が発生する可能性があります]、代わりに配列を使用して、int my_index = __sync_fetch_and_add(&index、1);を使用できます。それを使用して配列に格納します。

当然、各ループを完全に独立して実行できない場合、このメソッドは機能しません。その場合、処理はさらに複雑になります。

編集:このコードには多くの有用なエラーチェックが欠けていることに注意してください。スレッドをゼロにすると、何も実行されません。負の最終値を指定すると、誰が知っているかなどです。

$ time ./prime -t 1 -e 100000> / dev / null

real    0m5.574s
user    0m5.553s
sys     0m0.009s

and:time ./prime -t 4 -e 100000> / dev / null

real    0m1.762s
user    0m5.572s
sys     0m0.010s

ご覧のとおり、4倍高速です。

于 2012-12-25T21:40:48.083 に答える
0

これを並列化する唯一の方法は、N個の合計を追跡し、ループの後でそれらを合計することです。または、追加がより複雑な関数を表す場合は、共有変数へのアクセスにミューテックスを使用してみてください。これはおそらくパフォーマンスの面で吸うでしょう...

于 2012-12-25T21:43:59.810 に答える
0

openMPを使用して素数を計算するこのコードを確認できます

于 2012-12-25T22:19:14.117 に答える