How can a progress bar be added to the third loop shown below?
Experimental evidence has shown that of the following three loops the third is fastest. The best performance is when the thread count is the same as the logical processors of the CPU. I think this is due to reduced time spent allocating and releasing thread resources.
This is code to generate a noise map. Sometimes the processing time is long enough that a progress bar is needed.
for (int j = 0; j < data.Length; j++)
{
var x = (location.X + (j % width));
var y = (location.Y + (j / width));
Vector3 p = new Vector3(x, y, frame);
p *= zoom;
float val = noise.GetNoise(p);
data[j] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
}
Parallel.For(0, data.Length, (i) => {
var x = (location.X + (i % width));
var y = (location.Y + (i / width));
Vector3 p = new Vector3(x, y, frame);
p *= zoom;
float val = noise.GetNoise(p);
data[i] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
});
Parallel.For(0, threads, (i) =>
{
int from = i * data.Length / threads;
int to = from + data.Length / threads;
if (i == threads - 1) to = data.Length - 1;
for (int j = from; j < to; j++)
{
var x = (location.X + (j % width));
var y = (location.Y + (j / width));
Vector3 p = new Vector3(x, y, frame);
p *= zoom;
float val = noise.GetNoise(p);
data[j] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
}
}
);
A progress bar that has a limited update rate to a few times a second so as to not waist time drawing a progress bar too often would be the best.
Adding IProgress I have arrived here and this almost works. The problem is that the progress bar updates after the parallel.for has completely finished.
private async Task<int> FillDataParallelAsync(int threads, IProgress<int> progress)
{
int precent = 0;
/// parallel loop - easy and fast.
Parallel.For(0, threads, (i) =>
{
int from = i * data.Length / threads;
int to = from + data.Length / threads;
if (i == threads - 1) to = data.Length - 1;
for (int j = from; j < to; j++)
{
var x = (location.X + (j % width));
var y = (location.Y + (j / width));
Vector3 p = new Vector3(x, y, frame);
p *= zoom;
float val = noise.GetNoise(p);
data[j] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
if(j%(data.Length / 100) ==0)
{
if (progress != null)
{
progress.Report(precent);
}
Interlocked.Increment(ref precent);
}
}
}
);
return 0;
}
After working on this for too long it looks like this now.
private Boolean FillDataParallel3D(int threads, CancellationToken token, IProgress<int> progress)
{
int precent = 0;
Vector3 imageCenter = location;
imageCenter.X -= width / 2;
imageCenter.Y -= height / 2;
ParallelOptions options = new ParallelOptions { CancellationToken = token };
/// parallel loop - easy and fast.
try
{
ParallelLoopResult result =
Parallel.For(0, threads, options, (i, loopState) =>
{
int from = i * data.Length / threads;
int to = from + data.Length / threads;
if (i == threads - 1) to = data.Length - 1;
for (int j = from; j < to; j++)
{
if (loopState.ShouldExitCurrentIteration) break;
Vector3 p = imageCenter;
p.X += (j % width);
p.Y += (j / width);
p *= zoom;
float val = noise.GetNoise(p);
data[j] += val;
min = Math.Min(min, val);
max = Math.Max(max, val);
if (j % (data.Length / 100) == 0)
{
try { if (progress != null) progress.Report(precent); }
catch { }
Interlocked.Increment(ref precent);
}
}
}
);
return result.IsCompleted;
}
catch { }
return false;
}
The progress is incremented in part from each thread up to a total of 100 times. It still has some latency in updating the progress bar but it seems to be unavoidable. For example if the progress bar increments 100 times in less than the time to draw 100 updates the progress seems to que up and continues to progress after the method returns. Suppressing the display of progress for a second after calling the method works well enough. The progress bar is only really useful when the method takes so long that you wonder if anything is happening.
Full project at https://github.com/David-Marsh/Designer