La afinidad de procesador , o afinidad de procesador , o afinidad de caché , es una tecnología que garantiza que un proceso o subproceso se ancle y se desconecte de un núcleo de CPU, CPU o conjunto de procesadores específicos, de modo que el proceso o subproceso solo se ejecute en el núcleo especificado. , procesador o procesadores, y no en ningún procesador de un sistema multiprocesador. La afinidad del procesador se puede considerar como una modificación del algoritmo central de programación de la cola de tareas en un sistema operativo multiprocesador. Cada elemento de la cola de tareas tiene una etiqueta asociada , que especifica sus procesadores "relacionados".
Cuando se asignan recursos, cada tarea se distribuye preferiblemente para su ejecución en uno de los procesadores "relacionados". La afinidad del procesador aprovecha el hecho de que los datos y la configuración de un proceso que se ejecutaba anteriormente en un procesador determinado pueden estar disponibles más rápidamente para ese procesador que para otro. Esto puede suceder, por ejemplo, debido al almacenamiento en caché de datos de proceso en la memoria caché del procesador, así como en otras situaciones. La programación de un proceso de este tipo para que se ejecute en el mismo procesador mejora su rendimiento al reducir los eventos que degradan el rendimiento, como las pérdidas de caché.
Además, en algunos sistemas, cada uno de los procesadores puede tener un acceso más rápido a una región de RAM cercana. Al mismo tiempo, resulta racional mantener un vínculo constante del proceso con el procesador, cuyo acceso a la memoria RAM, donde se encuentran los datos de este proceso, es más rápido.
Un ejemplo práctico de afinidad de procesador es ejecutar varias instancias de una aplicación sin subprocesos, como algún software de representación de gráficos.
La implementación del algoritmo de programación de tareas, que brinda la posibilidad de vincularse al procesador, se implementa teniendo en cuenta las características de los procesadores específicos y la construcción de un sistema multiprocesador, que será controlado por dicho algoritmo. Algunas implementaciones, bajo ciertas circunstancias, permitirán que una tarea se transfiera a otro procesador, superando el enlace. Esto se hace en aquellos casos en los que, desde el punto de vista del programador, dicho cambio conducirá a un aumento en la eficiencia de la ejecución de la tarea. Por ejemplo, cuando dos tareas que hacen un uso intensivo del procesador (A y B) están vinculadas al mismo procesador y el otro procesador no está en uso, muchos programadores cambiarán la tarea B al segundo procesador para aprovechar al máximo el procesador disponible para el sistema. . La vinculación de la tarea B con el nuevo procesador en ese momento la establecerá el propio programador.
La afinidad del procesador puede reducir efectivamente los problemas con los datos que ingresan al sistema y/o al caché del procesador. Pero no proporciona una solución a los problemas de equilibrio de carga [1] . La afinidad de CPU es más compleja en sistemas con una arquitectura heterogénea, lo que requiere una lógica de programación más sofisticada que en sistemas completamente homogéneos. Por ejemplo, un sistema con dos CPU de doble núcleo , cada una de las cuales admite la tecnología Hyper-Threading , presenta un problema para el algoritmo del programador, que asume la afinidad de la CPU. Si el sistema tiene una cantidad aún mayor de procesadores y, por ejemplo, no es completamente simétrico en sí mismo, entonces la complejidad del problema de la programación eficiente de tareas aumenta aún más.
Para el ejemplo anterior con dos procesadores de doble núcleo con hiperprocesos, el planificador debe implementar un sistema de enlace de dos niveles. En términos de eficiencia de caché, el trabajo dentro del mismo núcleo en diferentes subprocesos es equivalente, y el programador tiene derecho a mover libremente una tarea de un subproceso a otro. El nivel de "proximidad" de diferentes núcleos dentro de un procesador es menor, ya que comparten parcialmente un caché de procesador común, el nivel de "proximidad" de diferentes procesadores es aún menor. Debido a que también se comparten otros recursos, la afinidad de la CPU por sí sola no se puede utilizar como base para la programación de tareas. Por ejemplo, si un proceso se ejecutó recientemente en una CPU virtual con hiperprocesamiento en algún núcleo y esa CPU virtual está actualmente ocupada, pero una segunda CPU virtual del mismo núcleo está inactiva, la afinidad de la CPU basada en la eficiencia de la memoria caché implica que el proceso debe transferirse a un segundo procesador virtual (no en ejecución) del mismo núcleo. Sin embargo, las dos CPU virtuales compiten por casi todos los recursos informáticos, la memoria caché y los recursos de memoria. En esta situación, por regla general, sería más eficiente asignar el proceso a otro núcleo o CPU, si hay inactivos entre ellos. Esto puede resultar en un impacto único en el rendimiento debido al hecho de que el proceso reubicado tendrá que volver a llenar el caché con sus datos. Pero el rendimiento general puede ser mejor porque los dos procesos no tienen que competir por los recursos dentro de la misma CPU.
Para lograr la máxima eficiencia, el programador de tareas debe tener en cuenta todos estos aspectos. Los sistemas con niveles aún mayores de asimetría ( NUMA , clústeres, etc.) requieren una complejidad aún mayor del programador.
En Linux, la afinidad del procesador de un proceso se puede averiguar o configurar mediante la utilidad taskset [2] . Mediante programación, se pueden realizar las mismas acciones mediante las llamadas al sistema sched_getaffinity y sched_setaffinity [3] . La afinidad del subproceso se puede establecer o cambiar mediante una de las funciones de la biblioteca: pthread_setaffinity_np [4] o pthread_attr_setaffinity_np [5] .
En los sistemas SGI , un proceso podría asociarse con un conjunto de procesadores utilizando la utilidad dplace [6] .
En DragonFly BSD 1.9 (2007) y posteriores, la llamada al sistema usched_set [7] [8] se puede usar para controlar la afinidad de la CPU . En NetBSD 5.0, FreeBSD 7.2, DragonFly BSD 4.7 y posteriores, se pueden usar las llamadas al sistema pthread_setaffinity_np y pthread_getaffinity_np [9] . En NetBSD , la utilidad [10] psrset establece la afinidad de un subproceso con un conjunto específico de CPU. FreeBSD usa la utilidad cpuset [11] para crear conjuntos de procesadores y asignar procesos a esos conjuntos. En DragonFly BSD 3.1 (2012) y versiones posteriores, la utilidad usched se puede usar para asignar procesos a un conjunto específico de procesadores [12] .
En Windows NT y versiones posteriores, las afinidades de subprocesos y procesos se pueden configurar por separado mediante las llamadas API SetThreadAffinityMask [13] y SetProcessAffinityMask [14] o mediante la interfaz del Administrador de tareas (solo para procesos).
macOS proporciona una API de vinculación [15] que brinda sugerencias al kernel del sistema operativo sobre cómo programar subprocesos de acuerdo con los conjuntos de vinculación.
En Solaris , puede controlar la vinculación de procesos y procesos ligeros al procesador utilizando la utilidad pbind [16] . También se proporciona la llamada al sistema Processor_bind [17] . Las llamadas de interfaz de nivel superior también están disponibles, a saber, pset_bind [18] o lgrp_affinity_get [19] , utilizando los conceptos de conjunto de procesador y grupo de localidad, respectivamente.
En AIX , puede gestionar los enlaces de procesos mediante la utilidad bindprocessor [20] [21] y la llamada al sistema bindprocessor [20 ] [ 22 ] .
z/OS implementa quizás el programador de tareas más sofisticado que se usa en la actualidad. Proporciona una redistribución dinámicamente cambiante de recursos de hardware entre procesos, incluidos aquellos basados en la vinculación de procesos a núcleos de procesadores individuales, procesadores y sus grupos [23]
La biblioteca estándar para el lenguaje de programación paralelo Julia incluye soporte experimental para la afinidad de proceso a procesador [24] .