简体   繁体   中英

C# Is there anything faster than PLINQ for averaging 10million item array while grouing

I'm basically trying to find a way to come with the the fastest method that's available to produce an average of 10 million collection while grouping. Below is the code i'm using as a baseline but I cannot seem to find a way to make this any faster. I'm evaluting this based on a StopWatrch mainSW

Here is my Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpeedTest
{
    class Program
    {
        static void Main(string[] args)
        {
            global.BuildItems();
            System.Diagnostics.Stopwatch mainSW = new System.Diagnostics.Stopwatch();
            mainSW.Start();
            baseLineTest.plinqGroup();
            Console.WriteLine("Press Return");
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Main SW Elapsed:" + mainSW.Elapsed);
            Console.ReadLine();
        }
    }
}

Here is my global.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpeedTest
{
    class global
    {
        //10 million
        public static long globalIteration = 10000000;

        private static string[] classOptions = { "AS", "CS", "LS", "PE", "WP", "LS" };
        public static Items[] items { get; set; }

        public static void BuildItems()
        {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

            Random r = new Random();
            Random r2 = new Random();

            Console.WriteLine("Building list");
            items = new Items[globalIteration];
            sw.Start();
            for (int i = 0; i < globalIteration; i++)
            {
                items[i] = new Items();
                items[i].cl = classOptions[r.Next(0, 5)];
                items[i].uc = Convert.ToDecimal(r.Next(300, 1000));
            }
            Console.WriteLine("Building list sw: " + sw.Elapsed);
        }

    }
    class Items
    {
        public decimal uc;
        public string cl;

    }
}

Here is my baseLineTest.cs

using System;
using System.Linq;
using System.Threading.Tasks;

namespace SpeedTest
{
    class baseLineTest
    {

        public static void plinqGroup()
        {

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            Console.WriteLine("This test is a plinq over an array");


            decimal avg = 0;
            decimal sum = 0;
            string clas = "";
            sw.Start();
            var list = global.items.AsParallel().GroupBy(d => d.cl)
    .Select(
        g => new
        {
            Key = g.Key,
            Value = g.Average(s => s.uc)
        });

            foreach (var item in list)
                Console.WriteLine(string.Format("{0} : {1} ", item.Key, item.Value));

            Console.WriteLine("PLinq Group elapsed : " + sw.Elapsed);


        }

    }


}

This depends on how much you know about your data. If the "classOptions" are known to your code that consumes your data, then it is possible to get it around twice as fast (at least on my 4 core machine) by first creating an Array of result objects and then use Parallel.ForEach to manipulate the Array

    [TestMethod]
    public void MeasureParallelForEach()
    {
        string[] classOptions = { "AS", "CS", "LS", "PE", "WP", "LS" };
        global.BuildItems();
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        Console.WriteLine("This test is a plinq over an array");

        sw.Start();
        IDictionary<string, Group> groups = global.classOptions.Distinct().ToDictionary(x => x, x => new Group(x));
        Parallel.ForEach(global.items, d => groups[d.cl].Add(d.uc));
        foreach (var item in groups)
            Console.WriteLine(string.Format("{0} : {1} ", item.Key, item.Value.Average));
        Console.WriteLine("Parallel.ForEach elapsed : " + sw.Elapsed);
    }

    public class Group
    {
        private int count;
        private decimal sum;
        public Group(string key)
        {
            Key = key;
        }

        public void Add(decimal d)
        {
            sum += d;
            count++;
        }

        public string Key { get; private set; }
        public decimal Average { get { return count==0?0m:sum/count; }}
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM