2010-10-18 18 views
16

Espero que esto no sea un mal uso de stackoverflow; Recientemente, he visto algunas grandes preguntas aquí en Parallel Extensions, y despertó mi interés.¿Está utilizando extensiones paralelas?

Mi pregunta es: ¿Está utilizando extensiones paralelas y, en caso afirmativo, cómo?

Mi nombre es Stephen Toub y estoy en el equipo de Parallel Computing Platform en Microsoft. Somos el grupo responsable de las Extensiones Paralelas. Siempre me interesa saber de qué manera los desarrolladores utilizan las extensiones paralelas (por ejemplo, Paralelo.para, PLINQ, ConcurrentDictionary, etc.), las experiencias positivas que has tenido, las experiencias negativas que has tenido, las solicitudes de funciones para el futuro y, por lo tanto, en.
Si está dispuesto a compartir dicha información, hágalo, ya sea aquí como respuesta a esta pregunta o para mí de manera privada a través del correo electrónico al stoub at microsoft dot com.

Tengo muchas ganas de tener noticias suyas.

¡Gracias de antemano!

+2

Dado que esta pregunta no puede tener una sola respuesta correcta, me temo que no es apropiada para StackOverflow. Como perteneces al equipo de MS, no votaré por el cierre, pero probablemente lo haga otro. Puede tener mejor suerte en los programadores.stackexchange.com y meta.stackoverflow.com. También puede marcar la pregunta como un "Wiki comunitario" para que los votos no cuenten hacia/contra el representante y es mucho menos probable que se cierren. –

+0

Ok. Gracias, Sam. Disculpas por usar mal el sitio. -Stephen –

+2

@Stephen Toub, 12 horas y cero votos para cerrar. Parece que a la gente no le importa. Sin embargo, ninguna respuesta. –

Respuesta

0

Lo he estado usando en mi proyecto MetaSharp. Tengo un canal de compilación basado en MSBuild para DSL y un tipo de etapa es una etapa de muchos a muchos. La etapa M: M usa .AsParallel.ForAll (...).

Aquí está la 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(); 
} 
+4

Recomiendo 'this.Input.Skip (1) .Any()', para que pueda dejar de contar después de tocar la segunda entrada . – StriplingWarrior

+1

Significado: replace this.Input.Count()> 1 con this.Input.Skip (1) .Any(). – dthorpe

+0

¡Buena llamada! Eso es muy útil –

4

estoy usando el TPL para hacer llamadas anidadas Parallel.ForEach. Como accedo a diccionarios a partir de estas llamadas, debo usar ConcurrentDictionary. Aunque es bueno, tengo un par de cuestiones:

  • Los delegados dentro de ForEach no hacen mucho trabajo, así que no entiendo mucho paralelismo. El sistema parece pasar la mayor parte del tiempo uniendo hilos. Sería bueno si hubiera una manera de descubrir por qué no está obteniendo una mejor concurrencia y mejorarla.

  • Las iteraciones internas ForEach son más de ConcurrentDictionary instancias, lo que provocaría que el sistema gastara la mayor parte de su tiempo en enumeradores para el diccionario si no añadiera un caché de enumerador.

  • Muchas de mis instancias ConcurrentDictionary son en realidad conjuntos, pero no hay ConcurrentSet, así que tuve que implementar la mía con un ConcurrentDictionary.

  • ConcurrentDictionary no es compatible con la sintaxis de inicialización de objetos, así que no puedo decir var dict = new ConcurrentDictionary<char, int> { { 'A', 65 } }; que también significa que no puedo asignar ConcurrentDictionary literales a 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 de GetOrAdd que toma un addValueFactory para que el valor se pueda calcular solo 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.

+0

Creo que puede estar mejor sin usar el anidamiento. No hay ninguna razón para crear más tareas que sus CPU, al menos no mucho más. Recomiendo deshacerse de la innter Parallel.ForEach. –

+0

Joe H: Los interiores realmente hacen que el proceso sea un 5-10% más rápido. – Gabe

1

no he utilizado ampliamente aún, pero definitivamente he mantenido mi oreja a sus usos y buscar oportunidades en nuestra base de código para ponerla en uso (por desgracia, estamos .NET 2.0 ligado en muchos de nuestros proyectos todavía por el momento).Una pequeña joya que se me ocurrió fue un contador de palabras único. Creo que esta es la aplicación más rápida y concisa que pueda llegar a - si alguien puede hacerlo mejor, eso sería genialidad:

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; 
} 
+1

lol, ¿te sientes incómodo con las expresiones lambda? ;) De todos modos, probé este código con el libro electrónico "El Señor de los Anillos" como entrada. Da más o menos los mismos resultados con o sin AsParallel, así que supongo que las extensiones paralelas realmente no ayudan en ese caso ...También escribí una implementación más corta (en la misma línea) que es casi el doble de rápida, por lo que su implementación probablemente no sea "la implementación más rápida y concisa que se pueda imaginar";). Aún así, es un ejercicio interesante ... –

+1

Bueno, vamos a verlo, hombre! –

+0

@Thomas - Probablemente no recibió una notificación del comentario de Jesse. Deje que este comentario sirva como esa notificación. – Greg

0

Nosotros no lo use ampliamente , pero ciertamente ha llegado en práctico.

Pude reducir el tiempo de ejecución de algunas de nuestras pruebas de unidad de funcionamiento prolongado a aproximadamente 1/3 de su tiempo original simplemente al envolver algunos de los pasos más intensivos en una llamada Parallel.Invoke().

También me encanta usar las bibliotecas paralelas para probar la seguridad de la hebra. He cogido y reportó un par de problemas de threads con Ninject con el código de 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 a ejecutar algunas acciones de integración que se supone que salen cada pocos minutos y que consisten principalmente en extraer datos de los servicios web. Esto tiene una ventaja especial en el paralelismo debido a la alta latencia inherente en las conexiones web, y permite que todos nuestros trabajos terminen funcionando antes de que se suponga que deben volver a iniciarse.

0

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. Luego, ConcurrentDictionary decide que quiere crecer en otro Add. Y quiere crecer mucho aparentemente (algún algoritmo prima interno) ya que se queda sin memoria. Esto está en x64 con 32 GB de memoria.

Por lo tanto, me gustaría que un booleano bloquee la regeneración/repetición automática de un diccionario (concurrente). Inicializaría el diccionario en la creación con un conjunto fijo de depósitos (¡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 que se repita y se salga de la memoria demasiado rápido e innecesariamente.

Cuestiones relacionadas