Reunir/dispersar es un tipo de direccionamiento de memoria que al mismo tiempo recopila (reúne) o almacena (dispersa) datos en múltiples índices arbitrarios. Ejemplos de su uso incluyen operaciones dispersas de álgebra lineal , [1] algoritmos de clasificación, transformadas rápidas de Fourier , [2] y algunos problemas de teoría de grafos computacionales. [3] Es el equivalente vectorial del direccionamiento indirecto de registros , en el que la recopilación implica lecturas indexadas y las escrituras indexadas y dispersas. Los procesadores vectoriales (y algunas unidades SIMD en las CPU ) tienen soporte de hardware para operaciones de recopilación y dispersión, al igual que muchos sistemas de entrada/salida , lo que permite transferir grandes conjuntos de datos a la memoria principal más rápidamente.
El concepto es algo similar a la E/S vectorial , que a veces también se denomina E/S de dispersión. Este sistema se diferencia en que se utiliza para mapear múltiples fuentes de datos de estructuras contiguas en una única secuencia para lectura o escritura. Un ejemplo común es escribir una serie de cadenas , que en la mayoría de los lenguajes de programación se almacenarían en ubicaciones de memoria separadas.
Un vector escasamente poblado que contiene elementos no vacíos se puede representar mediante dos vectores de longitud densamente poblados ; que contiene los elementos no vacíos de y proporciona el índice donde se encuentra el elemento. La reunión de en , denotada , asigna haber ya sido calculada. [4] Suponiendo que no haya alias de puntero entre x[], y[],idx[], una implementación en C es
para ( i = 0 ; i < N ; ++ i ) x [ i ] = y [ idx [ i ]];
La dispersión escasa que se indica es la operación inversa. Copia los valores de en las ubicaciones correspondientes en el vector escasamente poblado , es decir .
para ( i = 0 ; i < N ; ++ i ) y [ idx [ i ]] = x [ i ];
Las unidades de dispersión/reunión también formaban parte de la mayoría de las computadoras vectoriales, en particular la Cray-1 . En este caso, el propósito era almacenar valores de manera eficiente en el recurso limitado de los registros vectoriales. Por ejemplo, el Cray-1 tenía ocho registros vectoriales de 64 palabras, por lo que los datos que contenían valores que no tenían ningún efecto en el resultado, como ceros en una suma, consumían un espacio valioso que sería mejor utilizado. Al reunir valores distintos de cero en los registros y dispersar los resultados, los registros podrían usarse de manera mucho más eficiente, lo que conduciría a un mayor rendimiento. Estas máquinas generalmente implementaban dos modelos de acceso, dispersión/reunión y "zancada", este último diseñado para cargar rápidamente datos contiguos. [5] Este diseño básico fue ampliamente copiado en diseños de supercomputadoras posteriores , especialmente en la variedad de modelos de Japón.
A medida que el diseño de los microprocesadores mejoró durante la década de 1990, las CPU básicas comenzaron a agregar unidades de procesamiento vectorial. Al principio, estos tendían a ser simples, a veces superponiéndose a los registros de propósito general de la CPU, pero con el tiempo evolucionaron hasta convertirse en sistemas cada vez más potentes que igualaron y luego superaron las unidades de las supercomputadoras de alta gama. En ese momento, se habían agregado instrucciones de dispersión/reunión a muchos de estos diseños.
Las CPU x86-64 que admiten el conjunto de instrucciones AVX2 pueden recopilar elementos de 32 y 64 bits con compensaciones de memoria desde una dirección base. Un segundo registro determina si el elemento en particular está cargado y se suprimen los fallos que se producen por accesos no válidos a la memoria por parte de elementos enmascarados. [6] : 503–4 El conjunto de instrucciones AVX-512 también contiene operaciones de dispersión (potencialmente enmascaradas). [6] : 539 [7] La extensión vectorial escalable del conjunto de instrucciones ARM incluye operaciones de recopilación y dispersión en elementos de 8, 16, 32 y 64 bits. [8] [9] InfiniBand tiene soporte de hardware para recopilación/dispersión. [10]
Sin recopilación/dispersión a nivel de instrucción, es posible que sea necesario ajustar las implementaciones eficientes para lograr un rendimiento óptimo, por ejemplo, con captación previa ; bibliotecas como OpenMPI pueden proporcionar tales primitivas. [2] [8]