I'm currently trying to optimize this class I have for fractal generation. The equation is meant to be pluggable; I have used z => z*z + c
so that it's not using the Complex.Pow
method (eliminating a method call per iteration). Without changing it from the Complex
data type, how else can I optimize this method, and is there a better way I can set the colors in the bitmap? Methods shown below.
public void Calculate()
{
var maxIterations = MaxIterations;
var stopwatch = new Stopwatch();
var histogram = new int[maxIterations];
var width = Bitmap.Width;
var height = Bitmap.Height;
var iterationCount = new int[width, height];
var colors = new Color[width, height];
var epsilon = new Vector2((MaxBounds.X - MinBounds.X) / (width-1), (MaxBounds.Y - MinBounds.Y) / (height-1));
var scale = new Vector2(1 / epsilon.X, 1 / epsilon.Y);
var escapeRadiusSquared = EscapeRadius*EscapeRadius;
stopwatch.Start();
Parallel.ForEach(Multithreading.Iterate(MinBounds.X, MaxBounds.X, epsilon.X), cx =>
{
for (var cy = MinBounds.Y; cy < MaxBounds.Y; cy += epsilon.Y)
{
var iterations = 0;
var c = new Complex(cx, cy);
var z = StartingPoint(c);
while (z.Real*z.Real + z.Imaginary*z.Imaginary < escapeRadiusSquared && iterations < maxIterations)
{
z = Algorithm(z, c);
iterations++;
}
var xx = (int) Math.Round((cx - MinBounds.X)*scale.X);
var yy = height - 1 - (int) Math.Round((cy - MinBounds.Y)*scale.Y);
if (iterations >= maxIterations)
{
colors[xx, yy] = Color.Black;
}
else if (SmoothingType == SmoothingType.Continuous || SmoothingType == SmoothingType.Smooth)
{
for (var i = 0; i < 3; i++)
{
z = Algorithm(z, c);
iterations++;
}
var mu = iterations + 1 - Math.Log(Math.Log(z.Magnitude))/Math.Log(2);
if (SmoothingType == SmoothingType.Continuous)
{
mu = mu/ maxIterations * ColorPalette.Count;
}
colors[xx, yy] = ColorUtils.GetColor(mu, ColorPalette);
}
else if (SmoothingType == SmoothingType.None)
{
colors[xx, yy] = iterations == maxIterations
? Color.Black
: ColorPalette[iterations%ColorPalette.Count];
}
else if (SmoothingType == SmoothingType.Histogram)
{
iterationCount[xx, yy] = iterations;
histogram[iterations]++;
}
else
{
throw new ArgumentOutOfRangeException();
}
}
});
stopwatch.Stop();
TimeElapsed = stopwatch.Elapsed;
if (SmoothingType == SmoothingType.Histogram)
{
Parallel.ForEach(Multithreading.Iterate(MinBounds.X, MaxBounds.X, epsilon.X), cx =>
{
for (var cy = MinBounds.Y; cy < MaxBounds.Y; cy += epsilon.Y)
{
var xx = (int)Math.Round((cx - MinBounds.X) * scale.X);
var yy = height - 1 - (int)Math.Round((cy - MinBounds.Y) * scale.Y);
var total = 0;
for (var i = 0; i < MaxIterations; i++)
{
total += histogram[i];
}
var hue = 0.0;
for (var i = 0; i < iterationCount[xx, yy]; i += 1)
{
hue += ((double)histogram[i])/total;
}
hue = Math.Round(hue*(ColorPalette.Count - 1));
colors[xx, yy] = ColorPalette[(int)hue];
}
});
}
FastBitmap.SetPixels(colors);
}
And the FastBitmap
class.
public unsafe void SetPixels(Color[,] colors)
{
var data = Bitmap.LockBits(new Rectangle(0, 0, Bitmap.Width, Bitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
var scan0 = data.Scan0;
var width = colors.GetLength(0);
var height = colors.GetLength(1);
Parallel.For(0, width, x =>
{
for (var y = 0; y < height; y++)
{
var color = colors[x, y];
var imagePointer = (byte*) scan0.ToPointer(); // Pointer to first pixel of image
var offset = y*data.Stride + 3*x; // 3x because we have 24bits/px = 3bytes/px
var px = imagePointer + offset; // pointer to the pixel we want
px[0] = color.B; // Red component
px[1] = color.G; // Green component
px[2] = color.R; // Blue component
}
});
Bitmap.UnlockBits(data); // Set the data again
}
Multithreading.Iterate
:
public static class Multithreading
{
public static IEnumerable<double> Iterate(
double fromInclusive, double toExclusive, double step)
{
for (var d = fromInclusive; d < toExclusive; d += step) yield return d;
}
}
1 Answer 1
Some ideas :
use a
switch
case around theif (smoothingtype
statement(s), as you will gain clarity (as will the poor reader that I am too by the way)the following line is a constant inside the for loop, put it outside:
var xx = (int) Math.Round((cx - MinBounds.X)*scale.X);
have you use a profiler? even a basic one can give you useful hints on where this code spends its CPU time.
there are many calls to
Algorithm(z, c);
but I don't see the method in your code
I hope this helps !
Explore related questions
See similar questions with these tags.