1

OpenMP で次のマンデルブロ セット コードがあります。私の C コードは問題なく動作し、生成される画像は完璧です。しかし、OpenMP を使用すると、正しくコンパイルおよび実行されますが、残念ながら、出力された .ppm ファイルを開くことができません。単に Gimp が読み取れないだけです。

// mandopenmp.c
// to compile: gcc -fopenmp mandopenmp.c -o mandopenmp -lm
// usage: ./mandopenmp <no_of_iterations> > output.ppm

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <omp.h>

typedef struct {
    int r, g, b;
} rgb;


void color(rgb **m, int x, int y, int red, int green, int blue)
{
    m[x][y].r = red;
    m[x][y].g = green;
    m[x][y].b = blue;
}

void mandelbrot(int niterations, rgb **m)
{
    int w = 600, h = 400, x, y, i;
    // each iteration, it calculates: newz = oldz*oldz + p, 
    // where p is the current pixel, and oldz stars at the origin
    double pr, pi;                   // real and imaginary part of the pixel p
    double newRe, newIm, oldRe, oldIm;   // real and imaginary parts of new and old z
    double zoom = 1, moveX = -0.5, moveY = 0; // you can change these to zoom and change position

    printf("P6\n# AUTHOR: Erkan Tairi\n");
    printf("%d %d\n255\n",w,h);

    //loop through every pixel
    #pragma omp parallel for private(x,i,pr,pi,newRe,newIm,oldRe,oldIm) schedule(dynamic, 1)
    for(y = 0; y < h; y++) {
        for(x = 0; x < w; x++) {
            // calculate the initial real and imaginary part of z, 
            // based on the pixel location and zoom and position values
            pr = 1.5 * (x - w / 2) / (0.5 * zoom * w) + moveX;
                pi = (y - h / 2) / (0.5 * zoom * h) + moveY;
                newRe = newIm = oldRe = oldIm = 0; //these should start at 0,0
                // start the iteration process
                for(i = 0; i < niterations; i++) {
                        // remember value of previous iteration
                        oldRe = newRe;
                        oldIm = newIm;
                        // the actual iteration, the real and imaginary part are calculated
                        newRe = oldRe * oldRe - oldIm * oldIm + pr;
                        newIm = 2 * oldRe * oldIm + pi;
                        // if the point is outside the circle with radius 2: stop
                        if((newRe * newRe + newIm * newIm) > 4) break;
                }
                if(i == niterations)
                color(m, x, y, 0, 0, 0); // black
            else
            {
                // normalized iteration count method for proper coloring
                double z = sqrt(newRe * newRe + newIm * newIm);
                int brightness = 256. * log2(1.75 + i - log2(log2(z))) / log2((double)niterations);
                color(m, x, y, brightness, brightness, 255);
            }
            }
    }
}

int main(int argc, char *argv[])
{
    int niterations, i, j;

    if(argc != 2)
    {
        printf("Usage: %s <no_of_iterations> > output.ppm\n", argv[0]);
        exit(1);
    }

    niterations = atoi(argv[1]);

    rgb **m;
    m = malloc(600 * sizeof(rgb *));
    for(i = 0; i < 600; i++)
        m[i] = malloc(400 * sizeof(rgb));

    double begin = omp_get_wtime();
    mandelbrot(niterations, m);

    for(i = 0; i < 600; i++) {
        for(j = 0; j < 400; j++) {
            fputc((char)m[i][j].r, stdout);
            fputc((char)m[i][j].g, stdout);
            fputc((char)m[i][j].b, stdout);
        }
    }

    double end = omp_get_wtime();

    double time_spent = end - begin;
    fprintf(stderr, "Elapsed time: %.2lf seconds.\n", time_spent);

    for(i = 0; i < 600; i++)
        free(m[i]);
    free(m);

    return 0;
}
4

3 に答える 3

4

Mandrelbot セットの内部構造はわかりませんが、プログラムのワークフローに基づいて試してみます。

おそらく、並列セクションで出力ファイルに色を書き込んでいるためです。これが意味することは、計算プロセスが終了するとピクセルが書き込まれるということですが、これは、ピクセルの計算プロセスがピクセルXの処理前に終了することを意味するものではありませんX+1

このようにして、ファイルへの書き込み中に、最初に (たとえば) pixel を書き込みX+1、次に pixelを書き込みX、色を混合することになります。

出力結果を行列に書き込んでみてください。関数を変更して、2 つのパラメーターをcolor追加し、ピクセルの座標を書き込む必要があります。ij

処理全体が終了し、すべてのピクセルが計算された後、マトリックスのピクセルを出力ファイルに書き込む必要があります。

コード:

typedef struct {
    int r, g, b;
} rgb;

void color(rgb **m, int x, int y, int red, int green, int blue) {
    m[x][y].r = red;
    m[x][y].g = green;
    m[x][y].b = blue;
}

void mandelbrot(rgb **m, int niterations) { // note the new argument, m.
    // and your code goes on and on... until:
            if ( i == niterations )
                color(m, x, y, 0, 0, 0);
            else {
                // normalized iteration count method for proper coloring
                double z = sqrt(newRe * newRe + newIm * newIm);
                int brightness = 256. * log2(1.75 + i - log2(log2(z))) / log2((double)niterations);
                color(m, x, y, brightness, brightness, 255);
            }
        }
    }
}

int main(int argc, char *argv[]) {
    // everything ok until...

    double begin = omp_get_wtime();

    rgb **m;
    m = malloc(sizeof(rgb*) * 600);
    for ( i = 0; i < 600; i++ ) {
        m[i] = malloc(400 * sizeof(rgb));

    // finally call mandelbrot!
    mandelbrot(m, niterations);
    double end = omp_get_wtime();

    // now that you have computed your set, you just walk the array writing the output to the file.

    for ( i = 0; i < 600; i++ ) {
        free(m[i]);
    }
    free(m);

    double time_spent = end - begin;
    fprintf(stderr, "Elapsed time: %.2lf seconds.\n", time_spent);

    return 0;
}
于 2013-04-21T22:12:23.163 に答える
1

あなたの実装には欠陥があります。代わりになけれprivateばならない多くの変数を宣言しました。sharedこれには、、、、が含まれprます。また、 とは、並列領域の外側のスコープで宣言されているため、デフォルトで共有されます。これらはすべて非公開にする必要があります。pinewRenewImoldReoldIm

#pragma omp parallel for private(x,i,pr,pi,newRe,newIm,oldRe,oldIm)

また、ループのデフォルトのスケジューリングparallel forは頻繁に (必ずしも常にではありません)staticです。これは、画像の各行または列を計算するのに異なる時間がかかるため、フラクタルなどには最適ではありません。したがって、最適なスピードアップが得られるまで、schedule(dynamic,1)節を適用してチャンク サイズ (この場合) を調整する必要があります。1

#pragma omp parallel for private(x,i,pr,pi,newRe,newIm,oldRe,oldIm) \
            schedule(dynamic,1)
于 2013-04-22T11:58:11.333 に答える
0

ファイルにシーケンシャルに書き込んでいる場合 (編集前の元のコードで行っていた) ordered、ファイルに書き込む前にプラグマを使用できます。これにより、元のコードを使用して画像が正しくなります。次のリンクを参照してください http://bisqwit.iki.fi/story/howto/openmp/#ExampleCalculatingTheMandelbrotFractalInParallel

ただし、これは最適なソリューションではありません。最適な解決策は、最初にメモリ バッファーに書き込み、マンデルブロ コードがバッファーの書き込みを完了してからファイルに書き込むことです (新しいコードで行うように)。

コードを高速化するための提案がいくつかあります。x ループと y ループを融合し (リンクに示されているように)、動的なスケジュールを使用します (そのリンクにも示されています)。これは、ピクセルごとに異なる時間がかかるためです。最後に、SSE/AVX を使用して、2 つ (SSE) または 4 つ (AVX) ピクセルを一度に操作します。OpenMP と SSE/AVX を使用すると、合計で 20 倍以上の速度が得られるはずです。

#pragma omp ordered {
    if(i == niterations)
        color(m, x, y, 0, 0, 0); // black - use original color function which writes to file
    else
    {
        // normalized iteration count method for proper coloring
        double z = sqrt(newRe * newRe + newIm * newIm);
        int brightness = 256. * log2(1.75 + i - log2(log2(z))) / log2((double)niterations);
        color(m, x, y, brightness, brightness, 255); //use original color function which writes to file
    }
}
于 2013-04-22T09:44:44.973 に答える