.net - ¿Está utilizando extensiones paralelas?
parallel-processing task (5)
Espero que esto no sea un mal uso de stackoverflow; Recientemente, he visto algunas preguntas geniales aquí en Extensiones Paralelas, y esto despertó mi interés.
Mi pregunta: ¿Está utilizando extensiones paralelas y, de ser así, cómo?
Me llamo Stephen Toub y formo parte del equipo de Parallel Computing Platform en Microsoft. Somos el grupo responsable de las extensiones paralelas. Siempre me interesa saber cómo los desarrolladores están utilizando las Extensiones paralelas (por ejemplo, Parallel.For, PLINQ, ConcurrentDictionary, etc.), las experiencias positivas que ha tenido, las experiencias negativas que ha tenido, las solicitudes de características para el futuro, etc. en.
Si está dispuesto a compartir dicha información, hágalo, ya sea aquí como respuesta a esta pregunta, o envíeme un mensaje de correo electrónico en stoub at microsoft dot com
.
Tengo muchas ganas de saber de usted.
¡Gracias por adelantado!
Estoy usando el TPL para hacer llamadas anidadas Parallel.ForEach
. Como accedo a los diccionarios desde estas llamadas, tengo que usar ConcurrentDictionary
. Aunque es bueno, tengo algunos problemas:
Los delegados dentro de
ForEach
no hacen mucho trabajo, así que no obtengo mucho paralelismo. El sistema parece pasar la mayor parte de su tiempo uniendo hilos. Sería bueno si hubiera una manera de averiguar por qué no está obteniendo una mejor concurrencia y mejorarla.Las iteraciones internas de
ForEach
están sobre instancias deConcurrentDictionary
, lo que causaría que el sistema dedique gran parte de su tiempo a los enumeradores del diccionario si no agrego un caché de enumerador.Muchas de mis instancias de
ConcurrentDictionary
son en realidad conjuntos, pero no hay unConcurrentSet
así que tuve que implementar la mía con unConcurrentDictionary
.ConcurrentDictionary
no admite la sintaxis de inicialización de objetos, por lo que no puedo decirvar dict = new ConcurrentDictionary<char, int> { { ''A'', 65 } };
lo que también significa que no puedo asignar literales deConcurrentDictionary
a los miembros de la clase.Hay algunos lugares donde tengo que buscar una clave en un
ConcurrentDictionary
y llamar a una función costosa para crear un valor si no existe. Sería bueno si hubiera una sobrecarga deGetOrAdd
que tomeaddValueFactory
para que el valor solo pueda calcularse si la clave no existe. Esto se puede simular con.AddOrUpdate(key, addValueFactory, (k, v) => v)
pero eso agrega la sobrecarga de una llamada de delegado adicional a cada búsqueda.
Estoy usando un ConcurrentDictionary que almacena más de 100 millones de artículos. Mi aplicación usa alrededor de 8 GB de memoria en ese momento. El ConcurrentDictionary luego decide que quiere crecer en otro Add. Y quiere crecer mucho, aparentemente (un algoritmo prima interno) ya que se queda sin memoria. Esto es en x64 con 32GB de memoria.
Por lo tanto, me gustaría que un booleano bloquee el reabastecimiento / reabastecimiento automático de un diccionario (concurrente). Luego inicializaría el diccionario en la creación con un conjunto fijo de cubos (¡esto no es lo mismo que una capacidad fija!). Y se volvería un poco más lento con el tiempo, ya que hay más y más elementos en un cubo. Pero esto evitaría volver a quemar y perder la memoria demasiado rápido e innecesariamente.
Lo he estado usando en mi proyecto MetaSharp . Tengo un canal de compilación basado en MSBuild para DSL y una etapa de tipos es de muchos a muchos. La etapa M: M usa .AsParallel.ForAll (...).
Aquí está el snippet :
protected sealed override IEnumerable<IContext> Process()
{
if (this.Input.Count() > 1)
{
this.Input
.AsParallel<IContext>()
.ForAll(this.Process);
}
else if (this.Input.Any())
{
this.Process(this.Input.Single());
}
return this.Input.ToArray();
}
No lo usamos extensivamente , pero ciertamente ha sido útil.
Pude reducir el tiempo de ejecución de algunas de nuestras pruebas de unidad de ejecución más larga a aproximadamente 1/3 de su tiempo original simplemente envolviendo algunos de los pasos que requieren más tiempo en una llamada Parallel.Invoke()
.
También me encanta usar las bibliotecas paralelas para probar la seguridad de subprocesos. He detectado y reportado un par de problemas de subprocesos con Ninject con código algo como esto:
var repositoryTypes = from a in CoreAssemblies
from t in a.GetTypes()
where t.Name.EndsWith("Repository")
select t;
repositoryTypes.ToList().AsParallel().ForAll(
repositoryType => _kernel.Get(repositoryType));
En nuestro código de producción real, usamos algunas extensiones paralelas para ejecutar algunas acciones de integración que se supone que se ejecutan cada pocos minutos, y que consisten principalmente en extraer datos de los servicios web. Esto toma una ventaja especial del paralelismo debido a la alta latencia inherente a las conexiones web, y permite que todos nuestros trabajos terminen de ejecutarse antes de que se desencadenen nuevamente.
Todavía no lo he usado extensivamente, pero definitivamente he prestado atención a sus usos y busco oportunidades en nuestra base de código para ponerlo en uso (desafortunadamente, estamos vinculados a .NET-2.0 en muchos de nuestros proyectos aún). Siendo por el momento). Una pequeña joya que se me ocurrió fue un contador de palabras único. Creo que esta es la implementación más rápida y concisa que se me ocurre. Si alguien puede mejorarla, sería genial.
private static readonly char[] delimiters = { '' '', ''.'', '','', '';'', ''/''', ''-'', '':'', ''!'', ''?'', ''('', '')'', ''<'', ''>'', ''='', ''*'', ''/'', ''['', '']'', ''{'', ''}'', ''//', ''"'', ''/r'', ''/n'' };
private static readonly Func<string, string> theWord = Word;
private static readonly Func<IGrouping<string, string>, KeyValuePair<string, int>> theNewWordCount = NewWordCount;
private static readonly Func<KeyValuePair<string, int>, int> theCount = Count;
private static void Main(string[] args)
{
foreach (var wordCount in File.ReadAllText(args.Length > 0 ? args[0] : @"C:/DEV/CountUniqueWords/CountUniqueWords/Program.cs")
.Split(delimiters, StringSplitOptions.RemoveEmptyEntries)
.AsParallel()
.GroupBy(theWord, StringComparer.OrdinalIgnoreCase)
.Select(theNewWordCount)
.OrderByDescending(theCount))
{
Console.WriteLine(
"Word: /""
+ wordCount.Key
+ "/" Count: "
+ wordCount.Value);
}
Console.ReadLine();
}
private static string Word(string word)
{
return word;
}
private static KeyValuePair<string, int> NewWordCount(IGrouping<string, string> wordCount)
{
return new KeyValuePair<string, int>(
wordCount.Key,
wordCount.Count());
}
private static int Count(KeyValuePair<string, int> wordCount)
{
return wordCount.Value;
}