Я реализовал фрактал Ньютона на 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>.
Однако, возможно, он лучше подходит для сайта обмена стеками обзора кода. Но дважды проверьте их правила, прежде чем публиковать.
Обратите внимание, что если у вас есть работающий код, Stackoverflow — не то место, где нужно спрашивать , но может быть codereview.
Существует также возможность улучшить производительность с помощью SIMD-векторизации, но похоже, что существует довольно много улучшений производительности, которые, как объяснили другие, можно было бы сначала внести в ваш код, прежде чем прибегать к такой дополнительной сложности.





да, использование 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 }};
Мое предложение:
Ключевым фактором в общей стоимости является количество итераций, выполненных с каждой начальной точки. Как насчет того, чтобы запомнить их? Я имею в виду, что для каждого обработанного пикселя сохраняйте количество итераций. Но при итерации, когда вы приземляетесь на уже обработанный пиксель, предполагайте, что количество оставшихся итераций одинаково.
Это приближение, но вы в любом случае предполагаете один цвет на пиксель.
Вероятно, вы можете использовать стандартные комплексные числа вместо этой структуры
t_float2. И поскольку вы по какой-то причине используете float вместо double,powf()и т. д. Или, что еще лучше,x*xвместоpow(x, 2).