¿Qué significan los términos "enlazado a CPU" y "enlazado de E / S"?

Respuestas:

452

Es bastante intuitivo:

Un programa está vinculado a la CPU si iría más rápido si la CPU fuera más rápida, es decir, pasa la mayor parte de su tiempo simplemente usando la CPU (haciendo cálculos). Un programa que calcula nuevos dígitos de π generalmente estará enlazado a la CPU, solo está haciendo números crujientes.

Un programa está vinculado a E / S si iría más rápido si el subsistema de E / S fuera más rápido. A qué sistema de E / S se refiere puede variar; Normalmente lo asocio con el disco, pero, por supuesto, las redes o la comunicación en general también son comunes. Un programa que busca datos en un archivo enorme podría estar vinculado a E / S, ya que el cuello de botella es la lectura de los datos del disco (en realidad, este ejemplo es quizás algo anticuado en estos días con cientos de MB / s viniendo de SSDs).

relajarse
fuente
¿Cómo se relaciona esto con la comprensión de la comunicación HTTP en un dispositivo móvil? He visto un aumento en el uso de la CPU al usar las operaciones java.nio .
IgorGanapolsky
I / O es "entrada / salida".
Camarada Che
247

CPU Bound significa que la velocidad a la que progresa el proceso está limitada por la velocidad de la CPU. Es probable que una tarea que realiza cálculos en un pequeño conjunto de números, por ejemplo, multiplicar matrices pequeñas, esté vinculada a la CPU.

I / O Bound significa que la velocidad a la que progresa un proceso está limitada por la velocidad del subsistema de E / S. Es probable que una tarea que procesa datos del disco, por ejemplo, contar el número de líneas en un archivo esté vinculada a E / S.

Memoria enlazada significa que la velocidad a la que progresa un proceso está limitada por la cantidad de memoria disponible y la velocidad de acceso a esa memoria. Es probable que una tarea que procesa grandes cantidades de datos en la memoria, por ejemplo, multiplicar matrices grandes, sea Memory Bound.

Límite de caché significa la velocidad a la que el progreso de un proceso está limitado por la cantidad y la velocidad del caché disponible. Una tarea que simplemente procesa más datos de los que caben en el caché estará vinculada al caché.

I / O Bound sería más lento que Memory Bound sería más lento que Cache Bound sería más lento que CPU Bound.

La solución para estar vinculado a E / S no es necesariamente obtener más memoria. En algunas situaciones, el algoritmo de acceso podría diseñarse en torno a las limitaciones de E / S, memoria o caché. Ver caché algoritmos ajenos .

Sanjaya R
fuente
73

Multihilo

En esta respuesta, investigaré un caso de uso importante para distinguir entre el trabajo acotado de CPU frente a IO: al escribir código multiproceso.

Ejemplo de enlace de E / S de RAM: suma de vectores

Considere un programa que suma todos los valores de un solo vector:

#define SIZE 1000000000
unsigned int is[SIZE];
unsigned int sum = 0;
size_t i = 0;
for (i = 0; i < SIZE; i++)
    /* Each one of those requires a RAM access! */
    sum += is[i]

Paralelizar eso al dividir la matriz equitativamente para cada uno de sus núcleos es de utilidad limitada en los escritorios modernos comunes.

Por ejemplo, en mi Ubuntu 19.04, laptop Lenovo ThinkPad P51 con CPU: CPU Intel Core i7-7820HQ (4 núcleos / 8 hilos), RAM: 2x Samsung M471A2K43BB1-CRC (2x 16GiB) Obtengo resultados como este:

ingrese la descripción de la imagen aquí

Trazar datos .

Sin embargo, tenga en cuenta que hay mucha variación entre la ejecución. Pero no puedo aumentar el tamaño de la matriz mucho más ya que ya estoy en 8GiB, y no estoy de humor para estadísticas en varias ejecuciones hoy. Sin embargo, esto parecía una carrera típica después de hacer muchas carreras manuales.

Código de referencia:

No conozco suficiente arquitectura de computadora para explicar completamente la forma de la curva, pero una cosa está clara: ¡el cálculo no se vuelve 8 veces más rápido como se esperaba ingenuamente debido a que uso todos mis 8 hilos! Por alguna razón, 2 y 3 subprocesos fueron óptimos, y agregar más solo hace que las cosas sean mucho más lentas.

Compare esto con el trabajo vinculado a la CPU, que en realidad se vuelve 8 veces más rápido: ¿qué significan 'real', 'user' y 'sys' en la salida del tiempo (1)?

La razón es que todos los procesadores comparten un solo bus de memoria que se vincula a la RAM:

CPU 1   --\    Bus    +-----+
CPU 2   ---\__________| RAM |
...     ---/          +-----+
CPU N   --/

entonces el bus de memoria se convierte rápidamente en el cuello de botella, no en la CPU.

Esto sucede porque agregar dos números toma un solo ciclo de CPU, las lecturas de memoria toman alrededor de 100 ciclos de CPU en el hardware de 2016.

Por lo tanto, el trabajo de CPU realizado por byte de datos de entrada es demasiado pequeño, y llamamos a esto un proceso vinculado a IO.

La única forma de acelerar aún más ese cálculo sería acelerar los accesos a la memoria individual con un nuevo hardware de memoria, por ejemplo, memoria multicanal .

Actualizar a un reloj de CPU más rápido, por ejemplo, no sería muy útil.

Otros ejemplos

Cómo saber si estás vinculado a la CPU o IO

IO sin RAM enlazado como disco, red:, ps auxluego theck if CPU% / 100 < n threads. En caso afirmativo, está sujeto a E / S, por ejemplo, los bloqueos readsolo esperan datos y el planificador se salta ese proceso. Luego use otras herramientas como sudo iotoppara decidir qué IO es exactamente el problema.

O, si la ejecución es rápida y parametriza el número de subprocesos, puede verlo fácilmente a partir de timeque el rendimiento mejora a medida que aumenta el número de subprocesos para el trabajo vinculado a la CPU: ¿Qué significan 'real', 'usuario' y 'sys' en la salida del tiempo (1)?

Enlace RAM-IO: más difícil de determinar, ya que el tiempo de espera RAM se incluye en las CPU%mediciones, consulte también:

Algunas opciones:

GPU

Las GPU tienen un cuello de botella de E / S cuando transfiere los datos de entrada desde la RAM legible por CPU normal a la GPU.

Por lo tanto, las GPU solo pueden ser mejores que las CPU para las aplicaciones vinculadas a la CPU.

Sin embargo, una vez que los datos se transfieren a la GPU, pueden operar en esos bytes más rápido que la CPU, porque la GPU:

  • tiene más localización de datos que la mayoría de los sistemas de CPU, por lo que se puede acceder a los datos más rápido para algunos núcleos que para otros

  • explota el paralelismo de datos y sacrifica la latencia simplemente saltando cualquier dato que no esté listo para ser operado de inmediato.

    Dado que la GPU tiene que operar con datos de entrada paralelos grandes, es mejor simplemente pasar a los siguientes datos que podrían estar disponibles en lugar de esperar a que los datos actuales estén disponibles y bloquear todas las demás operaciones como lo hace la CPU en su mayoría

Por lo tanto, la GPU puede ser más rápida que una CPU si su aplicación:

  • puede estar muy paralelo: se pueden tratar diferentes fragmentos de datos por separado al mismo tiempo
  • requiere un número suficientemente grande de operaciones por byte de entrada (a diferencia de, por ejemplo, la adición de vectores que solo hace una adición por byte)
  • hay una gran cantidad de bytes de entrada

Estas opciones de diseño originalmente apuntaban a la aplicación de renderizado 3D, cuyos pasos principales son los que se muestran en ¿Qué son los sombreadores en OpenGL y para qué los necesitamos?

  • sombreador de vértices: multiplicar un grupo de vectores 1x4 por una matriz 4x4
  • sombreador de fragmentos: calcule el color de cada píxel de un triángulo en función de su posición relativa dentro del triángulo

y así concluimos que esas aplicaciones están vinculadas a la CPU.

Con el advenimiento de GPGPU programable, podemos observar varias aplicaciones de GPGPU que sirven como ejemplos de operaciones vinculadas a la CPU:

Ver también:

CPython Global Intepreter Lock (GIL)

Como un caso de estudio rápido, quiero señalar el Bloqueo de intérprete global de Python (GIL): ¿Qué es el bloqueo de intérprete global (GIL) en CPython?

Este detalle de implementación de CPython evita que varios subprocesos de Python utilicen eficientemente el trabajo vinculado a la CPU. Los documentos de CPython dicen:

Detalle de implementación de CPython: en CPython, debido al Bloqueo global del intérprete, solo un subproceso puede ejecutar código Python a la vez (aunque ciertas bibliotecas orientadas al rendimiento pueden superar esta limitación). Si desea que su aplicación haga un mejor uso de los recursos computacionales de las máquinas multinúcleo, se recomienda usar multiprocessingo concurrent.futures.ProcessPoolExecutor. Sin embargo, el subproceso sigue siendo un modelo apropiado si desea ejecutar varias tareas vinculadas de E / S simultáneamente.

Por lo tanto, aquí tenemos un ejemplo en el que el contenido vinculado a la CPU no es adecuado y el enlace de E / S sí.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
1
Hombre, ¿cómo logras ser tan competente y dar una escritura como esta?
Mikayil Abdullayev
1
@MikayilAbdullayev gracias! Tiendo a responder solo "preguntas importantes" y tiendo a volver a ellas una y otra vez a medida que aprendo nuevas cosas relevantes.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
31

El límite de CPU significa que la CPU o la unidad de procesamiento central bloquea el programa, mientras que el límite de E / S significa que el programa tiene un cuello de botella por E / S o entrada / salida, como leer o escribir en el disco, la red, etc.

En general, cuando se optimizan los programas informáticos, se intenta buscar el cuello de botella y eliminarlo. Saber que su programa está vinculado a la CPU ayuda, por lo que uno no optimiza innecesariamente otra cosa.

[Y por "cuello de botella", me refiero a lo que hace que su programa vaya más lento de lo que hubiera sido de otra manera].

Chris W. Rea
fuente
22

Otra forma de expresar la misma idea:

  • Si acelerar la CPU no acelera su programa, puede estar vinculado a E / S.

  • Si acelerar la E / S (por ejemplo, usar un disco más rápido) no ayuda, su programa puede estar vinculado a la CPU.

(Solía ​​"puede ser" porque necesita tener en cuenta otros recursos. La memoria es un ejemplo).

gimel
fuente
11

Cuando su programa está esperando E / S (es decir, una lectura / escritura de disco o lectura / escritura de red, etc.), la CPU es libre de realizar otras tareas incluso si su programa está detenido. La velocidad de su programa dependerá principalmente de qué tan rápido puede suceder IO, y si desea acelerarlo, deberá acelerar la E / S.

Si su programa ejecuta muchas instrucciones de programa y no espera E / S, se dice que está vinculado a la CPU. Acelerar la CPU hará que el programa se ejecute más rápido.

En cualquier caso, la clave para acelerar el programa podría no ser acelerar el hardware, sino optimizar el programa para reducir la cantidad de E / S o CPU que necesita, o hacer que haga E / S mientras también hace un uso intensivo de la CPU cosas.

Paul Tomblin
fuente
6

El límite de E / S se refiere a una condición en la que el tiempo que lleva completar un cálculo está determinado principalmente por el período de espera para que se completen las operaciones de entrada / salida.

Esto es lo opuesto a una tarea vinculada a la CPU. Esta circunstancia surge cuando la velocidad a la que se solicitan los datos es más lenta que la velocidad que se consume o, en otras palabras, se pasa más tiempo solicitando datos que procesándolos.

FellyTone84
fuente
6

Procesos vinculados a IO: pase más tiempo haciendo IO que los cálculos, tenga muchas ráfagas cortas de CPU. Procesos vinculados a la CPU: pase más tiempo haciendo cálculos, pocas ráfagas de CPU muy largas

dua
fuente
2

Mira lo que dice Microsoft.

El núcleo de la programación asíncrona son los objetos Tarea y Tarea, que modelan operaciones asincrónicas. Son compatibles con la asíncrona y esperan palabras clave. El modelo es bastante simple en la mayoría de los casos:

  • Para el código enlazado de E / S, espera una operación que devuelva una Tarea o Tarea dentro de un método asíncrono.

  • Para el código enlazado a la CPU, espera una operación que se inicia en un subproceso en segundo plano con el método Task.Run.

La palabra clave aguardar es donde sucede la magia. Otorga el control a la persona que llama del método que se realizó en espera y, en última instancia, permite que una IU responda o que un servicio sea elástico.

Ejemplo de enlace de E / S: descarga de datos de un servicio web

private readonly HttpClient _httpClient = new HttpClient();

downloadButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI as the request
    // from the web service is happening.
    //
    // The UI thread is now free to perform other work.
    var stringData = await _httpClient.GetStringAsync(URL);
    DoSomethingWithData(stringData);
};

Ejemplo vinculado a la CPU: realizar un cálculo para un juego

private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}

calculateButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI while CalculateDamageDone()
    // performs its work.  The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());
    DisplayDamage(damageResult);
};

Los ejemplos anteriores muestran cómo puede usar async y esperar trabajos vinculados a E / S y CPU. Es clave que pueda identificar cuando un trabajo que necesita hacer está vinculado a E / S o CPU, porque puede afectar en gran medida el rendimiento de su código y podría conducir a un mal uso de ciertas construcciones.

Aquí hay dos preguntas que debe hacer antes de escribir cualquier código:

¿Su código estará "esperando" algo, como los datos de una base de datos?

  • Si su respuesta es "sí", entonces su trabajo está vinculado a E / S.

¿Su código realizará un cálculo muy costoso?

  • Si respondió "sí", entonces su trabajo está vinculado a la CPU.

Si el trabajo que tiene está vinculado a E / S, use asíncrono y espere sin Task.Run . No debe usar la Biblioteca de tareas paralelas. La razón de esto se describe en el artículo Async in Depth .

Si el trabajo que tiene está vinculado a la CPU y le preocupa la capacidad de respuesta, use async y espere, pero genere el trabajo en otro hilo con Task.Run. Si el trabajo es apropiado para la concurrencia y el paralelismo, también debe considerar usar la Biblioteca de tareas paralelas .

Taşyürek Gökşah
fuente
2

Una aplicación está vinculada a la CPU cuando el rendimiento aritmético / lógico / de punto flotante (A / L / FP) durante la ejecución está mayormente cerca del rendimiento máximo teórico del procesador (datos proporcionados por el fabricante y determinados por las características del procesador: número de núcleos, frecuencia, registros, ALU, FPU, etc.).

Es muy difícil lograr el rendimiento máximo en aplicaciones del mundo real, por no decir imposible. La mayoría de las aplicaciones acceden a la memoria en diferentes partes de la ejecución y el procesador no realiza operaciones A / L / FP durante varios ciclos. Esto se llama limitación de von Neumann debido a la distancia que existe entre la memoria y el procesador.

Si desea estar cerca del rendimiento máximo de la CPU, una estrategia podría ser tratar de reutilizar la mayoría de los datos en la memoria caché para evitar requerir datos de la memoria principal. Un algoritmo que explota esta característica es la multiplicación matriz-matriz (si ambas matrices pueden almacenarse en la memoria caché). Esto sucede porque si las matrices son de tamaño, n x nentonces debe realizar 2 n^3operaciones utilizando solo 2 n^2números de datos FP. Por otro lado, la adición de matriz, por ejemplo, es una aplicación menos unida a la CPU o más unida a la memoria que la multiplicación de la matriz, ya que solo requiere n^2FLOP con los mismos datos.

En la siguiente figura se muestran los FLOP obtenidos con algoritmos ingenuos para la suma de matrices y la multiplicación de matrices en un Intel i5-9300H:

Comparación de FLOP entre la suma de matrices y la multiplicación de matrices

Tenga en cuenta que, como se esperaba, el rendimiento de la multiplicación de la matriz es mayor que la suma de la matriz. Estos resultados se pueden reproducir ejecutando test/gemmy test/matadddisponibles en este repositorio .

Sugiero también ver el video dado por J. Dongarra sobre este efecto.

GG1991
fuente
1

Proceso vinculado de E / S: - Si la mayor parte de la vida útil de un proceso se pasa en estado de E / S, entonces el proceso es un proceso vinculado a E / S. Ejemplo: -calculador, explorador de Internet

Proceso vinculado a la CPU: - Si la mayor parte de la vida del proceso se gasta en la CPU, entonces es un proceso vinculado a la CPU.

K.Abhishek
fuente
8
¿Cómo sería la calculadora un proceso vinculado a IO? Seguramente estaría vinculado a la CPU. Si su calculadora pasa la mayor parte del tiempo bloqueada accediendo a la red o al disco, le sugiero que algo esté mal.
rickerbh
66
Pensé que el ejemplo de la calculadora era claro: la mayoría de las veces espera que el usuario presione un botón, por lo tanto, espera E / S.
psp
1
@psp tiene razón, pero el ejemplo de la calculadora todavía es superficialmente difícil de ver como io enlazado debido a su nombre, "calculadora". Eso implica que el propósito principal del programa es realizar cálculos de CPU largos, pero si observa su calculadora en su escritorio, es muy simple y solo realiza cálculos que tardan nanosegundos en completarse. Por lo tanto, la mayoría de las veces está esperando la entrada del usuario, que es IO.
Calicoder