I am using someone else's .NET 4 open source smoothed particle hydrodynamics code and I'm trying to convert it for a Unity project which you can only count on to be up to .NET 2 standards. Unfortunately the code uses the Parallels class (which is awesome!) but he used one of the more obscure overloads. Can anyone see a good way to achieve the same thing in .NET 2 without a huge performance hit?
Parallel.For(
0,
_numActiveParticles,
() => new Vector2[MAX_PARTICLES],
(i, state, accumulatedDelta) => calculateForce(_activeParticles[i], accumulatedDelta),
(accumulatedDelta) =>
{
lock (_calculateForcesLock)
{
for (int i = _numActiveParticles - 1; i >= 0; i--)
{
int index = _activeParticles[i];
_delta[index] += accumulatedDelta[index] / MULTIPLIER;
}
}
}
);
I think this is what the code does (non threaded):
for (int i = 0; i < _numActiveParticles; i++)
{
Vector2[] temp = new Vector2[MAX_PARTICLES];
temp = calculateForce(_activeParticles[i], temp);
for (int k = _numActiveParticles - 1; k >= 0; k--)
{
int index = _activeParticles[k];
_delta[index] += temp[index] / MULTIPLIER;
}
}
Your second code isn't correct. I think the correct code is like this:
var accumulatedDelta= new Vector2[MAX_PARTICLES];
for(int i = 0; i < _numActiveParticles; ++i)
{
accumulatedDelta = calculateForce(_activeParticles[i], accumulatedDelta);
}
for (int i = _numActiveParticles - 1; i >= 0; i--)
{
int index = _activeParticles[i];
_delta[index] += accumulatedDelta[index] / MULTIPLIER;
}
I don't know what .net2
has and what doesn't. But you can simulate Parallel.For
yourself.
explanation of this overload of Parallel.For
is this:
First parameter: start index of loop
Second parameter: end index of loop
Third parameter: a delegate that will create task local data. for each every thread that Parallel.For
use (a task) this delegate will be called and return localInit
data.
Fourth parameter: a delegate that act as body of for
. In first execution of body delegate, this delegate will retrieve data that is created by previuse delegate (localInit
). in every subsequent loop, body delegate can change localInit
and then return that to next body execution. In last execution of body delegate, localInit
data will be passed to last delegate.
Last parameter: another delegate that will be called per each task, when task has finished it's work. localInit
will be passed to this delegate. Because this delegate can be called concurrency by multiple tasks, you must protect your shared data.
Edit:
A version of ParallelFor
can be like this:
public static void ParallerFor<TLocal>(int startIndex, int endIndex, Func<TLocal> initData, Func<int, TLocal, TLocal> body, Action<TLocal> finalizer)
{
int numThreads = Environment.ProcessorCount;
int chunkOffset = ((endIndex - startIndex) / numThreads) + 1;
Task[] tasks = new Task[numThreads];
Enumerable.Range(0, numThreads).ToList().ForEach(x =>
{
int start = x * chunkOffset;
int end = ((x + 1) * chunkOffset);
end = end > endIndex ? endIndex : end;
tasks[x] = Task.Factory.StartNew(() =>
{
TLocal init = initData();
for(int i = start; i < end; ++i)
{
init = body(i, init);
}
finalizer(init);
});
});
Task.WhenAll(tasks).Wait();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With