2013-04-21 2 views
1

다음 Mandelbrot 세트 코드가 OpenMP에 있습니다. 내 C 코드는 잘 동작하며, 생성 된 그림은 완벽합니다. 하지만 OpenMP를 사용하면 컴파일되고 올바르게 실행되지만 불행하게도 출력 .ppm 파일을 열 수는 없으므로 단순히 Gimp에서 읽을 수는 없습니다.OpenMP Mandelbrot 세트 코드 수정 필요

// 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; 
} 
+0

당신은 여기 https://stackoverflow.com/questions/48069990/multithreaded-simd-vectorized-mandelbrot-in-r-using-rcpp-openmp –

답변

4

나는 Mandrelbot 세트의 내부를 잘 모르겠지만, 나는 당신의 프로그램 워크 플로우를 기반으로 총을 줄 것이다.

아마 그것은 병렬 섹션에서 출력 파일에 색상을 쓰고 있기 때문입니다. 즉, 계산 프로세스가 완료되면 픽셀이 쓰여지지만 픽셀 X의 계산 프로세스가 픽셀 X+1 처리 전에 끝나는 것은 아닙니다.

이렇게하면 파일에 쓰는 동안 첫 번째 픽셀 (예 : X+1)을 작성한 다음 픽셀을 X으로 작성하고 색을 혼합합니다.

출력 결과를 매트릭스에 쓰십시오. 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; 
} 
+0

예 코드에 관심이있을 수 있습니다, 나는 생각 병렬 적으로 어떤 스레드도 다른 스레드보다 먼저 실행을 마칠 수 있습니다. 그러나 나는 그것을 고치는 법을 정말로 모른다. 당신이 의미하는 바에 대한 짧은 코드 예제를 작성할 수 있습니까? –

+0

일부 코드를 게시했습니다. 별로 잘 쓰여지지는 않았지만 아이디어는 주어집니다 (미안 해요, 저는 서둘러 ...하지만 어쨌든 도와주고 싶었습니다 :)). 아직 궁금한 점이 있으시면 여기에 의견을 남기십시오. –

+0

매트릭스 크기는 어떻게됩니까? 600x400을 의미하는 내 그림 크기가됩니까? 내 원래 코드를 편집하고 바보 같은 변화를 만들었습니다. 비록 지금은 많은 오류가 발생합니다. 나는 그들이 주로 매트릭스를 할당하고 할당 해제하는 것과 관련 있다고 생각한다. –

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 
    } 
} 
+0

나는 링크를 보았다. 그것에 감사한다. 더 빠른 속도 향상을 도울 수 있습니까? –

+0

다음 링크를 확인해 보겠습니다. http://www.bealto.com/mp-mandelbrot.html. 그러나 AVX를 사용하지 않으므로 CPU에서 속도를 상당히 높일 수 있습니다. 나는 코드를 가지고 있지만 스케일러 코드가 먼저 작동하도록 할 것입니다. 내 결과로 어떤 지점에 사이트를 올릴 계획입니다 ... –

1

구현에 결함이 있습니다. private이어야하는 많은 변수를 shared으로 대신 선언했습니다. 여기에는 pr, pi, newRe, newIm이 포함됩니다. 또한 oldReoldIm은 병렬 영역 외부의 범위에서 선언되므로 기본적으로 공유됩니다. 이들은 모두 개인 대신해야한다 :

#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) 
+0

이 정보를 보내 주셔서 감사합니다. 일부 속도 향상에 도움이되었습니다. –