0

私は、スレッドとミューテックスを含む C の小さなプロジェクトに取り組んでいます。私が取り組んでいるプログラムは、bmp メイジにフィルターを適用します。プロジェクトの目標は、このコマンド ラインを処理できるプログラムを実装することです。

$ ./filter -f filter1[,filter2[,...]] -t numThreads1[,numThreads2[,...]] input-folder output-folder

ここで、-f は適用するフィルター (「赤」、「青」、「緑」、「グレースケール」、「ぼかし」) であり、-t はフィルターごとに割り当てられたスレッドの数です。

これまでのところ、データ競合で立ち往生しているぼかしを除いて、すべて問題ありません(または、そう思います)。ぼかしフィルターは次のように機能します。

/* Add a Gaussian blur to an image using
* this 3X3 matrix as weights matrix:
*   0.0  0.2  0.0
*   0.2  0.2  0.2
*   0.0  0.2  0.0
*
* If we consider the red component in this image
* (every element has a value between 0 and 255)
*
*   1  2  5  2  0  3
*      -------
*   3 |2  5  1| 6  0       0.0*2 + 0.2*5 + 0.0*1 +
*     |       |
*   4 |3  6  2| 1  4   ->  0.2*3 + 0.2*6 + 0.2*2 +   ->  3.2
*     |       |
*   0 |4  0  3| 4  2       0.0*4 + 0.2*0 + 0.0*3
*      -------
*   9  6  5  0  3  9
* 
* The new value of the pixel (3, 4) is round(3.2) = 3.
*
* If a pixel is outside the image, we increment the central pixel weight by 0.2
* So the new value of pixel (0, 0) is:
*   0.2 * 0 + 0.2 * 9 + 0.2 * 6 + 0.2 * 9 + 0.2 * 9 = 6.6 -> 7
*/

問題は、このぼかしフィルターを使用して「チェス盤」画像でプログラムを実行すると、次のようになります。

$ ./filter -f blur -t 8 chess.bmp chessBlur.bmp

この画像を取得することを期待していますが、これを取得しています「壊れた」行はランダムに異なります)

ミューテックスを使用してクリティカル セクションのロックとロック解除を行っていますが、ご覧のとおり、データ競合は依然として発生しています。フィルターに 2 語だけかけると、各スレッドに一度に 1 行ずつ、下から上へと順に表示されます。filter_blur の私のコードは次のとおりです。

int filter_blur(struct image *img, int nThread)
{
    int error = 0;
    int mod = img->height%nThread;
    if (mod > 0)
        mod = 1;

    pthread_t threads[nThread];
    pthread_mutex_t mutex;
    args arguments[nThread];

    struct image* img2 = (struct image*)malloc(sizeof(struct image));
    memcpy(img2,img,sizeof(struct image));

    error=pthread_mutex_init( &mutex, NULL);
    if(error!=0)
        err(error,"pthread_mutex_init");

    int i = 0;
    for (i=0; i<nThread; i++) {
        arguments[i].img2 = img2;
        arguments[i].mutex = &mutex;
    }

    int j = 0;
    for (i=0; i<(img->height)/nThread + mod; i++) {
        for (j=0; j<nThread; j++) {

            arguments[j].img = img; arguments[j].line = i*nThread + j;

            error=pthread_create(&threads[j],NULL,threadBlur,(void*)&arguments[j]);
            if(error!=0)
                err(error,"pthread_create");
        }
        for (j=0; j<nThread; j++) {
            error=pthread_join(threads[j],NULL);
            if(error!=0)
                err(error,"pthread_join");
        }
    }
    free(img2);
    return 0;
}

void* threadBlur(void* argument) {

    // unpacking arguments
    args* image = (args*)argument;
    struct image* img = image->img;
    struct image* img2 = image->img2;
    pthread_mutex_t* mutex = image->mutex;

    int error;
    int line = image->line;
    if (line < img->height) {
        int i;

        error=pthread_mutex_lock(mutex);
        if(error!=0)
            fprintf(stderr,"pthread_mutex_lock");

        for (i=0; i<img->width; i++) {
            img->pixels[line * img->width +i] = blur(img2,i,line);
        }

        error=pthread_mutex_unlock(mutex);
        if(error!=0)
            fprintf(stderr,"pthread_mutex_unlock");
    }
    pthread_exit(NULL);
}

struct pixel blur(struct image* img2, int x, int y) {
    double red = 0;
    double green = 0;
    double blue = 0;

    red=(double)img2->pixels[y * img2->width + x].r/5.0;
    green=(double)img2->pixels[y * img2->width + x].g/5.0;
    blue=(double)img2->pixels[y * img2->width + x].b/5.0;

    if (x != 0) {
        red+=(double)img2->pixels[y * img2->width + x - 1].r/5.0;
        green+=(double)img2->pixels[y * img2->width + x - 1].g/5.0;
        blue+=(double)img2->pixels[y * img2->width + x - 1].b/5.0;
    } else {
        red+=(double)img2->pixels[y * img2->width + x].r/5.0;
        green+=(double)img2->pixels[y * img2->width + x].g/5.0;
        blue+=(double)img2->pixels[y * img2->width + x].b/5.0;
    }

    if (x != img2->width - 1) {
        red+=(double)img2->pixels[y * img2->width + x + 1].r/5.0;
        green+=(double)img2->pixels[y * img2->width + x + 1].g/5.0;
        blue+=(double)img2->pixels[y * img2->width + x + 1].b/5.0;
    } else {
        red+=(double)img2->pixels[y * img2->width + x].r/5.0;
        green+=(double)img2->pixels[y * img2->width + x].g/5.0;
        blue+=(double)img2->pixels[y * img2->width + x].b/5.0;
    }

    if (y != 0) {
        red+=(double)img2->pixels[(y - 1) * img2->width + x].r/5.0;
        green+=(double)img2->pixels[(y - 1) * img2->width + x].g/5.0;
        blue+=(double)img2->pixels[(y - 1) * img2->width + x].b/5.0;
    } else {
        red+=(double)img2->pixels[y * img2->width + x].r/5.0;
        green+=(double)img2->pixels[y * img2->width + x].g/5.0;
        blue+=(double)img2->pixels[y * img2->width + x].b/5.0;
    }

    if (y != img2->height - 1) {
        red+=(double)img2->pixels[(y + 1) * img2->width + x].r/5.0;
        green+=(double)img2->pixels[(y + 1) * img2->width + x].g/5.0;
        blue+=(double)img2->pixels[(y + 1) * img2->width + x].b/5.0;
    } else {
        red+=(double)img2->pixels[y * img2->width + x].r/5.0;
        green+=(double)img2->pixels[y * img2->width + x].g/5.0;
        blue+=(double)img2->pixels[y * img2->width + x].b/5.0;
    }

    struct pixel pix = {(unsigned char)round(blue),(unsigned char)round(green),(unsigned char)round(red)};
    return pix;
}

編集1:

@job が正しく推測したように、問題は構造体の memcpy が原因でした (構造体はコピーされましたが、構造体内のポインターは元の構造体要素を指していました)。私はミューテックスも削除しました(私の問題を解決できると思ったので、ミューテックスはここにありました。申し訳ありませんが、悪いです)今、私のプロジェクトは魅力的に機能しています(処理速度とスレッドを使用する必要性についてまだ話し合うことができますが) )。私が言ったように、これはプロジェクトであり、私の C クラスの大学プロジェクトです。目標はフィルターを並列化することです。したがって、スレッドが必要です。

ありがとう!

4

2 に答える 2

0

まずはご協力ありがとうございます!私はあなたの答えのおかげで私のコードを修正することができました:-)

かなりの数のコメントが私のミューテックスの役に立たないことを指摘していたので、それらは私の問題の解決策というよりも、私のプログラムパフォーマンスのボトルネックのようなものだと思いました。それらが私の問題を魔法のように修正することを望んでいたので、それらを追加しただけです(プログラミングで奇跡が起こることがあります)。今ではそれらはなくなっており(それらは決して来るべきではありませんでした)、コードはより高速です!

元の問題に戻ります!ぼかしフィルターを適用するには、画像の読み取り専用コピーが必要でした。このコピーを入手するために、私は次のようにmemcpyを使用しました。

struct image* img2 = (struct image*)malloc(sizeof(struct image));
memcpy(img2,img,sizeof(struct image));

しかし、@ jopがこれを指摘したように、コピーしていても、コピーされた内部に割り当てられたメモリへimgのポインタは、元の配列を指しています。したがって、コピーする代わりに、コピーすることでうまくいきました。私はこれでコードを変更しました:pixelsimg2imgimg->pixels

struct pixel* pixels = (struct pixel*)malloc(sizeof(struct pixel)*img->width*img->height);
memcpy(pixels,img->pixels,sizeof(struct pixel)*img->width*img->height);

そして、voilà、問題が修正されました!だからみんなありがとう!

一部のコメントでは、スレッドを使用するかどうかについても説明しました。さて、この場合、プロジェクトの目標はいくつかの並列化された画像フィルターを作成することであるため、私には選択の余地がありません。そうそう、スレッドが必要です!。

于 2013-03-08T20:17:22.127 に答える
0

わかりました、これはあなたのコードに関する多くの観察ほど多くの答えではありません:

  • プログラムのどこかにある複数のスレッドから 1 つの特定のメモリ セルに実際にアクセスしているようには見えません。したがって、ミュートは必要ないように思われます。

  • あるいは、スレッドが同じメモリ セグメントにアクセスする可能性もあります。その場合、すべての計算を 1 つのスレッドで行うだけで、プログラムの効率が大幅に向上する可能性が非常に高くなります。このケースのベンチマークを行い、スレッド化されたバージョンと比較する必要があります。

  • 少なくとも私にとっては、ここでマルチスレッドが必要になる明確な理由はありません。これらの float 計算を 1 つのスレッドで行うと、OS が 2 番目のスレッドを生成する前に実行される可能性があります。ワークロードは、スレッド作成のオーバーヘッド時間と比較して重要ではありません。

  • 現在のマルチスレッド設計には欠陥があり、ミューテックスで保護されたコード内ですべての作業が行われています。ミューテックス ロックの外で実行できる実際の作業はないため、1000 のスレッドを作成しても、一度に実行できるスレッドは 1 つだけであり、他のスレッドは自分の順番を待ってスリープ状態になります。

于 2013-03-08T12:31:55.573 に答える