Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting .NET 4 Threading to .NET 2

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;
        }
    }
like image 506
PorcupineRending Avatar asked Nov 10 '22 16:11

PorcupineRending


1 Answers

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();
    }
like image 182
MRB Avatar answered Nov 14 '22 22:11

MRB