Beispiel #1
0
        /// <summary>Executes a loop in chunks, using up to the given number of threads.</summary>
        /// <param name="maxParallelism">The maximum number of threads to use to execute the loop.</param>
        /// <include file="documentation.xml" path="/Utilities/Tasks/ParallelForChunked/*"/>
        public static void ParallelFor(int start, int endExclusive, Action <int, int, LoopThreadInfo> body, int maxParallelism)
        {
            if (start > endExclusive || maxParallelism <= 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (body == null)
            {
                throw new ArgumentNullException();
            }

            uint iterations = (uint)(endExclusive - start), parallelism = Math.Min((uint)maxParallelism, iterations);

            if (iterations == 0)
            {
                return;
            }

            if (parallelism == 1)
            {
                body(start, endExclusive, new LoopThreadInfo(0));
            }
            else
            {
                WorkItemTask[] tasks = new WorkItemTask[parallelism];
                for (uint basicChunkSize = iterations / parallelism, errorInc = iterations % parallelism, error = 0, i = 0; i < (uint)tasks.Length; i++)
                {
                    int  threadNumber = (int)i, chunkStart = start; // make a copy of these so they can be properly captured by the closure
                    uint chunkSize = basicChunkSize;

                    // say we're dividing 100 iterations over 6 threads. then each chunk should be 16.666... iterations in length. since we
                    // can't have a fractional iteration, we'll truncate the chunk size (to 16) and keep track of an error value. the error
                    // will be incremented by the fractional part of the ideal chunk size (0.666...). if the error is greater than or equal
                    // to 0.5, then we'll add one to the chunk size and subtract 1 from the error. since we don't want to use floating point
                    // math, due to its inaccuracy, we'll do the same thing with integers. the error increment will be the integer remainder
                    // from the calculation of the chunk size (so 100/6 leaves a remainder of 4). and instead of comparing against 0.5, we'll
                    // compare double the error against the divisor (parallelism). this is the same as comparing the error against half the
                    // divisor, except that it avoids truncation error. unfortunately, i can't really prove that the algorithm works for all
                    // combinations of iterations and thread counts, but it seems to, and i'm showing my trust in it by not adding a special
                    // case for the last chunk that simply takes up the remaining iterations.
                    error += errorInc;
                    if (error * 2 >= parallelism)
                    {
                        chunkSize++;
                        error -= parallelism;
                    }

                    tasks[i] = new WorkItemTask(task =>
                    {
                        LoopThreadInfo info = new LoopThreadInfo(threadNumber);
                        body(chunkStart, chunkStart + (int)chunkSize, info);
                    });

                    start += (int)chunkSize;
                }

                new CompositeTask(tasks).Run();
            }
        }
Beispiel #2
0
        /// <summary>Creates and returns a task that represents the running of a number of instances of a work item simultaneously.
        /// The method accepts an optional cancellation event that can be associated with all instances of the work item, as well as
        /// the aggregate task that encapsulates them.
        /// </summary>
        /// <include file="documentation.xml" path="/Utilities/Tasks/ParallelismRemarks/*"/>
        public static CompositeTask CreateParallel <T>(TaskDelegate <T> work, int instanceCount, TaskCancellationEvent cancellationEvent)
        {
            if (instanceCount <= 0)
            {
                throw new ArgumentOutOfRangeException();
            }

            Task[] tasks = new Task[instanceCount];
            for (int i = 0; i < tasks.Length; i++)
            {
                tasks[i] = new WorkItemTask <T>(work, cancellationEvent);
            }
            return(new CompositeTask(tasks, cancellationEvent));
        }
Beispiel #3
0
        /// <summary>Executes a loop using up to the given number of threads.</summary>
        /// <param name="maxParallelism">The maximum number of threads to use to execute the loop.</param>
        /// <include file="documentation.xml" path="/Utilities/Tasks/ParallelFor/*[not(@name='body')]"/>
        /// <include file="documentation.xml" path="/Utilities/Tasks/ParallelForWithInit/*"/>
        public static void ParallelFor <T>(int start, int endExclusive, Func <LoopThreadInfo, T> threadInitializer,
                                           Action <int, T, LoopThreadInfo> body, int maxParallelism)
        {
            if (start > endExclusive || maxParallelism <= 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (threadInitializer == null || body == null)
            {
                throw new ArgumentNullException();
            }

            int iterations = endExclusive - start;

            if (iterations == 0)
            {
                return;
            }

            if (maxParallelism > iterations)
            {
                maxParallelism = iterations;
            }
            if (maxParallelism == 1)
            {
                LoopThreadInfo info = new LoopThreadInfo(0);
                T value             = threadInitializer(info);
                for (; start < endExclusive; start++)
                {
                    body(start, value, info);
                }
            }
            else
            {
                WorkItemTask[] tasks = new WorkItemTask[maxParallelism];
                for (int i = 0; i < tasks.Length; i++)
                {
                    int threadNumber = i;
                    tasks[i] = new WorkItemTask(task =>
                    {
                        LoopThreadInfo info = new LoopThreadInfo(threadNumber);
                        T value             = threadInitializer(info);

                        // get an available index
                        int currentIndex;
                        while (true)
                        {
                            do
                            {
                                currentIndex = start;
                                if (currentIndex == endExclusive)
                                {
                                    goto done;
                                }
                            } while(Interlocked.CompareExchange(ref start, currentIndex + 1, currentIndex) != currentIndex);

                            body(currentIndex, value, info);
                        }

                        done:;
                    });
                }

                new CompositeTask(tasks).Run();
            }
        }