tutorial parallelism jenkov and java multithreading concurrency cpu-cores processors

java - jenkov - concurrency and parallelism difference



Configuración del tamaño ideal de la agrupación de subprocesos (3)

Esta pregunta ya tiene una respuesta aquí:

Cuál es la diferencia entre-

newSingleThreadExecutor vs newFixedThreadPool(20)

En términos de sistema operativo y punto de vista de programación.

Cuando estoy ejecutando mi programa usando newSingleThreadExecutor mi programa funciona muy bien y la latencia de punta a punta (percentil 95) es de aproximadamente 5 5ms .

Pero tan pronto como comience a ejecutar mi programa usando-

newFixedThreadPool(20)

El rendimiento de mi programa se degrada y comienzo a ver la latencia de extremo a extremo en 37ms .

Así que ahora estoy tratando de entender desde el punto de vista de la arquitectura, ¿qué significa aquí el número de hilos? ¿Y cómo decidir cuál es el número óptimo de hilos que debería elegir?

Y si estoy usando más cantidad de hilos, ¿qué pasará?

Si alguien me puede explicar estas cosas simples en un lenguaje lego, eso será muy útil para mí. Gracias por la ayuda.

La especificación de configuración de mi máquina. Estoy ejecutando mi programa desde la máquina Linux.

processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 45 model name : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz stepping : 7 cpu MHz : 2599.999 cache size : 20480 KB fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm arat pln pts bogomips : 5199.99 clflush size : 64 cache_alignment : 64 address sizes : 40 bits physical, 48 bits virtual power management: processor : 1 vendor_id : GenuineIntel cpu family : 6 model : 45 model name : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz stepping : 7 cpu MHz : 2599.999 cache size : 20480 KB fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm arat pln pts bogomips : 5199.99 clflush size : 64 cache_alignment : 64 address sizes : 40 bits physical, 48 bits virtual power management:


Así que ahora estoy tratando de entender desde el punto de vista de la arquitectura, ¿qué significa aquí el número de hilos?

Cada hilo tiene su propia memoria de pila, contador de programas (como un puntero a qué instrucción se ejecuta a continuación) y otros recursos locales. Cambiarlos perjudica la latencia para una sola tarea. El beneficio es que mientras un hilo está inactivo (generalmente cuando se espera la entrada / salida), otro hilo puede hacer el trabajo. Además, si hay varios procesadores disponibles, se pueden ejecutar en paralelo si no hay recursos y / o contención de bloqueo entre las tareas.

¿Y cómo decidir cuál es el número óptimo de hilos que debería elegir?

La compensación entre el precio de intercambio frente a la oportunidad de evitar el tiempo de inactividad depende de los pequeños detalles de cómo se ve su tarea (cuánto i / o, y cuándo, con cuánto trabajo entre i / o, usando cuánta memoria completar). La experimentación es siempre la clave.

Y si estoy usando más cantidad de hilos, ¿qué pasará?

Por lo general, al principio habrá un crecimiento lineal en el rendimiento, luego una parte relativamente plana, luego una caída (que puede ser bastante pronunciada). Cada sistema es diferente.


De acuerdo. Lo ideal sería que los subprocesos no tuvieran bloqueo, de manera que no se bloqueen entre sí (independientemente uno del otro) y se pueda suponer que la carga de trabajo (procesamiento) es la misma, luego resulta que, tienen un tamaño de grupo de Runtime.getRuntime().availableProcessors() o availableProcessors() + 1 da los mejores resultados.

Pero digamos, si los subprocesos interfieren entre sí o tienen la I / O involucrada, entonces la ley de Amadhal explica bastante bien. De wiki,

La ley de Amdahl establece que si P es la proporción de un programa que se puede hacer en paralelo (es decir, se beneficia de la paralelización), y (1 - P) es la proporción que no puede ser paralelizada (permanece en serie), entonces la aceleración máxima que puede ser logrado mediante el uso de procesadores N es

En su caso, según el número de núcleos disponibles y el trabajo que realizan con precisión (¿cálculo puro? ¿E / S? ¿Bloqueos de retención? ¿Están bloqueados para algún recurso? Etc.), debe encontrar la solución basada en lo anterior. parámetros

Por ejemplo: hace algunos meses participé en la recopilación de datos de numerosos sitios web. Mi máquina era de 4 núcleos y tenía un tamaño de piscina de 4 . Pero como la operación fue puramente de I/O y mi velocidad neta fue decente, me di cuenta de que tenía el mejor rendimiento con un tamaño de grupo de 7 . Y eso es porque los subprocesos no luchaban por la potencia de cálculo, sino por la E / S. Así que podría aprovechar el hecho de que más hilos pueden competir por el núcleo de manera positiva.

PD: Sugiero, repasando el capítulo Rendimiento del libro - Java Concurrency in Practice por Brian Goetz. Se ocupa de tales asuntos en detalle.


Mirar la ley de Amdahl está bien, especialmente si sabes exactamente cuán grandes son P y N. Como esto nunca sucederá realmente, puede monitorear el rendimiento (lo que debería hacer de todos modos) y aumentar / disminuir el tamaño de la agrupación de subprocesos para optimizar las métricas de rendimiento que sean importantes para usted.