Генератор изображений Мандельброта и зритель

Это программа на C ++, которая выводит фрактальное изображение Мандельброта в графическое окно и позволяет пользователю увеличивать и панорамировать изображение, генерируя новое изображение каждый раз, когда пользователь делает это. Я использую SFML для графической части.

Поскольку он генерирует новое изображение после каждого масштабирования или панорамирования, он довольно медленный, особенно с изображением 1000x600 (для загрузки нового изображения может потребоваться до половины секунды).

#include <SFML/Graphics.hpp>
#include <cmath>

const int IMAGE_WIDTH = 1000;
const int IMAGE_HEIGHT = 600;
double zoom = 0.004; // allow the user to zoom in and out...
double offsetX = -0.7; // and move around
double offsetY = 0.0;
const int MAX = 127; // maximum number of iterations for mandelbrot()
                     // don't increase MAX or the colouring will look strange

int mandelbrot(double, double, int);
sf::Color getColor(int);

int main() {
    sf::RenderWindow window(sf::VideoMode(IMAGE_WIDTH, IMAGE_HEIGHT), "Mandelbrot");
    window.setFramerateLimit(30);

    sf::Image image;
    image.create(IMAGE_WIDTH, IMAGE_HEIGHT, sf::Color(0, 0, 0));
    sf::Texture texture;
    sf::Sprite sprite;

    bool stateChanged = true; // track whether the image needs to be regenerated

    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            switch (event.type) {
                case sf::Event::Closed:
                    window.close();
                    break;
                case sf::Event::KeyPressed:
                    stateChanged = true; // image needs to be recreated when the user changes zoom or offset
                    switch (event.key.code) {
                        case sf::Keyboard::Escape:
                            window.close();
                            break;
                        case sf::Keyboard::Equal:
                            zoom *= 0.9;
                            break;
                        case sf::Keyboard::Dash:
                            zoom /= 0.9;
                            break;
                        case sf::Keyboard::W:
                            offsetY -= 40 * zoom;
                            break;
                        case sf::Keyboard::S:
                            offsetY += 40 * zoom;
                            break;
                        case sf::Keyboard::A:
                            offsetX -= 40 * zoom;
                            break;
                        case sf::Keyboard::D:
                            offsetX += 40 * zoom;
                            break;
                        default: break;
                    }
                default:
                    break;
            }
        }

        if (stateChanged) { // only generate a new image if something has changed, to avoid unnecessary lag
            for (int x = 0; x < IMAGE_WIDTH; x++) {
                for (int y = 0; y < IMAGE_HEIGHT; y++) {
                    // convert x and y to the appropriate complex number
                    double real = (x - IMAGE_WIDTH / 2.0) * zoom + offsetX;
                    double imag = (y - IMAGE_HEIGHT / 2.0) * zoom + offsetY;
                    int value = mandelbrot(real, imag, MAX);
                    image.setPixel(x, y, getColor(value));
                }
            }
            texture.loadFromImage(image);
            sprite.setTexture(texture);
        }

        window.clear();
        window.draw(sprite);
        window.display();

        stateChanged = false;
    }

    return 0;
}

int mandelbrot(double startReal, double startImag, int maximum) {
    int counter = 0;
    double zReal = startReal;
    double zImag = startImag;
    double nextRe;

    while (pow(zReal, 2.0) + pow(zImag, 2.0) <= 4.0 && counter <= maximum) {
        nextRe = pow(zReal, 2.0) - pow(zImag, 2.0) + startReal;
        zImag = 2.0 * zReal * zImag + startImag;
        zReal = nextRe;
        if (zReal == startReal && zImag == startImag) { // a repetition indicates that the point is in the Mandelbrot set
            return -1; // points in the Mandelbrot set are represented by a return value of -1
        }
        counter += 1;
    }

    if (counter >= maximum) {
        return -1; // -1 is used here to indicate that the point lies within the Mandelbrot set
    } else {
        return counter; // returning the number of iterations allows for colouring
    }
}

sf::Color getColor(int iterations) {
    int r, g, b;

    if (iterations == -1) {
        r = 0;
        g = 0;
        b = 0;
    } else if (iterations == 0) {
        r = 255;
        g = 0;
        b = 0;
    } else {
        // colour gradient:      Red -> Blue -> Green -> Red -> Black
        // corresponding values:  0  ->  16  ->  32   -> 64  ->  127 (or -1)
        if (iterations < 16) {
            r = 16 * (16 - iterations);
            g = 0;
            b = 16 * iterations - 1;
        } else if (iterations < 32) {
            r = 0;
            g = 16 * (iterations - 16);
            b = 16 * (32 - iterations) - 1;
        } else if (iterations < 64) {
            r = 8 * (iterations - 32);
            g = 8 * (64 - iterations) - 1;
            b = 0;
        } else { // range is 64 - 127
            r = 255 - (iterations - 64) * 4;
            g = 0;
            b = 0;
        }
    }

    return sf::Color(r, g, b);
}

Окно сразу после его открытия:

 окно при открытии

Окно после увеличения (с помощью клавиши + ) и панорамирования вверх (с помощью клавиши W ):

 окно после масштабирования и панорамирования

40 голосов | спросил monopole 31 MarpmThu, 31 Mar 2016 15:16:51 +03002016-03-31T15:16:51+03:0003 2016, 15:16:51

7 ответов


42

Я вижу некоторые вещи, которые могут помочь вам улучшить ваш код.

Переместить инварианты цикла вне цикла

Чтобы максимизировать производительность, инварианты движущихся циклов вне цикла часто помогают. Это оптимизация, которую многие компиляторы могут создавать самостоятельно, но она помогает явно писать ее. В частности, вложенный цикл внутри main, где изображение пересчитывается. Прямо сейчас цикл выглядит так:

for (int x = 0; x < IMAGE_WIDTH; x++) {
    for (int y = 0; y < IMAGE_HEIGHT; y++) {
        // convert x and y to the appropriate complex number
        double real = (x - IMAGE_WIDTH / 2.0) * zoom + offsetX;
        double imag = (y - IMAGE_HEIGHT / 2.0) * zoom + offsetY;
        int value = mandelbrot(real, imag, MAX);
        image.setPixel(x, y, getColor(value));
    }
}

Однако, имея немного алгебры, мы видим, что вычисления real и imag могут быть переписаны как

double real = x * zoom - IMAGE_WIDTH / 2.0 * zoom + offsetX;
double imag = y * zoom - IMAGE_HEIGHT / 2.0 * zoom + offsetY;

Поскольку в цикле фактически изменяются только x и y, на самом деле просто заменить все это на:

double real = 0 * zoom - IMAGE_WIDTH / 2.0 * zoom + offsetX;
double imagstart = 0 * zoom - IMAGE_HEIGHT / 2.0 * zoom + offsetY;
for (int x = 0; x < IMAGE_WIDTH; x++, real += zoom) {
    double imag = imagstart;
    for (int y = 0; y < IMAGE_HEIGHT; y++, imag += zoom) {
        // convert x and y to the appropriate complex number
        int value = mandelbrot(real, imag, MAX);
        image.setPixel(x, y, getColor(value));
    }
}

Избегайте особых случаев

Значение -1 используется для представления точек в наборе Mandelbrot, но тогда это значение явно проверяется при назначении цвета и эквивалентно MAX. Почему бы просто не назначить значение MAX и избежать специального случая?

Предпочитает простой поиск кода

Функция getColor() получает одно число в диапазоне от 0 до MAX (если выполняется предыдущее предложение) и возвращает цвет. Почему бы не заменить это на поиск таблицы? Вычислите значения таблиц один раз (либо во время компиляции, либо во время выполнения), а затем используйте таблицу. В начале main у вас может получиться следующее:

std::array<sf::Color, MAX+1> colors;
for (int i=0; i <= MAX; ++i) {
    colors[i] = getColor(i);
}

Тогда цикл будет использовать это:

image.setPixel(x, y, colors[value]);

Не используйте == для чисел с плавающей запятой

Использование == с номерами с плавающей запятой, как правило, не очень хорошо, потому что, если они отличаются только небольшим количеством (скажем, 1е-99), они не равны. Фактически, если вы измените код, чтобы подсчитать количество раз, которое этот оператор оценивает как true, я думаю, вы обнаружите, что он никогда этого не делает, и поэтому просто тратит время.

Для более подробной информации о проблемах с плавающей запятой я бы рекомендовал отличную статью «Что каждый компьютерный ученый должен знать о плавающих, точной арифметики " Дэвида Голдберга.

Оптимизировать циклы для раннего спасения

Цикл mandelbrot можно переписать следующим образом:

int mandelbrot(double startReal, double startImag) {
    double zReal = startReal;
    double zImag = startImag;

    for (int counter = 0; counter < MAX; ++counter) {
        double r2 = zReal * zReal;
        double i2 = zImag * zImag;
        if (r2 + i2 > 4.0) {
            return counter;
        }
        zImag = 2.0 * zReal * zImag + startImag;
        zReal = r2 - i2 + startReal;
    }
    return MAX;
}

Здесь были внесены некоторые изменения. Во-первых, поскольку исходный третий параметр maximum всегда передавался со значением MAX, он просто был исключен из списка параметров и значение MAX подставляется непосредственно.

Далее, цикл теперь представляет собой цикл for вместо цикла while, который делает структуру более очевидной.

Затем значения r2 и i2 предварительно вычисляются, что делает код немного короче и проще (и, вероятно, более эффективным).

Наконец, раннее спасение выполняется в цикле. Только если мы дойдем до конца цикла, возвращается значение MAX.

Опустить работу не требуется

Вызов window.clear() в конце main не требуется, поскольку следующая строка все равно записывает все окно. Эта строка может быть просто устранена.

Устранить глобальные переменные

Все, кроме MAX, могут быть сделаны локально внутри main.

Устранить ограничение частоты кадров

Если нет причиныдля этого вы можете обнаружить, что устранение вызова window.setFramerateLimit или установка значения 0 позволяет программе быть более отзывчивой. После того, как я применил все предложения, упомянутые выше, я обнаружил, что ограничение частоты кадров - это то, что препятствует дальнейшему увеличению скорости на моей машине.

Не обновляйте экран, если не требуется

В настоящий момент любая клавиша (даже те, которые не имеют никакого эффекта, заставляют экран пересчитывать. Это легко можно установить, добавив stateChanged = false; к случаю по умолчанию в обработке ключевых событий switch.

Использовать потоки C ++ 11

Можно довольно безболезненно распараллелить этот код. Я изменил конец main, так что теперь он выглядит следующим образом:

        if (stateChanged) { 
            updateImage(zoom, offsetX, offsetY, image);
            texture.loadFromImage(image);
            sprite.setTexture(texture);
            stateChanged = false;
        }
        window.draw(sprite);
        window.display();
    }
}

Как вы можете видеть, вложенные циклы были заменены вызовом updateImage. Теперь он выглядит следующим образом:

void updateImage(double zoom, double offsetX, double offsetY, sf::Image& image) 
{
    std::vector<std::thread> threads;
    for (int i = 0; i < IMAGE_HEIGHT; i += 100) {
        threads.push_back(std::thread(updateImageSlice, zoom, offsetX, offsetY, 
            std::ref(image), i, i+100));
    }
    for (auto &t : threads) {
        t.join();
    }
}

updateImageSlice выполняет именно то, что он утверждает, и выглядит следующим образом:

void updateImageSlice(double zoom, double offsetX, double offsetY, sf::Image& image, int minY, int maxY) 
{
    double real = 0 * zoom - IMAGE_WIDTH / 2.0 * zoom + offsetX;
    double imagstart = minY * zoom - IMAGE_HEIGHT / 2.0 * zoom + offsetY;
    for (int x = 0; x < IMAGE_WIDTH; x++, real += zoom) {
        double imag = imagstart;
        for (int y = minY; y < maxY; y++, imag += zoom) {
            int value = mandelbrot(real, imag);
            image.setPixel(x, y, colors[value]);
        }
    }
}

При этом все заметно быстрее и хорошо выглядит, пока я не получу коэффициент масштабирования около 2e-16 или меньше, где есть заметная полоса из-за вышеупомянутых проблем с плавающей запятой. Более тщательная переработка вычислений может устранить или отложить это, но я не нашел времени для этого.

Также я должен указать, что совместное использование единственной ссылки на переменную image обычно не способ обрабатывать общие потоки, потому что это приводит, по крайней мере, к потенциалу гонки данных и коррупции. Однако, как я уже догадался, структура объекта image такова, что потоки, похоже, не противоречат тем же ячейкам памяти.

Пересмотренная версия

По запросу в комментариях, вот и все. Это происходит на несколько шагов дальше, поскольку большая часть этой программы превращается в объект Mandelbrot, а также масштабирует количество потоков на std::thread::hardware_concurrency. Для этого требуется компилятор, совместимый с C ++ 11.

#include <SFML/Graphics.hpp>
#include <array>
#include <vector>
#include <thread>

static constexpr int IMAGE_WIDTH = 1000;
static constexpr int IMAGE_HEIGHT = 600;

class Mandelbrot {
public:
    Mandelbrot();
    void updateImage(double zoom, double offsetX, double offsetY, sf::Image& image) const; 
private:
    static const int MAX = 127; // maximum number of iterations for mandelbrot()
                         // don't increase MAX or the colouring will look strange
    std::array<sf::Color, MAX+1> colors;

    int mandelbrot(double startReal, double startImag) const;
    sf::Color getColor(int iterations) const;
    void updateImageSlice(double zoom, double offsetX, double offsetY, sf::Image& image, int minY, int maxY) const;
};

Mandelbrot::Mandelbrot() {
    for (int i=0; i <= MAX; ++i) {
        colors[i] = getColor(i);
    }
}

int Mandelbrot::mandelbrot(double startReal, double startImag) const {
    double zReal = startReal;
    double zImag = startImag;

    for (int counter = 0; counter < MAX; ++counter) {
        double r2 = zReal * zReal;
        double i2 = zImag * zImag;
        if (r2 + i2 > 4.0) {
            return counter;
        }
        zImag = 2.0 * zReal * zImag + startImag;
        zReal = r2 - i2 + startReal;
    }
    return MAX;
}

sf::Color Mandelbrot::getColor(int iterations) const {
    int r, g, b;

    // colour gradient:      Red -> Blue -> Green -> Red -> Black
    // corresponding values:  0  ->  16  ->  32   -> 64  ->  127 (or -1)
    if (iterations < 16) {
        r = 16 * (16 - iterations);
        g = 0;
        b = 16 * iterations - 1;
    } else if (iterations < 32) {
        r = 0;
        g = 16 * (iterations - 16);
        b = 16 * (32 - iterations) - 1;
    } else if (iterations < 64) {
        r = 8 * (iterations - 32);
        g = 8 * (64 - iterations) - 1;
        b = 0;
    } else { // range is 64 - 127
        r = 255 - (iterations - 64) * 4;
        g = 0;
        b = 0;
    }
    return sf::Color(r, g, b);
}

void Mandelbrot::updateImageSlice(double zoom, double offsetX, double offsetY, sf::Image& image, int minY, int maxY) const
{
    double real = 0 * zoom - IMAGE_WIDTH / 2.0 * zoom + offsetX;
    double imagstart = minY * zoom - IMAGE_HEIGHT / 2.0 * zoom + offsetY;
    for (int x = 0; x < IMAGE_WIDTH; x++, real += zoom) {
        double imag = imagstart;
        for (int y = minY; y < maxY; y++, imag += zoom) {
            int value = mandelbrot(real, imag);
            image.setPixel(x, y, colors[value]);
        }
    }
}

void Mandelbrot::updateImage(double zoom, double offsetX, double offsetY, sf::Image& image) const
{
    const int STEP = IMAGE_HEIGHT/std::thread::hardware_concurrency();
    std::vector<std::thread> threads;
    for (int i = 0; i < IMAGE_HEIGHT; i += STEP) {
        threads.push_back(std::thread(&Mandelbrot::updateImageSlice, *this, zoom, offsetX, offsetY, std::ref(image), i, std::min(i+STEP, IMAGE_HEIGHT)));
    }
    for (auto &t : threads) {
        t.join();
    }
}

int main() {
    double offsetX = -0.7; // and move around
    double offsetY = 0.0;
    double zoom = 0.004; // allow the user to zoom in and out...
    Mandelbrot mb;

    sf::RenderWindow window(sf::VideoMode(IMAGE_WIDTH, IMAGE_HEIGHT), "Mandelbrot");
    window.setFramerateLimit(0);

    sf::Image image;
    image.create(IMAGE_WIDTH, IMAGE_HEIGHT, sf::Color(0, 0, 0));
    sf::Texture texture;
    sf::Sprite sprite;

    bool stateChanged = true; // track whether the image needs to be regenerated

    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            switch (event.type) {
                case sf::Event::Closed:
                    window.close();
                    break;
                case sf::Event::KeyPressed:
                    stateChanged = true; // image needs to be recreated when the user changes zoom or offset
                    switch (event.key.code) {
                        case sf::Keyboard::Escape:
                            window.close();
                            break;
                        case sf::Keyboard::Equal:
                            zoom *= 0.9;
                            break;
                        case sf::Keyboard::Dash:
                            zoom /= 0.9;
                            break;
                        case sf::Keyboard::W:
                            offsetY -= 40 * zoom;
                            break;
                        case sf::Keyboard::S:
                            offsetY += 40 * zoom;
                            break;
                        case sf::Keyboard::A:
                            offsetX -= 40 * zoom;
                            break;
                        case sf::Keyboard::D:
                            offsetX += 40 * zoom;
                            break;
                        default: 
                            stateChanged = false;
                            break;
                    }
                default:
                    break;
            }
        }

        if (stateChanged) { 
            mb.updateImage(zoom, offsetX, offsetY, image);
            texture.loadFromImage(image);
            sprite.setTexture(texture);
            stateChanged = false;
        }
        window.draw(sprite);
        window.display();
    }
}
ответил Edward 31 MarpmThu, 31 Mar 2016 18:26:11 +03002016-03-31T18:26:11+03:0006 2016, 18:26:11
18

Namings

  • В отличие от Python, имена all-caps обычно оставляются для макросов (которых вам следует избегать, кстати) на C ++; например: M_PI, CHAR_BIT и т. д.

Функции языка модема

Есть несколько мест, где вы можете воспользоваться современными возможностями C ++:

  • constexpr: чистые константы и магические числа должны быть объявлены как constexpr.

    static constexpr int image_width  = 1000;
    static constexpr int image_height = 600;
    
    static constexpr int max_iterations  = 127;
    

    Константы также могут быть собраны в пространстве имен (например, namespace constants)) или в разделе enum s.

  • Унифицированная инициализация : вы можете использовать его для упрощения некоторых частей, не записывая явные типы. В getColor предпочитайте:

    sf::Color getColor(int iterations) 
    {
         int r, g, b;
    
         // ...
    
         return {r, g, b};
    }
    

    То же самое верно для sf::RenderWindow /sf::Color:

    sf::RenderWindow window { {IMAGE_WIDTH, IMAGE_HEIGHT}, "Mandelbrot"};
    

Производительность

Небольшая оптимизация для getColor будет пытаться уменьшить условные присвоения, указав значения по умолчанию r, g, b и тем самым изменив их только при необходимости:

sf::Color getColor(int iterations) {
    int r = g = b = 0;

    if (iterations == -1) {
        // nothing to do

    } else if (iterations == 0) {
        r = 255;
        // g = 0;
        // b = 0;
    } else {
        // colour gradient:      Red -> Blue -> Green -> Red -> Black
        // corresponding values:  0  ->  16  ->  32   -> 64  ->  127 (or -1)
        if (iterations < 16) {
            r = 16 * (16 - iterations);
            // g = 0;
            b = 16 * iterations - 1;
        } else if (iterations < 32) {
            // r = 0;
            g = 16 * (iterations - 16);
            b = 16 * (32 - iterations) - 1;
        } else if (iterations < 64) {
            r = 8 * (iterations - 32);
            g = 8 * (64 - iterations) - 1;
            // b = 0;
        } else { // range is 64 - 127
            r = 255 - (iterations - 64) * 4;
            // g = 0;
            // b = 0;
        }
    } 

Другие сведения

  • Обычно следует избегать глобалов;
  • maximum mandelbrot может по умолчанию MAX, т. е. int mandelbrot(double startReal, double startImag, int maximum = ::MAX)
  • zoom должен иметь максимальный уровень, и поэтому вы должны убедиться, что он не сломан (double -s signed)).
ответил edmz 31 MarpmThu, 31 Mar 2016 16:58:12 +03002016-03-31T16:58:12+03:0004 2016, 16:58:12
13

Я буду комментировать в основном аспекты производительности вашего кода здесь. Стилистические части вашего кода должны быть улучшены кем-то более известным на C ++, чем я.

Магические числа

Вы определили некоторые константы, но когда вы вычисляете масштабирование, 0.9 и 40 являются «магическими числами», номера без объяснения того, что они делают, и те, использование которых влечет за собой обновление значений в нескольких местах при их изменении. Возможно, вы хотите сделать их константами и называть их ZOOM_DOWN_FACTOR и ZOOM_UP_FACTOR соответственно.

Производительность

  • Я понимаю, что компиляция с помощью -O3 и -ffastmath может заставить ваш компилятор превратить эти вызовы pow() в простые умножения, которые быстрее, поэтому компиляция для выпуска с этими параметрами. Чтобы компенсировать неточности с плавающей запятой, вы должны учитывать небольшое значение Epsilon (максимально допустимое отклонение) около 10 -15 . Попытайтесь изменить условие выкупа на <= 4.0 + 10 -15 .

  • Один указатель для более быстрого панорамирования: укажите код генерации изображения в другом методе из main(), который принимает параметры, определяющие диапазон пикселей изображения, которые должны быть подвергнуты Расчет Мандельброта. Вызовите эту функцию на каждом событии панорамирования с диапазоном пикселей, которые были недействительными из-за панорамирования, чтобы получить это изображение с пустыми пикселями и объединить его со старым изображением, чтобы получить развернутое изображение. Должна увеличивать скорость панорамирования.

Разное

  • counter >= maximum не требуется. == будет прекрасно работать.
  • Предложение: используйте return вместо break после выполнения window.close() в соответствующих операциях case , Следует избегать потенциальной утечки памяти. (Спасибо @pacmaninbw за то, что привлекли это к моему уведомлению).

Незначительные полезные советы от (разумно) опытного программиста фракталов:

(я, конечно. Посмотрите здесь , тем более, что вы знакомы с Java)

  • Внедрите подходящую форму сглаживания цвета. Линейная интерполяция должна помочь. Его немного дороже по производительности, но он значительно улучшает качество изображения. Экспоненциальное сглаживание должно помочь обеспечить подходящее значение интерполяции.

Это формула: expiter += exp(-cabs(z)-0.5/(cabs(zold - z))).

Здесь exp(x) является стандартной экспоненциальной функцией e ^ x, в вашей математической библиотеке. zold и z являются сложными числами, zold - это значение z из предыдущей итерации. cabs(z) возвращает квадрат модуля z (в основном сумма квадратов реальных и мнимых компонентов z). expiter - это дробное или гладкое количество итераций.

Для каждого компонента RGB применяйте линейную интерполяцию следующим образом:

value = toValue * expiter + fromValue * ( 1 - expiter). toValue и fromValue являются в основном значениями упомянутого компонента RGB для iterations и iterations + 1

ответил Tamoghna Chowdhury 31 MarpmThu, 31 Mar 2016 15:56:43 +03002016-03-31T15:56:43+03:0003 2016, 15:56:43
10

Использовать библиотечную функциональность

Изображения Мандельброта вычисляются с помощью комплексных чисел. C ++ предлагает заголовок <complex>, который содержит шаблон для генерации комплексных чисел из поплавков или других числовых типов.

Инициализировать при определении

Многие компиляторы могут дать вам предупреждения о путях кода с неинициализированными переменными, но в целом может быть полезно с нуля инициализировать ваш r, g и b до нуля в начале функции getColor. Это также позволяет удалить все последующие нулевые присвоения в if s.

Каскадный if s

Зачастую это плохая идея каскадировать if s так, как вы это делали. Поскольку ваш код использует равное расстояние в кратных 16, вы можете вместо этого использовать переключатель switch:

if(iterations < 0) {
} else {
    switch(iterations / 16) {
    case 0: // deal with 0-15
    break;
    case 1: // deal with 16-31
    break;
    case 2: // deal with 32-47
    /* no break! */
    case 3: // deal with 48-63
    break;
    default: // deal with iterations >= 64
    }
}

Распараллеливать

Результаты разных пикселей не зависят друг от друга. Это кричит о параллелизме. Достаточно использовать параллельный OpenMP для.

Разное

  • используйте ++counter вместо counter += 1; для увеличения (возможно, оптимизированного компилятором)
ответил Nobody 31 MarpmThu, 31 Mar 2016 15:59:28 +03002016-03-31T15:59:28+03:0003 2016, 15:59:28
5

Я должен начать с того, что это действительно классный и хорошо продуманный проект! Если вы хотите улучшить производительность своего кода, я рекомендую разгружать работу с шейдерами GPU. mandelbrot легко вписывается в описание шейдера фрагмента. Вы можете использовать OpenGL через SFML для достижения этого, см. это и это .

ответил jacwah 31 MarpmThu, 31 Mar 2016 16:05:09 +03002016-03-31T16:05:09+03:0004 2016, 16:05:09
3

Именование, незначительное, но полезное

int mandelbrot - эта функция возвращает число итераций, имя mandelbrot ничего не говорит о своей цели.

int counter = 0; - эта переменная содержит количество итераций. Имя numIterations было бы лучше, потому что оно объясняет гораздо больше

ответил Piotr Aleksander Chmielowski 1 AMpFri, 01 Apr 2016 11:14:41 +030014Friday 2016, 11:14:41
2

Я вижу, у вас уже есть много ответов о хороших практиках кода, таких как соглашения об именах и другие хорошие вещи. Вот несколько рекомендаций по оптимизации для вычислительных алгоритмов для современных процессоров в целом.

  • Заменяйте условные обозначения (если, в то время как в случае с переключателем) внутри циклов с арифметикой, когда это возможно. Это важно, чтобы не рисковать разрушением инструкций в современных процессорах.

Примеры в коде: if и whiles внутри функций mandelbrot и getColor.

  • Параметры оптимизации для вашего компилятора. Позвольте ему оптимизировать, попробовать и развернуть петли, если это возможно, помочь ему выбрать хорошие инструкции ассемблера, сообщив ему, какой у вас процессор. Позвольте ему выполнять встроенные вызовы функций, если вызовы функций находятся в циклах. Возможно, используйте быстрые инструкции и более низкую точность (например, float вместо double), если пониженная точность в порядке. Я подозреваю, что вы можете использовать быстрые инструкции без каких-либо проблем в этой конкретной программе.

Примеры в коде : позволяя компилятору делать inlining, может избежать разветвления на вызовы функций mandelbrot и getColor (которые находятся внутри циклов).

  • Планирование и структурирование памяти. Планируйте структуры данных и вычисления, чтобы использовать кэши оперативной памяти как можно больше. Разница между отсутствующими и попадающими кэшами может легко повлиять на производительность в сотни и сотни раз.
ответил mathreadler 2 PMpSat, 02 Apr 2016 19:32:18 +030032Saturday 2016, 19:32:18

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132