Intenté ejecutar el programa X con 8 subprocesos y terminó en n minutos .
Intenté ejecutar el mismo programa con 50 subprocesos y terminó en n * 10 minutos .
¿Por qué sucede esto y cómo puedo obtener el número óptimo de hilos que puedo usar?
multithreading
PoGibas
fuente
fuente
"¿Por qué pasó esto?" Es un poco fácil de responder. Imagine que tiene un corredor en el que puede acomodar a cuatro personas, una al lado de la otra. Desea mover toda la basura de un extremo al otro. El número más eficiente de personas es 4.
Si tienes de 1 a 3 personas, te estás perdiendo el uso del espacio del pasillo. Si tiene 5 o más personas, entonces al menos una de esas personas está básicamente atrapada haciendo cola detrás de otra persona todo el tiempo. Agregar más y más personas simplemente obstruye el corredor, no acelera la actividad.
Por lo tanto, desea tener tantas personas como sea posible sin causar colas. ¿Por qué has cola (o cuellos de botella) depende de las preguntas de la respuesta de SLM.
fuente
4
es el mejor número.Una recomendación común es n + 1 subprocesos, n es el número de núcleos de CPU disponibles. De esa manera, n subprocesos pueden trabajar la CPU mientras 1 subproceso está esperando la E / S de disco. Tener menos subprocesos no utilizaría completamente el recurso de la CPU (en algún momento siempre habrá E / S para esperar), tener más subprocesos provocaría que los subprocesos peleen por el recurso de la CPU.
Los subprocesos no son gratuitos, pero con cambios generales como contexto y, si los datos tienen que intercambiarse entre subprocesos, que suele ser el caso, varios mecanismos de bloqueo. Esto solo vale el costo cuando en realidad tiene núcleos de CPU más dedicados para ejecutar el código. En una CPU de un solo núcleo, un solo proceso (sin hilos separados) suele ser más rápido que cualquier subproceso realizado. Los hilos no hacen que su CPU vaya mágicamente más rápido, solo significa trabajo extra.
fuente
Como otros han señalado ( respuesta slm , respuesta EightBitTony ), esta es una pregunta complicada y más aún, ya que no describe lo que hizo y cómo lo hacen.
Pero arrojar definitivamente más hilos puede empeorar las cosas.
En el campo de la computación paralela, existe la ley de Amdahl que puede ser aplicable (o no, pero no describe los detalles de su problema, entonces ...) y puede dar una idea general sobre esta clase de problemas.
El punto de la ley de Amdahl es que en cualquier programa (en cualquier algoritmo) siempre hay un porcentaje que no se puede ejecutar en paralelo (la parte secuencial ) y hay otro porcentaje que se puede ejecutar en paralelo (la parte paralela ) [Obviamente estas dos porciones suman 100%].
Estas porciones se pueden expresar como un porcentaje del tiempo de ejecución. Por ejemplo, puede haber un 25% del tiempo invertido en operaciones estrictamente secuenciales, y el 75% restante del tiempo dedicado a la operación puede ejecutarse en paralelo.
(Imagen de Wikipedia )
La ley de Amdahl predice que por cada porción paralela dada (p. Ej., 75%) de un programa, puede acelerar la ejecución solo hasta ahora (p. Ej., Como máximo 4 veces) incluso si usa más y más procesadores para hacer el trabajo.
Como regla general, cuanto más programa usted no pueda transformar en ejecución paralela, menos podrá obtener utilizando más unidades de ejecución (procesadores).
Dado que está utilizando subprocesos (y no procesadores físicos), la situación puede ser aún peor que esto. Recuerde que los subprocesos se pueden procesar (dependiendo de la implementación y el hardware disponible, por ejemplo, CPU / núcleos) que comparten el mismo procesador / núcleo físico (es una forma de multitarea, como se señala en otra respuesta).
Esta predicción teórica (sobre los tiempos de CPU) no considera otros cuellos de botella prácticos como
eso puede ser fácilmente el factor limitante en aplicaciones prácticas.
fuente
El culpable aquí debería ser el "CONTEXTO DE CONMUTACIÓN". Es el proceso de guardar el estado del subproceso actual para comenzar a ejecutar otro subproceso. Si a varios subprocesos se les da la misma prioridad, deben cambiarse hasta que finalicen la ejecución.
En su caso, cuando hay 50 subprocesos, se produce una gran cantidad de cambios de contexto en comparación con solo ejecutar 10 subprocesos.
Esta sobrecarga introducida debido al cambio de contexto es lo que hace que su programa funcione lento
fuente
ps ax | wc -l
informa 225 procesos, y de ninguna manera está muy cargado). Me inclino a ir con la suposición de @ EightBitTony; La invalidación de la memoria caché es probablemente un problema mayor, porque cada vez que vacía la memoria caché, la CPU tiene que esperar eones para obtener el código y los datos de la RAM.Para arreglar la metáfora de EightBitTony:
Una persona aquí es un hilo, y un cubo representa cualquier recurso de ejecución que sea el cuello de botella. Agregar más hilos no ayuda si no pueden hacer nada. Además, debemos enfatizar que pasar un cubo de una persona a otra suele ser más lento que una sola persona que solo lleva el cubo la misma distancia. Es decir, dos subprocesos que se turnan en un núcleo generalmente realizan menos trabajo que un solo subproceso que se ejecuta el doble de tiempo: esto se debe al trabajo adicional realizado para cambiar entre los dos subprocesos.
Si el recurso de ejecución limitante (bucket) es una CPU, o un núcleo, o una canalización de instrucciones hiperhebra para sus propósitos, depende de qué parte de la arquitectura sea su factor limitante. Tenga en cuenta también que estamos asumiendo que los hilos son completamente independientes. Este es sólo el caso si comparten no hay datos (y evitar cualquier colisión de caché).
Como han sugerido un par de personas, para E / S, el recurso limitante podría ser la cantidad de operaciones de E / S útilmente en cola: esto podría depender de una gran cantidad de factores de hardware y kernel, pero fácilmente podría ser mucho mayor que la cantidad de núcleos Aquí, el cambio de contexto que es tan costoso en comparación con el código vinculado a la ejecución, es bastante barato en comparación con el código vinculado de E / S. Lamentablemente, creo que la metáfora se descontrolará por completo si trato de justificar esto con cubos.
Tenga en cuenta que el comportamiento óptimo con el código enlazado de E / S generalmente sigue teniendo como máximo un subproceso por canalización / núcleo / CPU. Sin embargo, debe escribir código de E / S asíncrono o síncrono / sin bloqueo, y la mejora del rendimiento relativamente pequeña no siempre justificará la complejidad adicional.
PD. Mi problema con la metáfora original del corredor es que sugiere fuertemente que debería poder tener 4 colas de personas, con 2 colas cargando basura y 2 regresando para recoger más. A continuación, puede hacer que cada cola casi tan largo como el corredor, y la gente añadiendo hizo acelerar el algoritmo (que, básicamente, se volvió todo el corredor en una cinta transportadora).
De hecho, este escenario es muy similar a la descripción estándar de la relación entre la latencia y el tamaño de la ventana en las redes TCP, por lo que me llamó la atención.
fuente
Es bastante sencillo y fácil de entender. Tener más subprocesos de los que admite su CPU realmente está serializando y no paralelizando. Cuantos más subprocesos tenga, más lento será su sistema. Sus resultados son en realidad una prueba de este fenómeno.
fuente