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:
- 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
- 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.
- El marco de eventos permite que se ejecute más código.
- Una vez completada la copia de disco a memoria, el controlador DMA provoca una interrupción.
- 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?
- Finaliza el código que se ejecuta actualmente dentro del marco de trabajo de E / S de eventos.
- 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?
fuente
Respuestas:
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:
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.
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.
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 (
WriteFile
conOVERLAPPED
estructura) 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 llameWaitForMultipleObjectsEx
conbAlertable
configurado entrue
.Más lectura en la web:
fuente
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í .
fuente
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.
fuente
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.
fuente
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.
fuente
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
fuente
En Node, se están lanzando varios subprocesos, pero es una capa hacia abajo en el tiempo de ejecución de C ++.
https://codeburst.io/how-node-js-single-thread-mechanism-work-understanding-event-loop-in-nodejs-230f7440b0ea
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.
fuente
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.
fuente
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.
fuente