[英]Need correction to my OpenMP Mandelbrot set code
我在OpenMP中有以下Mandelbrot設置代碼。 我的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;
}
我不了解Mandrelbot集的內部原理,但是我將根據您的程序工作流程進行介紹。
可能是因為您在並行部分中正在將顏色寫入輸出文件。 這意味着您的像素將在計算過程完成時被寫入,但這並不意味着像素X
的計算過程將在像素X+1
的處理之前結束。
這樣,在寫入文件時,您將首先寫入(例如)像素X+1
,然后寫入像素X
,從而混合顏色。
嘗試將輸出結果寫入矩陣。 您將不得不更改color
功能,將兩個參數i
和j
與要寫入像素的坐標相加。
整個處理完成並計算完每個像素后,應將矩陣的像素寫入輸出文件。
編碼:
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;
}
您的實現存在缺陷。 您已經聲明了許多必須private
變量才能shared
。 這包括pr
, pi
, newRe
, newIm
。 默認情況下, oldRe
和oldIm
也是共享的,因為它們是在並行區域外部的作用域中聲明的。 這些都應該是私有的:
#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)
如果要順序寫入文件(在編輯之前是在原始代碼中進行的操作),則可以在寫入文件之前使用ordered
編譯指示。 使用原始代碼可以使圖像正確。 請參閱以下鏈接http://bisqwit.iki.fi/story/howto/openmp/#ExampleCalculatingTheMandelbrotFractalInParallel
但是,這不是最佳解決方案。 最佳解決方案是先寫入內存緩沖區,然后在mandelbrot代碼完成填充緩沖區后再寫入文件(就像在新代碼中一樣)。
我有一些建議可以加快您的代碼速度。 融合x和y循環(如鏈接中所示)並使用時間表動態(如該鏈接中所示),因為每個像素花費不同的時間。 最后,使用SSE / AVX一次操作兩個(SSE)或四個(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
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.