Фрактальная оптимизация Ньютона

Я реализовал фрактал Ньютона на C. Мне было интересно, знает ли кто-нибудь, как я могу повысить эффективность кода без использования потоков или нескольких процессов. Я попытался развернуть петли while, но это мало что дало. Я также пытался использовать -o1, -o2 и -o3 при компиляции, это тоже мало что дало. Если у вас есть какие-либо предложения, пожалуйста, дайте мне знать, спасибо.

typedef struct s_float2 {
    float   x;
    float   y;
}   t_float2;

static t_float2 next(t_float2 z)
{
    t_float2    new;
    float       temp_deno;

    temp_deno = 3 * pow((pow(z.x, 2) + pow(z.y, 2)), 2);
    new.x = ((pow(z.x, 5) + 2 * pow(z.x, 3) * pow(z.y, 2)
                - pow(z.x, 2) + z.x * pow(z.y, 4) + pow(z.y, 2)) / temp_deno);
    new.y = ((z.y * (pow(z.x, 4) + 2 * pow(z.x, 2)
                    * pow(z.y, 2) + 2 * z.x + pow(z.y, 4))) / temp_deno);
    z.x -= new.x;
    z.y -= new.y;
    return (z);
}

static t_float2 find_diff(t_float2 z, t_float2 root)
{
    t_float2    new;

    new.x = z.x - root.x;
    new.y = z.y - root.y;
    return (new);
}

void    init_roots(t_float2 *roots)
{
    static int  flag;

    flag = 0;
    if (flag == 0)
    {
        roots[0] = (t_float2){ 1, 0 };
        roots[1] = (t_float2){ -0.5, sqrt(3) / 2 };
        roots[2] = (t_float2){ -0.5, -sqrt(3) / 2 };
        flag = 1;
    }
    return ;
}

void    calculate_newton(t_fractal *fractal)
{
    t_float2        z;
    int             iterations;
    int             i;
    t_float2        difference;
    static t_float2 roots[3];

    fractal->name = "newton";
    init_roots(&roots);
    iterations = -1;
    z.x = (fractal->x / fractal->zoom) + fractal->offset_x;
    z.y = (fractal->y / fractal->zoom) + fractal->offset_y;
    while (++iterations < fractal->max_iterations)
    {
        z = next(z);
        i = -1;
        while (++i < 3)
        {
            difference = find_diff(z, roots[i]);
            if (fabs(difference.x) < fractal->tolerance
                && fabs(difference.y) < fractal->tolerance)
                return (put_color_to_pixel(fractal, fractal->x, fractal->y,
                        fractal->color * iterations / 2));
        }
    }
    put_color_to_pixel(fractal, fractal->x, fractal->y, 0x000000);
}

Было бы лучше вручную выполнять математические операции, а не использовать библиотеку <math.h>.

Вероятно, вы можете использовать стандартные комплексные числа вместо этой структуры t_float2. И поскольку вы по какой-то причине используете float вместо double, powf() и т. д. Или, что еще лучше, x*x вместо pow(x, 2).

Shawn 07.07.2023 03:05

Однако, возможно, он лучше подходит для сайта обмена стеками обзора кода. Но дважды проверьте их правила, прежде чем публиковать.

Shawn 07.07.2023 03:06

Обратите внимание, что если у вас есть работающий код, Stackoverflow — не то место, где нужно спрашивать , но может быть codereview.

Mike 'Pomax' Kamermans 07.07.2023 05:27

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

Simon Goater 07.07.2023 14:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
75
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

да, использование pow для небольших целочисленных показателей - плохая идея (поскольку это относительно сложная операция, включающая log, exp, *, /), вычисление ее просто с помощью * намного лучше. Вдобавок к этому вы используете несколько мощностей одной и той же переменной и вычисляете их снова, вы можете повторно использовать более низкие мощности, например, изменить:

x3 = pow(x,3);
x5 = pow(x,5);

в:

x3 = x*x*x;
x5 = x3*x*x;

это уменьшит количество операций.

Кроме того, вы вычисляете все снова и снова для каждого пикселя...

Я предполагаю, что ваш код выглядит так:

t_fractal fractal;
for (fractal.y=0;fractal.y<ys;fractal.y++)
 for (fractal.x=0;fractal.x<xs;fractal.x++)
  calculate_newton(&fractal);

в то время как внутри calculate_newton(t_fractal *fractal) вы пересчитываете все вещи, связанные с x,y позицией, снова и снова... если у вас есть y цикл внешней, вы можете вычислять все вещи только один раз для каждого y изменения вместо того, чтобы делать это xs раз больше ... для этого вы должны переместить эти переменные в фрактальный контекст/структуру

в некоторых случаях вы можете избежать использования * для увеличения значений (однако зависит от используемой вычислительной архитектуры, если это имеет какое-либо значение с точки зрения производительности) см.

для получения подробной информации о том, как это сделать.

Кроме того, если вы постоянно масштабируете/панорамируете, вы можете повторно использовать уже вычисленные значения из предыдущего кадра, как я сделал в здесь для Мандельброта

Однако без параллелизма это все, что вы можете сделать...

При параллелизме, если у вас нет многоядерного процессора/компьютера, скорость не увеличится, гораздо лучше использовать для этого GPU:

Где вы визуализируете один QUAD/прямоугольник, покрывающий ваше представление, и вычисляете фрактал внутри фрагментного шейдера (который для GLSL более или менее похож на язык C, поэтому он не сильно отличается от того, что у вас уже есть).

Ответ принят как подходящий

Вы должны начать с вычисления степеней с умножением, а не с помощью функции pow:

static t_float2 next(t_float2 z)
{
    t_float2    new;
    float       temp_deno;
    float       x2 = z.x * z.x;
    float       y2 = z.y * z.y;
    float       x4 = zx2 * zx2;
    float       y4 = zy2 * zy2;

    temp_deno = 3 * (x2 + y2) * (x2 + y2);
    new.x = (z.x * (x4 + 2 * x2 * y2 + y4) + y2 - x2) / temp_deno;
    new.y = (z.y * (x4 + 2 * x2 * y2 + y4) + 2 * z.x * z.y) / temp_deno;
    z.x -= new.x;
    z.y -= new.y;
    return z;
}

Затем обратите внимание, что вы можете упростить алгебру, разложив на множители (x2 + y2) * (x2 + y2), что также x4 + 2 * x2 * y2 + y4:

static t_float2 next(t_float2 z)
{
    float x2 = z.x * z.x;
    float y2 = z.y * z.y;
    float temp_deno = 3 * (x2 + y2) * (x2 + y2);
    return (t_float2){
        z.x * 2 / 3 - (y2 - x2) / temp_deno,
        z.y * 2 / 3 - (2 * z.x * z.y) / temp_deno
    };
}

roots должен быть предварительно вычислен во время компиляции. В C вы должны просто использовать константы:

    #define COS_PI_6  0.866025403784439  // sqrt(3) / 2
    static t_float2 roots[3] = {{ 1, 0 }, { -0.5, COS_PI_6 }, { -0.5, -COS_PI_6 }};

Мое предложение:

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

Это приближение, но вы в любом случае предполагаете один цвет на пиксель.

Другие вопросы по теме