Trabajo con corrutinas bastante tiempo, pero todavía no entiendo completamente, ¿por qué necesito preferir corrutinas de subprocesos múltiples en lugar de corrutinas de un solo subproceso?
Puedo ver claramente el beneficio de usar corrutinas de subprocesos múltiples cuando su conteo es menor o igual al conteo de subprocesos físicos. Pero si tenemos más tareas que hilos físicos, ¿por qué no preferiríamos usar solo un hilo de rutina?
Aclararé la pregunta final: ¿Por qué 10 subprocesos de rutinas son mejores que solo un subproceso con muchas corrutinas?
Las corrutinas son unidades de computación (como tareas). La forma en que se envían a los subprocesos reales es ortogonal a la cantidad de rutinas que tiene. Puede usar un despachador de subproceso único o un despachador de subprocesos múltiples, y dependiendo de esto, sus rutinas se programarán de manera diferente.
Las corrutinas de subprocesos múltiples no significan 1 subproceso por corrutina. Puede enviar 100 corotinas en 8 subprocesos.
Pero si tenemos más tareas que hilos físicos, ¿por qué no preferiríamos usar solo un hilo de rutina?
Hay varias partes en esta pregunta.
En primer lugar, si tiene más tareas que núcleos lógicos, aún podría enviar todas esas tareas a la cantidad justa de subprocesos. No tienes que renunciar por completo a los subprocesos múltiples. En realidad, esto es exactamente de lo que se trata Dispatchers.Default : enviar tantas corrutinas como desee en una cantidad limitada de subprocesos igual a la cantidad de subprocesos de hardware (núcleos lógicos) que tiene. El punto es hacer uso de todo el hardware tanto como sea posible sin desperdiciar anuncios (y por lo tanto memoria).
En segundo lugar, no todas las tareas están vinculadas a la CPU. Algunas operaciones de E/S bloquean subprocesos (llamadas de red, lecturas/escrituras de disco, etc.). Cuando un subproceso está bloqueado en E/S, no utiliza la CPU. Si tiene 8 núcleos lógicos, usar solo 8 subprocesos para E/S sería subóptimo, porque mientras algunos subprocesos están bloqueados, la CPU no puede ejecutar otras tareas. Con más subprocesos, puede (a costa de algo de memoria). Este es el punto de Dispatchers.IO , que puede crear más subprocesos según sea necesario y puede exceder la cantidad de núcleos lógicos (dentro de un límite razonable).
¿Por qué 10 subprocesos de rutinas son mejores que solo un subproceso con muchas corrutinas?
Supongamos que tiene 100 rutinas para enviar.
Usar solo un subproceso para ejecutar esas corrutinas implica que solo 1 núcleo como máximo está haciendo el trabajo en un momento dado, por lo que no sucede nada en paralelo. Esto significa que todos los demás núcleos están inactivos, lo que no es óptimo. Peor aún, cualquier operación de E/S realizada por una corrutina bloquea este único hilo y evita que la CPU haga algo mientras esperamos la E/S.
Usando 10 subprocesos, puede ejecutar literalmente 10 rutinas al mismo tiempo si su hardware es suficiente, lo que puede ser 10 veces más rápido (si sus rutinas no tienen interdependencias).
El uso de 100 subprocesos no sería tan beneficioso si sus rutinas están vinculadas a la CPU, pero podría ser útil si tiene un montón de tareas de E/S (como hemos visto). Dicho esto, cuantos más subprocesos utilice, más memoria se consume. Entonces, incluso con una tonelada de operaciones de E/S, debe encontrar un equilibrio entre el rendimiento y la memoria, no desea generar millones de subprocesos.
En resumen, el uso de subprocesos múltiples sigue teniendo las mismas ventajas con o sin corrutinas: permite utilizar los recursos de su hardware tanto como sea posible. El uso de rutinas es solo una forma más fácil de definir tareas, enviarlas a subprocesos, expresar dependencias, evitar bloquear subprocesos innecesariamente, etc.