Нужна коррекция моего кода набора Мандельброта OpenMP

У меня есть следующий код набора Мандельброта в 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;
}

person Community    schedule 21.04.2013    source источник
comment
Вас также может заинтересовать код здесь Мандельброта в r с использованием rcpp openmp"> stackoverflow.com/questions/48069990/   -  person Tom Wenseleers    schedule 24.01.2018


Ответы (3)


Я не знаю внутренностей набора 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;
}
person Vinícius Gobbo A. de Oliveira    schedule 21.04.2013
comment
Да, я думал об этом, в то время как параллельно любой поток может закончить свое выполнение раньше другого потока. Но я действительно не знаю, как это исправить. Можете ли вы написать краткий пример кода о том, что вы имеете в виду? - person ; 22.04.2013
comment
Выложил какой-то код. Не очень хорошо написано, но идея дана (извините, тороплюсь... но все равно хотел помочь :) ). Пожалуйста, прокомментируйте здесь, если у вас остались какие-либо вопросы. - person Vinícius Gobbo A. de Oliveira; 22.04.2013
comment
Какой у меня будет размер матрицы, это будет размер моей картинки, то есть 600х400? Я отредактировал исходный код и внес несколько глупых изменений. Хотя теперь я получаю много ошибок. Я предполагаю, что они в основном касаются выделения и освобождения моей матрицы. - person ; 22.04.2013
comment
@erkant Точно! Ваш цикл распределения неверен. Взгляните на обновленный код. Какие ошибки вы получаете? Ошибки компиляции или выполнения? - person Vinícius Gobbo A. de Oliveira; 22.04.2013
comment
Хорошо, я снова изменил свой код, теперь он компилируется правильно, но, к сожалению, на этот раз, когда я пытаюсь открыть выходной файл .ppm с помощью gimp, я получаю следующую ошибку: Не удалось открыть «file.ppm»: плагин PNM Image не смог открыть изображение. Я изменил свой исходный пост в соответствии с моим текущим кодом. - person ; 22.04.2013
comment
Это какая-то проблема с программой, которая неправильно генерирует файл. Я не знаю, как работает формат PNM, поэтому мне не рекомендуется помогать с этим. Позже у меня будет свободное время, тогда я посмотрю, как работает этот формат, и внесу некоторые исправления. - person Vinícius Gobbo A. de Oliveira; 22.04.2013
comment
Кроме того, пожалуйста, опубликуйте свой код с выводом матрицы в файл, чтобы я мог ее отладить. - person Vinícius Gobbo A. de Oliveira; 22.04.2013
comment
Я также пытался использовать свою первую версию, которая не включала структуру rgb и матрицу m. И мне удалось заставить мою программу работать и правильно выводить файл, используя только директиву #pragma omp order, которая объясняется в другом ответе. Хотя на этот раз я не добился никакого ускорения. - person ; 22.04.2013
comment
Я только что заметил свои огромные ошибки, когда я закончил расчеты, я забыл пройтись по матрице и записать значения в файл. ЛОЛ, как глупо. Потому что я не видел кода для этой части в вашем ответе и совершенно забыл его. Можете ли вы также включить его для меня? - person ; 22.04.2013
comment
Хорошо, я написал и эту часть, я просматриваю матрицу и записываю их в файл, но она печатается три раза и смешивается. Я предполагаю, что мои циклы для записи их в файл неверны. - person ; 22.04.2013
comment
Используя приведенный выше код, я печатаю его три раза и поворачиваю на 90 градусов, я думаю, мне нужно изменить значения для x и y и сделать что-то еще, касающееся записи значений в файл. - person ; 22.04.2013
comment
@erkant Интересно ... здесь он напечатал это правильно, 600x400 пикселей ... или 400x600 ... - person Vinícius Gobbo A. de Oliveira; 22.04.2013
comment
@erkant Теперь я вижу! Это действительно проблема со смешением переменных X и Y. Простое и грязное исправление: сделайте так, чтобы ваш выходной код исправил эту проблему... - person Vinícius Gobbo A. de Oliveira; 22.04.2013
comment
Извините, я не получил ваш последний комментарий? - person ; 22.04.2013
comment
@erkant Пожалуйста, не принимайте во внимание мой последний комментарий. Я действительно не знаю, о чем я думал, когда публиковал это. - person Vinícius Gobbo A. de Oliveira; 22.04.2013
comment
Ох, ладно. Можете ли вы помочь мне решить эту проблему и заставить ее работать правильно? Если вы сейчас заняты, я могу подождать до завтра. - person ; 22.04.2013
comment
@erkant Думаю, я нашел это! Я не проверял, но, читая ваш код, я думаю, что проблема в функции color. Он путается с координатами. Переключите x и y в качестве индексов массивов, и это может сработать (измените m[x][y] на m[y][x]). - person Vinícius Gobbo A. de Oliveira; 23.04.2013
comment
Да, это сработало. Мне нужно было изменить y и x, как вы сказали, а затем везде в моей основной функции заменить 400 на 600 и наоборот. Теперь он работает отлично. Большое спасибо! - person ; 23.04.2013
comment
@erkant Хорошо! Отправьте еще раз, если вам нужно! - person Vinícius Gobbo A. de Oliveira; 23.04.2013
comment
Спасибо за все. Есть ли способ добиться большего ускорения с помощью моего кода OpenMP? - person ; 23.04.2013
comment
@erkant Есть несколько вещей, но не так много. Я не думаю, что вы можете достичь скорости намного выше, чем у вас уже есть. Но я говорю только о кодовой точке зрения, а не об алгоритме мандренбота. Я углублюсь в это завтра... в моей стране уже за полночь... - person Vinícius Gobbo A. de Oliveira; 25.04.2013
comment
@erkant Замените w, h, moveX, moveY, zoom константами или определениями. Вы также можете заменить константные выражения, такие как w / 2, на константы или определения (компилятор, вероятно, оптимизирует, но давайте поможем ему!). Упражнение на нахождение этих выражений (их не так много). С точки зрения кода, я считаю, что это все. Опубликуйте здесь улучшения производительности (обратите внимание, что компилятор, возможно, уже сделал это за вас, в зависимости от установленных вами флагов оптимизации...). - person Vinícius Gobbo A. de Oliveira; 26.04.2013

Ваша реализация ошибочна. Вы объявили много переменных, которые должны быть 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)
person Hristo Iliev    schedule 22.04.2013
comment
Спасибо за эту информацию, она помогла мне добиться некоторого ускорения. - person ; 22.04.2013

Если вы записываете в файл последовательно (что вы делали в своем исходном коде до его редактирования), вы можете использовать прагму ordered перед записью в файл. Это сделает изображение правильным, используя ваш исходный код. См. следующую ссылку: http://bisqwit.iki.fi/story/howto/openmp/#ExampleCalculatingTheMandelbrotFractalInParallel

Однако это не оптимальное решение. Оптимальное решение состоит в том, чтобы сначала записать в буфер памяти, а после того, как код Мандельброта закончит заполнение буфера, затем записать в файл (как вы делаете в своем новом коде).

У меня есть несколько предложений по ускорению вашего кода. Объедините цикл x и y (как показано в ссылке) и используйте динамическое расписание (также показано в этой ссылке), поскольку каждый пиксель занимает разное время. Наконец, используйте SSE/AVX для одновременной работы с двумя (SSE) или четырьмя (AVX) пикселями. В общей сложности вы должны получить ускорение более чем в 20 раз с OpenMP и SSE/AVX.

#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
    }
}
person Community    schedule 22.04.2013
comment
Я видел ссылку, спасибо за нее. Можете ли вы помочь мне добиться большего ускорения? - person ; 23.04.2013
comment
Я бы проверил следующую ссылку bealto.com/mp-mandelbrot.html . Однако он не использует AVX, поэтому вы можете немного ускорить его на процессоре. У меня есть код, который делает это, но я бы сначала заставил ваш код скейлера работать. Я планирую создать сайт с моими результатами в какой-то момент ... - person ; 23.04.2013