Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallel.For in C#

I am trying convert the following Collatz Conjecture algorithm from:

 public class CollatzConjexture
    {
        public static int Calculate(int StartIndex, int MaxSequence)
        {
            int ChainLength = 0;
            int key = 0;
            bool ContinuteCalulating = true;
            int LongestChain = 0;
            Int64 Remainder = 0;

            for (int i = StartIndex; i <= MaxSequence; i++)
            {
                ChainLength = 1;
                Remainder = i;
                ContinuteCalulating = true;

                while (ContinuteCalulating)
                {
                    Remainder = CalculateCollatzConjecture(Remainder);
                    if (Remainder == 1)
                        ContinuteCalulating = false;

                    ChainLength += 1;
                }

                if (ChainLength > LongestChain)
                {
                    LongestChain = ChainLength;
                    key = i;
                }
            }

            return key;
        }

        private static Int64 CalculateCollatzConjecture(Int64 Number)
        {
            if (Number % 2 == 0)
                return Number / 2;
            else
                return (3 * Number) + 1;
        }
    } 

To instead use the .NET 4.0 Parallel.For :

int ChainLength = 0;
            int key = 0;
            bool ContinuteCalulating = true;
            int LongestChain = 0;
            Int64 Remainder = 0;

            int[] nums = Enumerable.Range(1, 1500000).ToArray();
            long total = 0;

            // Use type parameter to make subtotal a long, not an int
            Parallel.For<int>(1, nums.Length, () => 1, (j, loop, subtotal) =>
            {
                Remainder = j;

                while (ContinuteCalulating)
                {
                    Remainder = CalculateCollatzConjecture(Remainder);
                    if (Remainder == 1)
                        ContinuteCalulating = false;

                    ChainLength += 1;
                }

                if (ChainLength > LongestChain)
                {
                    LongestChain = ChainLength;
                    key = j;
                }
                return key;


            },
                (x) => Interlocked.Add(ref key, x)
            );

I have a feeling I'm not too far from it, famous last words.

Thanks in advance.

like image 679
Seany84 Avatar asked Dec 21 '10 16:12

Seany84


1 Answers

Your problem is that you don't want to use Parallel.For in this instance because you already have an array (nums) to iterate over, which calls for Parallel.ForEach. However, your array is created with Enumerable.Range and you don't actually use it for anything, so the best way to do it is with AsParallel and LINQ (PLINQ):

public static class CollatzConjexture2
{
    public static int Calculate(int StartIndex, int MaxSequence)
    {
        var nums = Enumerable.Range(StartIndex, MaxSequence);
        return nums.AsParallel()
                    // compute length of chain for each number
                   .Select(n => new { key = n, len = CollatzChainLength(n) })
                    // find longest chain
                   .Aggregate((max, cur) => cur.len > max.len ||
                    // make sure we have lowest key for longest chain
                      max.len == cur.len && cur.key < max.key ? cur : max)
                    // get number that produced longest chain
                   .key;
    }

    private static int CollatzChainLength(Int64 Number)
    {
        int chainLength;
        for (chainLength = 1; Number != 1; chainLength++)
            Number = (Number & 1) == 0 ? Number >> 1 : Number * 3 + 1;
        return chainLength;
    }
}

This method is about twice as fast on my computer as the serial implementation. Also note that I optimized the main loop so that it does the computation inline rather than calling a function and it uses bitwise math instead of multiplying and dividing. This made it about twice as fast again. Compiling it for x64 instead of x86 also made it more than twice as fast.

like image 90
Gabe Avatar answered Oct 18 '22 22:10

Gabe