¿La E / S sin bloqueo es realmente más rápida que la E / S de bloqueo de subprocesos múltiples? ¿Cómo?

117

Busqué en la web algunos detalles técnicos sobre el bloqueo de E / S y no bloqueo de E / S y encontré a varias personas que afirmaban que el bloqueo de E / S sería más rápido que el bloqueo de E / S. Por ejemplo en este documento .

Si utilizo el bloqueo de E / S, entonces, por supuesto, el hilo que está bloqueado actualmente no puede hacer nada más ... Porque está bloqueado. Pero tan pronto como un hilo comienza a bloquearse, el sistema operativo puede cambiar a otro hilo y no volver a cambiar hasta que haya algo que hacer por el hilo bloqueado. Entonces, siempre que haya otro hilo en el sistema que necesite CPU y no esté bloqueado, no debería haber más tiempo de inactividad de CPU en comparación con un enfoque sin bloqueo basado en eventos, ¿verdad?

Además de reducir el tiempo que la CPU está inactiva, veo una opción más para aumentar la cantidad de tareas que una computadora puede realizar en un período de tiempo determinado: Reducir la sobrecarga introducida por el cambio de subprocesos. Pero, ¿cómo se puede hacer esto? ¿Es la sobrecarga lo suficientemente grande como para mostrar efectos medibles? Aquí hay una idea de cómo puedo imaginarlo funcionando:

  1. Para cargar el contenido de un archivo, una aplicación delega esta tarea a un marco de E / S basado en eventos, pasando una función de devolución de llamada junto con un nombre de archivo
  2. El marco de eventos delega en el sistema operativo, que programa un controlador DMA del disco duro para escribir el archivo directamente en la memoria.
  3. El marco de eventos permite que se ejecute más código.
  4. Una vez completada la copia de disco a memoria, el controlador DMA provoca una interrupción.
  5. El controlador de interrupciones del sistema operativo notifica al marco de E / S basado en eventos sobre la carga completa del archivo en la memoria. ¿Como hace eso? ¿Usando una señal?
  6. Finaliza el código que se ejecuta actualmente dentro del marco de trabajo de E / S de eventos.
  7. El marco de trabajo de E / S basado en eventos comprueba su cola y ve el mensaje del sistema operativo del paso 5 y ejecuta la devolución de llamada que recibió en el paso 1.

¿Así es como funciona? Si no es así, ¿cómo funciona? Eso significa que el sistema de eventos puede funcionar sin tener la necesidad de tocar explícitamente la pila (como un programador real que necesitaría hacer una copia de seguridad de la pila y copiar la pila de otro hilo en la memoria mientras cambia de hilo). ¿Cuánto tiempo realmente ahorra esto? ¿Hay más?

yanqui
fuente
5
respuesta corta: se trata más de la sobrecarga de tener un hilo por conexión. io sin bloqueo permite evitar tener un hilo por conexión.
Dan D.
10
El bloqueo de E / S es caro en un sistema en el que no se pueden crear tantos subprocesos como conexiones existen. En la JVM puede crear miles de subprocesos, pero ¿qué pasa si tiene más de 100.000 conexiones? Así que tienes que ceñirte a una solución asincrónica. Sin embargo, hay lenguajes donde los hilos no son costosos (por ejemplo, hilos verdes) como en Go / Erlang / Rust donde no es un problema tener 100.000 hilos. Cuando el número de subprocesos puede ser grande, creo que el bloqueo de E / S produce tiempos de respuesta más rápidos. Pero eso es algo que también tendría que preguntar a los expertos si eso es cierto en la realidad.
OlliP
@OliverPlow, yo también lo creo, porque bloquear IO generalmente significa que dejamos que el sistema maneje la "administración paralela", en lugar de hacerlo nosotros mismos utilizando colas de tareas y demás.
Pacerier
1
@DanD., ¿Y si la sobrecarga de tener subprocesos es igual a la sobrecarga de realizar E / S sin bloqueo? (generalmente cierto en el caso de hilos verdes)
Pacerier
"copiar la pila" no sucede. Los diferentes subprocesos tienen sus pilas en diferentes direcciones. Cada hilo tiene su propio puntero de pila, junto con otros registros. Un cambio de contexto guarda / restaura solo el estado arquitectónico (incluidos todos los registros), pero no la memoria. Entre subprocesos en el mismo proceso, el kernel ni siquiera tiene que cambiar las tablas de páginas.
Peter Cordes

Respuestas:

44

La mayor ventaja de las E / S asíncronas o sin bloqueo es que su hilo puede continuar su trabajo en paralelo. Por supuesto, puede lograr esto también utilizando un hilo adicional. Como indicó para obtener el mejor rendimiento general (del sistema), supongo que sería mejor usar E / S asincrónicas y no múltiples subprocesos (reduciendo así el cambio de subprocesos).

Veamos posibles implementaciones de un programa de servidor de red que manejará 1000 clientes conectados en paralelo:

  1. Un subproceso por conexión (puede bloquear E / S, pero también puede ser E / S sin bloqueo).
    Cada hilo requiere recursos de memoria (¡también memoria del kernel!), Eso es una desventaja. Y cada hilo adicional significa más trabajo para el programador.
  2. Un hilo para todas las conexiones.
    Esto toma carga del sistema porque tenemos menos subprocesos. Pero también le impide utilizar el rendimiento completo de su máquina, porque podría terminar conduciendo un procesador al 100% y dejando todos los demás procesadores inactivos.
  3. Algunos hilos donde cada hilo maneja algunas de las conexiones.
    Esto toma carga del sistema porque hay menos subprocesos. Y puede utilizar todos los procesadores disponibles. En Windows, este enfoque es compatible con Thread Pool API .

Por supuesto, tener más hilos no es un problema per se. Como habrás reconocido, elegí una gran cantidad de conexiones / subprocesos. Dudo que vea alguna diferencia entre las tres posibles implementaciones si estamos hablando de solo una docena de subprocesos (esto es también lo que Raymond Chen sugiere en la publicación del blog de MSDN. ¿Windows tiene un límite de 2000 subprocesos por proceso? ).

En Windows, el uso de E / S de archivos sin búfer significa que las escrituras deben tener un tamaño que sea un múltiplo del tamaño de la página. No lo he probado, pero parece que esto también podría afectar positivamente el rendimiento de escritura para escrituras síncronas y asíncronas almacenadas en búfer.

Los pasos 1 a 7 que describe le dan una buena idea de cómo funciona. En Windows, el sistema operativo le informará sobre la finalización de una E / S asíncrona ( WriteFilecon OVERLAPPEDestructura) mediante un evento o una devolución de llamada. Las funciones de devolución de llamada solo se llamarán, por ejemplo, cuando su código llame WaitForMultipleObjectsExcon bAlertableconfigurado en true.

Más lectura en la web:

Werner Henze
fuente
Desde el punto de vista de la web, el conocimiento común (Internet, comentarios de expertos) sugiere que aumentar enormemente el max. La cantidad de subprocesos de solicitud es algo malo en el bloqueo de IO (lo que hace que el procesamiento de solicitudes sea aún más lento) debido al aumento de la memoria y al tiempo de cambio de contexto, pero, ¿Async IO no está haciendo lo mismo cuando se difiere el trabajo a otro subproceso? Sí, puede atender más solicitudes ahora pero tener la misma cantidad de hilos en segundo plano ... ¿cuál es el beneficio real de eso?
JavierJ
1
@JavierJ ¿Parece creer que si n subprocesos realizan IO de archivos asíncronos, se crearán otros n subprocesos para hacer un IO de archivos de bloqueo? Esto no es verdad. El sistema operativo tiene soporte para E / S de archivos asíncronos y no necesita bloquearse cuando se espera que se complete el E / S. Puede poner en cola solicitudes de E / S y, si se produce una interrupción de hardware (por ejemplo, DMA), puede marcar la solicitud como realizada y establecer un evento que señale el hilo de las personas que llaman. Incluso si se requiriera un hilo adicional, el sistema operativo podría usar ese hilo para múltiples solicitudes de E / S de múltiples hilos.
Werner Henze
Gracias, tiene sentido involucrar el soporte de IO del archivo asíncrono del sistema operativo, pero cuando escribo código para una implementación real de esto (desde el punto de vista web), digamos con Java Servlet 3.0 NIO, todavía veo un hilo para la solicitud y un hilo de fondo ( async) en bucle para leer un archivo, base de datos o lo que sea.
JavierJ
1
@piyushGoyal Reescribí mi respuesta. Espero que ahora esté más claro.
Werner Henze
1
En Windows, el uso de E / S de archivos asíncronos significa que las escrituras deben ser de un tamaño que sea un múltiplo del tamaño de la página. - no, no es así. Estás pensando en E / S sin búfer. (A menudo se usan juntos, pero no es necesario que lo estén)
Harry Johnston
29

La E / S incluye varios tipos de operaciones, como leer y escribir datos desde discos duros, acceder a recursos de red, llamar a servicios web o recuperar datos de bases de datos. Dependiendo de la plataforma y del tipo de operación, las E / S asíncronas generalmente aprovecharán cualquier soporte de hardware o sistema de bajo nivel para realizar la operación. Esto significa que se realizará con el menor impacto posible en la CPU.

A nivel de aplicación, la E / S asincrónica evita que los subprocesos tengan que esperar a que se completen las operaciones de E / S. Tan pronto como se inicia una operación de E / S asincrónica, libera el subproceso en el que se inició y se registra una devolución de llamada. Cuando se completa la operación, la devolución de llamada se pone en cola para su ejecución en el primer subproceso disponible.

Si la operación de E / S se ejecuta sincrónicamente, mantiene su subproceso en ejecución sin hacer nada hasta que se completa la operación. El tiempo de ejecución no sabe cuándo se completa la operación de E / S, por lo que periódicamente proporcionará algo de tiempo de CPU al subproceso en espera, tiempo de CPU que de otro modo podría haber sido utilizado por otros subprocesos que tienen operaciones reales vinculadas a la CPU para realizar.

Entonces, como mencionó @ user1629468, la E / S asíncrona no proporciona un mejor rendimiento sino una mejor escalabilidad. Esto es obvio cuando se ejecuta en contextos que tienen un número limitado de subprocesos disponibles, como es el caso de las aplicaciones web. Las aplicaciones web suelen utilizar un grupo de subprocesos desde el que asignan subprocesos a cada solicitud. Si las solicitudes se bloquean en operaciones de E / S de ejecución prolongada, existe el riesgo de agotar el grupo web y hacer que la aplicación web se congele o demore en responder.

Una cosa que he notado es que la E / S asincrónica no es la mejor opción cuando se trata de operaciones de E / S muy rápidas. En ese caso, el beneficio de no mantener un subproceso ocupado mientras se espera que se complete la operación de E / S no es muy importante y el hecho de que la operación se inicie en un subproceso y se complete en otro agrega una sobrecarga a la ejecución general.

Puede leer una investigación más detallada que he realizado recientemente sobre el tema de E / S asíncrona frente a subprocesos múltiples aquí .

Florin Dumitrescu
fuente
Me pregunto si valdría la pena hacer una distinción entre las operaciones de E / S que se espera que se completen y las cosas que no pueden [por ejemplo, "obtener el siguiente carácter que llega a un puerto serie", en los casos en que el dispositivo remoto puede o no enviar cualquier cosa]. Si se espera que una operación de E / S se complete en un tiempo razonable, se puede retrasar la limpieza de los recursos relacionados hasta que se complete la operación. Sin embargo, si la operación nunca se completa, tal demora sería irrazonable.
supercat
@supercat, el escenario que está describiendo se usa en aplicaciones y bibliotecas de nivel inferior. Los servidores confían en él, ya que esperan continuamente las conexiones entrantes. La E / S asíncrona descrita anteriormente no puede caber en este escenario porque se basa en iniciar una operación específica y registrar una devolución de llamada para completarla. En el caso que está describiendo, debe registrar una devolución de llamada en un evento del sistema y procesar cada notificación. Está procesando continuamente la entrada en lugar de realizar operaciones. Como se dijo, esto generalmente se hace a bajo nivel, casi nunca en sus aplicaciones.
Florin Dumitrescu
El patrón es bastante común en aplicaciones que vienen con varios tipos de hardware. Los puertos seriales no son tan comunes como solían ser, pero los chips USB que emulan los puertos seriales son bastante populares en el diseño de hardware especializado. Los caracteres de tales cosas se manejan a nivel de aplicación, ya que el sistema operativo no tendrá forma de saber que una secuencia de caracteres de entrada significa, por ejemplo, que se abrió un cajón de efectivo y se debe enviar una notificación a algún lugar.
supercat
No creo que la parte sobre el costo de la CPU del bloqueo de IO sea precisa: cuando está en estado de bloqueo, el sistema operativo pone en espera un hilo que desencadenó el bloqueo de IO y no cuesta períodos de CPU hasta que el IO se haya completado por completo, solo después de lo cual ¿El sistema operativo (notifica mediante interrupciones) reanuda el hilo bloqueado? Lo que describió (espera ocupada por sondeo largo) no es cómo se implementa el bloqueo de E / S en casi cualquier tiempo de ejecución / compilador.
Lifu Huang
4

La razón principal para utilizar AIO es la escalabilidad. Cuando se ve en el contexto de algunos hilos, los beneficios no son obvios. Pero cuando el sistema escala a miles de subprocesos, AIO ofrecerá un rendimiento mucho mejor. La advertencia es que la biblioteca AIO no debería introducir más cuellos de botella.

fisurazona
fuente
4

Para suponer una mejora de la velocidad debido a cualquier forma de computación múltiple, debe suponer que se están ejecutando múltiples tareas basadas en CPU simultáneamente en múltiples recursos informáticos (generalmente núcleos de procesador) o que no todas las tareas se basan en el uso concurrente de el mismo recurso, es decir, algunas tareas pueden depender de un subcomponente del sistema (almacenamiento en disco, por ejemplo) mientras que algunas tareas dependen de otro (recibir comunicación desde un dispositivo periférico) y otras pueden requerir el uso de núcleos de procesador.

El primer escenario a menudo se denomina programación "paralela". El segundo escenario se denomina a menudo programación "concurrente" o "asincrónica", aunque en ocasiones también se utiliza "concurrente" para referirse al caso de permitir que un sistema operativo intercale la ejecución de múltiples tareas, independientemente de si dicha ejecución debe tomar lugar en serie o si se pueden utilizar varios recursos para lograr la ejecución en paralelo. En este último caso, "concurrente" generalmente se refiere a la forma en que la ejecución está escrita en el programa, más que desde la perspectiva de la simultaneidad real de la ejecución de la tarea.

Es muy fácil hablar de todo esto con supuestos tácitos. Por ejemplo, algunos se apresuran a hacer una afirmación como "La E / S asíncrona será más rápida que la E / S de subprocesos múltiples". Esta afirmación es dudosa por varias razones. En primer lugar, podría darse el caso de que algún marco de E / S asíncrono determinado se implemente precisamente con subprocesos múltiples, en cuyo caso son uno en el mismo y no tiene sentido decir que un concepto "es más rápido" que el otro. .

En segundo lugar, incluso en el caso de que exista una implementación de un solo subproceso de un marco asincrónico (como un bucle de eventos de un solo subproceso), aún debe hacer una suposición sobre lo que está haciendo ese bucle. Por ejemplo, una cosa tonta que puede hacer con un bucle de eventos de un solo subproceso es solicitar que complete de forma asincrónica dos tareas diferentes puramente vinculadas a la CPU. Si hiciera esto en una máquina con solo un núcleo de procesador único idealizado (ignorando las optimizaciones de hardware modernas), entonces realizar esta tarea "asincrónicamente" no funcionaría realmente de manera diferente a realizarla con dos subprocesos administrados de forma independiente, o con solo un proceso solitario: - la diferencia puede deberse al cambio de contexto de subprocesos oa las optimizaciones de la programación del sistema operativo, pero si ambas tareas van a la CPU, sería similar en cualquier caso.

Es útil imaginar muchos de los casos de esquina inusuales o estúpidos con los que podría encontrarse.

"Asincrónico" no tiene que ser simultáneo, por ejemplo, como se indicó anteriormente: usted ejecuta "asincrónicamente" dos tareas vinculadas a la CPU en una máquina con exactamente un núcleo de procesador.

La ejecución de subprocesos múltiples no tiene por qué ser simultánea: genera dos subprocesos en una máquina con un solo núcleo de procesador, o solicita a dos subprocesos que adquieran cualquier otro tipo de recurso escaso (imagine, por ejemplo, una base de datos de red que solo puede establecer uno conexión a la vez). La ejecución de los subprocesos puede estar intercalada, sin embargo, el programador del sistema operativo lo considera oportuno, pero su tiempo de ejecución total no se puede reducir (y se incrementará a partir del cambio de contexto del subproceso) en un solo núcleo (o más generalmente, si genera más subprocesos de los que hay) núcleos para ejecutarlos, o tener más subprocesos que piden un recurso de los que el recurso puede sostener). Lo mismo ocurre con el multiproceso.

Por lo tanto, ni la E / S asíncrona ni los subprocesos múltiples tienen que ofrecer una ganancia de rendimiento en términos de tiempo de ejecución. Incluso pueden ralentizar las cosas.

Sin embargo, si define un caso de uso específico, como un programa específico que hace una llamada a la red para recuperar datos de un recurso conectado a la red, como una base de datos remota, y también realiza algunos cálculos locales vinculados a la CPU, entonces puede comenzar a razonar sobre las diferencias de rendimiento entre los dos métodos dada una suposición particular sobre el hardware.

Las preguntas para hacer: ¿Cuántos pasos computacionales necesito realizar y cuántos sistemas independientes de recursos hay para realizarlos? ¿Hay subconjuntos de los pasos computacionales que requieran el uso de subcomponentes del sistema independientes y puedan beneficiarse de hacerlo simultáneamente? ¿Cuántos núcleos de procesador tengo y cuál es la sobrecarga de usar varios procesadores o subprocesos para completar tareas en núcleos separados?

Si sus tareas dependen en gran medida de subsistemas independientes, entonces una solución asincrónica podría ser buena. Si la cantidad de subprocesos necesarios para manejarlo fuera grande, de modo que el cambio de contexto no fuera trivial para el sistema operativo, entonces una solución asíncrona de un solo subproceso podría ser mejor.

Siempre que las tareas estén vinculadas por el mismo recurso (por ejemplo, múltiples necesidades para acceder simultáneamente a la misma red o recurso local), entonces el subproceso múltiple probablemente introducirá una sobrecarga insatisfactoria, y mientras que la asincronía de un solo subproceso puede introducir menos sobrecarga, en tal recurso- situación limitada tampoco puede producir una aceleración. En tal caso, la única opción (si desea una aceleración) es hacer disponibles múltiples copias de ese recurso (por ejemplo, múltiples núcleos de procesador si el recurso escaso es la CPU; una mejor base de datos que admita más conexiones simultáneas si el recurso escaso es una base de datos con conexión limitada, etc.).

Otra forma de decirlo es: permitir que el sistema operativo intercale el uso de un solo recurso para dos tareas no puede ser más rápido que simplemente dejar que una tarea use el recurso mientras la otra espera, y luego dejar que la segunda tarea termine en serie. Además, el costo del programador de entrelazar significa que en cualquier situación real crea realmente una desaceleración. No importa si el uso intercalado se produce en la CPU, un recurso de red, un recurso de memoria, un dispositivo periférico o cualquier otro recurso del sistema.

ely
fuente
2

Una posible implementación de E / S sin bloqueo es exactamente lo que dijo, con un grupo de subprocesos en segundo plano que bloquean la E / S y notifican al subproceso del creador de la E / S a través de algún mecanismo de devolución de llamada. De hecho, así es como funciona el módulo AIO en glibc. Aquí hay algunos detalles vagos sobre la implementación.

Si bien esta es una buena solución que es bastante portátil (siempre que tenga subprocesos), el sistema operativo generalmente puede brindar servicio de E / S sin bloqueo de manera más eficiente. Este artículo de Wikipedia enumera posibles implementaciones además del grupo de subprocesos.

Miguel
fuente
2

Actualmente estoy en el proceso de implementar async io en una plataforma integrada usando protothreads. El io sin bloqueo marca la diferencia entre funcionar a 16000 fps y 160 fps. El mayor beneficio de io sin bloqueo es que puede estructurar su código para hacer otras cosas mientras el hardware hace lo suyo. Incluso la inicialización de dispositivos se puede realizar en paralelo.

Martín

usuario2826084
fuente
1

En Node, se están lanzando varios subprocesos, pero es una capa hacia abajo en el tiempo de ejecución de C ++.

"Entonces, Sí, NodeJS es de un solo subproceso, pero esto es una verdad a medias, en realidad está controlado por eventos y de un solo subproceso con trabajadores en segundo plano. El bucle de eventos principal es de un solo subproceso, pero la mayoría de los trabajos de E / S se ejecutan en subprocesos separados, porque las API de E / S en Node.js son asincrónicas / sin bloqueo por diseño, para adaptarse al bucle de eventos ".

https://codeburst.io/how-node-js-single-thread-mechanism-work-understanding-event-loop-in-nodejs-230f7440b0ea

"Node.js no es bloqueante, lo que significa que todas las funciones (devoluciones de llamada) se delegan al bucle de eventos y son (o pueden ser) ejecutadas por diferentes subprocesos. Eso lo maneja el tiempo de ejecución de Node.js".

https://itnext.io/multi-threading-and-multi-process-in-node-js-ffa5bb5cde98 

La explicación "El nodo es más rápido porque no bloquea ..." es un poco de marketing y esta es una gran pregunta. Es eficiente y escalable, pero no exactamente de un solo hilo.

ChimeneaRayo
fuente
0

La mejora en lo que sé es que los usos E / S asíncrona (Estoy hablando de MS del sistema, sólo para aclarar) el así llamado I / O puertos de finalización . Al utilizar la llamada asincrónica, el marco aprovecha dicha arquitectura automáticamente, y se supone que esto es mucho más eficiente que el mecanismo de subprocesamiento estándar. Como experiencia personal, puedo decir que sentiría sensiblemente que su aplicación es más reactiva si prefiere AsyncCalls en lugar de bloquear hilos.

Felice Pollano
fuente
0

Permítame darle un contraejemplo de que la E / S asíncrona no funciona. Estoy escribiendo un proxy similar al que se muestra a continuación usando boost :: asio. https://github.com/ArashPartow/proxy/blob/master/tcpproxy_server.cpp

Sin embargo, el escenario de mi caso es que los mensajes entrantes (del lado del cliente) son rápidos mientras que los salientes (del lado del servidor) son lentos para una sesión, para mantenerse al día con la velocidad entrante o para maximizar el rendimiento total del proxy, tenemos que usar múltiples sesiones bajo una sola conexión.

Por lo tanto, este marco de E / S asíncrono ya no funciona. Necesitamos un grupo de hilos para enviar al servidor asignando a cada hilo una sesión.

Zhidian Du
fuente