2012-04-13 27 views
5

Tengo un método en mi algoritmo que ejecuta un ciclo muy ajustado en un gran conjunto de datos. Originalmente lo escribí de subproceso único, que estaba bien, pero tardó mucho tiempo. Estoy al punto ahora de querer acelerarlo, entonces ahora estoy usando ThreadPool para paralelizar el trabajo. El problema es que esto hace que el uso de mi CPU pase al 95-100%, lo que esperaba. Sin embargo, mi desempeño ha aumentado dramáticamente, pero creo que podría hacerlo mejor si pudiera reducir todo el cambio de contexto. Esto también hace que mis otros programas estén un poco descuidados ya que tienen que luchar contra los hilos de los recursos de la CPU.Devolución de llamada de ThreadPool en lazo cerrado - 100% CPU

Mi pregunta es ¿cómo debo hacer esto? Lo único que he podido pensar es limitar el número de subprocesos que se ejecutan a la vez, pero esto puede hacer que mi algoritmo sea más lento, ya que solo unos pocos subprocesos se podrán ejecutar a la vez. Tampoco quiero añadir dormidos en mis hilos, ya que solo necesito que el algoritmo se ejecute lo más rápido posible.

EDITAR: Varias personas han mencionado usar el TPL. Creo que es una gran idea, pero desafortunadamente me olvidé de mencionar que estoy atascado con .NET 3.5 ya que la aplicación principal aún no ha lanzado una versión con .NET 4.

+1

Si quiere velocidad, ¿por qué se llevaría todas las cosas a la velocidad? El cambio de contexto lo realiza el sistema operativo, no te metas con eso ... – gbianchi

+1

La solución es reducir la prioridad de los subprocesos en el grupo. Esta no es una respuesta porque no sé cómo hacerlo de manera eficiente :( –

+0

Suena como que debe hacer que las tareas individuales sean más grandes. OTOH, el grupo de subprocesos es lo suficientemente inteligente como para hacer ya la mayor parte de lo que sugiere. Si tiene más tareas que las CPU, los pondrá en cola en lugar de iniciar más hilos. –

Respuesta

6

Esto es todo acerca de la administración de recursos. Su programa actualmente está acaparando todos los recursos, por lo que otros programas tienen acceso reducido a ellos. Debe equilibrar el "Solo necesito que el algoritmo se ejecute hasta su finalización lo más rápido posible", junto con "Esto también hace que mis otros programas estén un poco retrasados, ya que tienen que luchar contra los hilos de los recursos de la CPU". Son mutuamente excluyentes; no puede hacer que su aplicación se ejecute tan rápido como sea posible en una máquina en particular y también permite que otras aplicaciones respondan perfectamente. Simplemente hay un límite de cuánto puede hacer la CPU en cualquier período de tiempo.

En cuanto a ganancias de eficiencia, hay algunas cosas que puede hacer:

  • No utilice el ThreadPool para los algoritmos ultra optimizado roscados. El ThreadPool es excelente para operaciones simples de "Salir y haz esto y hazme saber que ya terminaste". Sin embargo, si está buscando optimizar, se puede evitar la sobrecarga inherente en la adición de un nivel adicional de programación de subprocesos con ThreadPool (además de la sobrecarga inherente en la CPU y el sistema operativo). También tiene un control más limitado sobre los hilos en un ThreadPool, lo que significa optimizaciones como la asignación de la afinidad del procesador (para equilibrar la carga) y la prioridad (para dar un hilo más o menos tiempo) de hilos individuales no están disponibles.Intenta crear Hilos simples, o investigando el TPL que tiene una serie de estrategias para hacer varias cosas (no todas requieren enhebrar en primer lugar).

  • Sí, querrá poder "estrangular" el número de subprocesos. Esto es tanto para permitir que otros programas tengan un poco de tiempo de CPU al reducir la necesidad de su programa, pero como dije, también hay gastos generales inherentes al multihilo. La regla general es que si a una CPU se le asigna más del doble del conteo de subprocesos activos ya que tiene "unidades de ejecución" (estos son los núcleos físicos en un chip de CPU, y los "procesadores lógicos" como la tecnología HyperThreading que divide un núcleo en dos), entonces el sistema operativo pasará más tiempo planificando los hilos y cambiando entre ellos ("cache-thrashing") de lo que realmente gastará al ejecutar los hilos. En términos más generales, existe una ley de rendimientos decrecientes, que progresará hacia "deseconomías de escala"; eventualmente, agregar otro hilo hará que su programa se ejecute más lentamente que si no hubiera usado ese hilo. Sí, ThreadPool maneja los hilos máximos para ti, pero esa es probablemente la más simple de sus varias características para implementarse en su propio algoritmo.

  • Asegúrese de que el trabajo de cada hilo esté optimizado. Busque algoritmos ingenuos o ineficientes (los llamo "O (Mi Dios) -complejidad") y los agilizo. Hay un límite inferior para la eficiencia de la mayoría de las operaciones (varía según el tipo de operación) y "la optimización prematura es la raíz de todo mal" (no optimice el rendimiento a expensas de hacer que el código realmente funcione), pero Comprenda que en un entorno multiproceso, cualquier ganancia que pueda obtener en la eficiencia de un algoritmo cuando se ejecuta una vez se multiplicará por la cantidad de veces que la ejecuta, por lo que asegurarse de que una operación paralela sea eficiente es una doble ventaja.

+0

+1 solo para O (Mi Dios) solo - gran respuesta ;-) – BrokenGlass

+1

'La regla de oro es que si una CPU recibe más del doble de la cuenta de ejecutar activamente los hilos como lo ha hecho "unidades de ejecución" (estos son los núcleos físicos en un chip CPU y "procesadores lógicos" como la tecnología HyperThreading que divide un núcleo en dos), entonces el sistema operativo pasará más tiempo programando hilos y cambiando entre ellos ("cache-thrashing")) de lo que gastará realmente corriendo los hilos '- ¿realmente has probado esto? En el código no administrado, no hay ninguna diferencia si tiene 8 subprocesos enlazados a la CPU u 800 - aproximadamente la misma cantidad de trabajo se realiza. –

+0

Entonces, si tengo una CPU Core i7 (4 núcleos físicos + 4 núcleos virtuales), ¿16 hilos es el límite según esa regla? –

2

Si puede reescribir la aplicación principal en un bucle foreach en un IEnumerable, puede usar PLINQ para paralelizar su ciclo. Puede utilizar WithDegreeOfParallelism para controlar cuántos núcleos usará su aplicación. Puede evitar el "retraso" que experimenta al no usar todos los núcleos en su computadora. Además, no tiene que lidiar con la partición de su ciclo en los hilos para evitar la contención de recursos innecesarios. PLINQ hace todo eso por ti.

Asumiendo que tiene esta muy simple bucle de un solo subproceso:

var arrayOfStuff = new[] { ... }; 
for (var i = 0; i < arrayOfStuff.Length; ++i) 
    DoSomething(arrayOfStuff[i]); 

Si el pedido no importa se puede paralelizar usando PLINQ utilizando un núcleo menor que está disponible:

var cores = Math.Max(1, Environment.ProcessorCount - 1); 
arrayOfStuff.AsParallel().WithDegreeOfParallelism(cores).ForAll(DoSomething); 

Incluso si su bucle principal es más complejo, puede volver a escribirlo en un bloque de iteradores que luego puede paralelizar:

IEnumerable<Stuff> GetStuff() { 
    for (... very complex looping ...) { 
    ... 
    yield return stuff; 
    } 
}