threads thread programming parallel net c# optimization parallel-processing sequential

thread - c#Paralelo vs Secuencial



parallel.foreach c# (1)

Tengo una gran lista para recorrer (1.500.000 elementos), con cada elemento tengo que hacer un pequeño cheque. Totalmente durante 30 segundos.

La utilización de la CPU al usar Sequential es de alrededor del 10%, por lo que no se utilizan muchos recursos.

Lo primero que se pensó fue usar Parallel, pero debido a la duración limitada de cada elemento, Parallel dura más tiempo que un Foreach secuencial, esto se debe a " ¿Por qué la versión paralela fue más lenta que la versión secuencial en este ejemplo? ", Lo que explica que la creación de cada tarea costará tiempo.

Así que tuve otro pensamiento y es dividir la lista en 4 (o más) paces iguales y crear un hilo para recorrer los elementos para que sea más rápido.

Antes de crear mi propia clase, ¿es este un buen enfoque? ¿O algún otro pensamiento sobre cómo acelerar las cosas? ¿O conoces una forma mejor de manejar esto?

Código

El código que creé para otro enfoque paralelo: (usado en mi propia clase estática)

public static void ForEach<T>(IEnumerable<T> list, Action<T> body, int listDevide) { // Number of items int items = list.Count(); // Divided (in int, so floored) int listPart = items / listDevide; // Get numbers extra for last run int rest = items % listDevide; // List to save the actions var actions = new List<Action>(); for(var x = 0; x < listDevide; x++) { // Create the actions actions.Add(delegate { foreach(var item in list.Skip(x * listPart).Take(listPart)) { body.Invoke(item); } }); } // Run the actions parallel Parallel.Invoke(actions.ToArray()); }

Observación: la variable "reposo" para hacer los últimos elementos no se usa actualmente en este ejemplo.

Solución a continuación, más información: http://msdn.microsoft.com/en-us/library/dd997411.aspx


Sí, particionar la matriz de entrada es un buen enfoque.

De hecho, Microsoft proporciona una clase Partitioner para ayudar con exactamente este enfoque.

Aquí hay un ejemplo que muestra cómo hacerlo:

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; namespace Demo { class Program { private void run() { double sum = 0; Func<double, double> func = x => Math.Sqrt(Math.Sin(x)); object locker = new object(); double[] data = testData(); // For each double in data[] we are going to calculate Math.Sqrt(Math.Sin(x)) and // add all the results together. // // To do this, we use class Partitioner to split the input array into just a few partitions, // (the Partitioner will use knowledge about the number of processor cores to optimize this) // and then add up all the values using a separate thread for each partition. // // We use threadLocalState to compute the total for each partition, and then we have to // add all these together to get the final sum. We must lock the additon because it isn''t // threadsafe, and several threads could be doing it at the same time. Parallel.ForEach ( Partitioner.Create(0, data.Length), () => 0.0, (subRange, loopState, threadLocalState) => { for (int i = subRange.Item1; i < subRange.Item2; i++) { threadLocalState += func(data[i]); } return threadLocalState; }, finalThreadLocalState => { lock (locker) { sum += finalThreadLocalState; } } ); Console.WriteLine("Sum = " + sum); } private static double[] testData() { double[] array = new double[1000003]; // Test with an odd number of values. Random rng = new Random(12345); for (int i = 0; i < array.Length; ++i) array[i] = rng.Next() & 3; // Don''t want large values for this simple test. return array; } static void Main() { new Program().run(); } } }