En informática , un procesador vectorial o un procesador de matriz es una unidad central de procesamiento (CPU) que implementa un conjunto de instrucciones que contiene instrucciones que operan en matrices de datos unidimensionales llamadas vectores . Compare esto con los procesadores escalares , cuyas instrucciones operan en elementos de datos únicos. Los procesadores vectoriales pueden mejorar enormemente el rendimiento en determinadas cargas de trabajo, en particular la simulación numérica y tareas similares. Las máquinas vectoriales aparecieron a principios de la década de 1970 y dominaron el diseño de supercomputadoras desde la década de 1970 hasta la de 1990, en particular las diversasPlataformas Cray . La rápida caída en la relación precio-rendimiento de los diseños de microprocesadores convencionales llevó a la desaparición de la supercomputadora vectorial a fines de la década de 1990.
A partir de 2016, la [actualizar]mayoría de las CPU básicas implementan arquitecturas que cuentan con instrucciones SIMD de longitud fija . Estos se denominan de manera confusa como una forma de procesamiento vectorial porque operan en conjuntos de datos múltiples (vectorizados, de longitud explícita) y, a menudo, toman características parciales de ISA vectoriales históricas. Esto no los califica como procesadores vectoriales. Los ejemplos comunes que usan SIMD con características inspiradas en los procesadores Vector incluyen las instrucciones MMX , SSE y AVX de Intel x86 , 3DNow de AMD ! extensiones, de Sparc VIS extensión, PowerPC 's AltiVec y MIPS' MSA . Las técnicas de procesamiento de vectores también operan en hardware de consola de videojuegos y en aceleradores de gráficos . En 2000, IBM , Toshiba y Sony colaboraron para crear el procesador Cell , que también es SIMD , no vectores verdaderos (estilo Cray).
Otros diseños de CPU incluyen algunas instrucciones múltiples para el procesamiento de vector en varios conjuntos de datos (vectorizados), típicamente conocidos como MIMD ( M ultiple I NSTRUCCIÓN, M ultiple D ata) y realizadas con VLIW ( V ery L ong I NSTRUCCIÓN W ord). El procesador vectorial Fujitsu FR-V VLIW combina ambas tecnologías.
Los ISA vectoriales "reales" (verdaderos) se pueden distinguir por tener una o ambas de dos características:
- una instrucción Vector Length (VL) (como setvl en RISCV RVV [1] ) y / o
- máscaras de predicado de nivel de elemento individuales integrales, como ahora está disponible en ARM SVE2. [2] y AVX-512
Un ejemplo extremo y raro de un procesador de matriz fue el Aspex Microelectronics ASP [3] , que se clasificó a sí mismo como "SIMD ancho masivo" pero tenía ALU a nivel de bit y predicación a nivel de bit, por lo que definitivamente podría considerarse una matriz (vector) Procesador. [4] [5] El Linedancer, lanzado en 2010, contenía 4096 ALU SIMD Predicadas de 2 bits, cada una con su propia Memoria Direccionable por Contenido , y era capaz de 800 mil millones de instrucciones por segundo. [6]
Historia
Trabajo temprano
El desarrollo del procesamiento de vectores comenzó a principios de la década de 1960 en Westinghouse en su proyecto "Solomon". El objetivo de Solomon era aumentar drásticamente el rendimiento matemático mediante el uso de una gran cantidad de coprocesadores matemáticos simples bajo el control de una sola CPU maestra . La CPU alimentó una única instrucción común a todas las unidades lógicas aritméticas (ALU), una por ciclo, pero con un punto de datos diferente para que cada una trabajara. Esto permitió a la máquina Solomon aplicar un solo algoritmo a un gran conjunto de datos , alimentado en forma de matriz.
En 1962, Westinghouse canceló el proyecto, pero el esfuerzo se reinició en la Universidad de Illinois como ILLIAC IV . Su versión del diseño originalmente requería una máquina de 1 GFLOPS con 256 ALU, pero, cuando finalmente se entregó en 1972, tenía solo 64 ALU y podía alcanzar solo de 100 a 150 MFLOPS. Sin embargo, demostró que el concepto básico era sólido y, cuando se usaba en aplicaciones con uso intensivo de datos, como la dinámica de fluidos computacional , ILLIAC era la máquina más rápida del mundo. El enfoque ILLIAC de usar ALU separadas para cada elemento de datos no es común a diseños posteriores y, a menudo, se lo denomina en una categoría separada, computación masivamente paralela .
Kartsev presentó y desarrolló una computadora para operaciones con funciones en 1967. [7]
Supercomputadoras
La primera implementación exitosa del procesamiento de vectores ocurrió en 1966, cuando se introdujeron tanto Control Data Corporation STAR-100 como Texas Instruments Advanced Scientific Computer (ASC).
La ALU ASC básica (es decir, "una tubería") utilizaba una arquitectura de tubería que admitía cálculos tanto escalares como vectoriales, con un rendimiento máximo que alcanzaba aproximadamente 20 MFLOPS, que se lograba fácilmente al procesar vectores largos. Las configuraciones de ALU ampliadas admitían "dos tubos" o "cuatro tubos" con una ganancia de rendimiento correspondiente de 2X o 4X. El ancho de banda de la memoria era suficiente para admitir estos modos expandidos.
Por lo demás, el STAR era más lento que las propias supercomputadoras de los CDC , como el CDC 7600 , pero en las tareas relacionadas con los datos podían mantenerse al día siendo mucho más pequeño y menos costoso. Sin embargo, la máquina también tomó un tiempo considerable para decodificar las instrucciones vectoriales y prepararse para ejecutar el proceso, por lo que requirió conjuntos de datos muy específicos para trabajar antes de que realmente acelerara algo.
La técnica del vector fue explotada por primera vez en 1976 por el famoso Cray-1 . En lugar de dejar los datos en la memoria como STAR y ASC, el diseño de Cray tenía ocho registros vectoriales , que contenían sesenta y cuatro palabras de 64 bits cada uno. Las instrucciones vectoriales se aplicaron entre registros, lo que es mucho más rápido que hablar con la memoria principal. Mientras que STAR aplicaría una sola operación a través de un vector largo en la memoria y luego pasaría a la siguiente operación, el diseño de Cray cargaría una sección más pequeña del vector en los registros y luego aplicaría tantas operaciones como pudiera a esos datos, por lo tanto evitando muchas de las operaciones de acceso a la memoria mucho más lentas.
El diseño de Cray utilizó el paralelismo de la tubería para implementar instrucciones vectoriales en lugar de múltiples ALU. Además, el diseño tenía canales completamente separados para diferentes instrucciones, por ejemplo, la suma / resta se implementó en un hardware diferente al de la multiplicación. Esto permitió que un lote de instrucciones vectoriales se canalizara a cada una de las subunidades de ALU, una técnica que llamaron encadenamiento de vectores . El Cray-1 normalmente tenía un rendimiento de aproximadamente 80 MFLOPS, pero con hasta tres cadenas en funcionamiento, podía alcanzar un máximo de 240 MFLOPS y promediar alrededor de 150, mucho más rápido que cualquier máquina de la época.
Siguieron otros ejemplos. Control Data Corporation intentó volver a ingresar al mercado de alta gama nuevamente con su máquina ETA-10 , pero se vendió mal y tomaron eso como una oportunidad para dejar el campo de la supercomputación por completo. A principios y mediados de la década de 1980, las empresas japonesas ( Fujitsu , Hitachi y Nippon Electric Corporation (NEC) introdujeron máquinas vectoriales basadas en registros similares a la Cray-1, que por lo general eran un poco más rápidas y mucho más pequeñas. Sistemas de punto flotante con base en Oregón (FPS) construyó procesadores de matriz complementarios para miniordenadores , y luego construyó sus propias minisupercomputadoras .
En todo momento, Cray continuó siendo el líder en rendimiento, superando continuamente a la competencia con una serie de máquinas que llevaron al Cray-2 , Cray X-MP y Cray Y-MP . Desde entonces, el mercado de las supercomputadoras se ha centrado mucho más en el procesamiento masivamente paralelo que en mejores implementaciones de procesadores vectoriales. Sin embargo, reconociendo los beneficios del procesamiento de vectores, IBM desarrolló la Arquitectura de Vector Virtual para su uso en supercomputadoras que acoplan varios procesadores escalares para actuar como un procesador de vectores.
Aunque las supercomputadoras vectoriales que se asemejan al Cray-1 son menos populares en estos días, NEC ha continuado fabricando este tipo de computadora hasta el día de hoy con su serie de computadoras SX . Más recientemente, el SX-Aurora TSUBASA coloca el procesador y 24 o 48 gigabytes de memoria en un módulo HBM 2 dentro de una tarjeta que se asemeja físicamente a un coprocesador gráfico, pero en lugar de servir como coprocesador, es la computadora principal con el ordenador compatible con PC en el que está conectado para funciones de soporte.
GPU
Las unidades de procesamiento de gráficos ( GPU ) modernas incluyen una serie de canales de sombreado que pueden ser impulsados por núcleos de cómputo , que pueden considerarse procesadores vectoriales (utilizando una estrategia similar para ocultar latencias de memoria).
Descripción
En términos generales, las CPU pueden manipular uno o dos datos a la vez. Por ejemplo, la mayoría de las CPU tienen una instrucción que básicamente dice "agregue A a B y ponga el resultado en C". Los datos para A, B y C podrían estar, al menos en teoría, codificados directamente en la instrucción. Sin embargo, en una implementación eficiente, las cosas rara vez son tan simples. Los datos rara vez se envían sin procesar y, en cambio, se "apuntan" al pasar una dirección a una ubicación de memoria que contiene los datos. Decodificar esta dirección y sacar los datos de la memoria lleva algún tiempo, durante el cual la CPU tradicionalmente se queda inactiva esperando que aparezcan los datos solicitados. A medida que aumentaron las velocidades de la CPU, esta latencia de la memoria se ha convertido históricamente en un gran impedimento para el rendimiento; ver Muro de la memoria .
Para reducir la cantidad de tiempo consumido por estos pasos, la mayoría de las CPU modernas utilizan una técnica conocida como canalización de instrucciones en la que las instrucciones pasan a través de varias subunidades sucesivamente. La primera subunidad lee la dirección y la decodifica, la siguiente "busca" los valores en esas direcciones y la siguiente hace los cálculos por sí misma. Con la canalización, el "truco" es comenzar a decodificar la siguiente instrucción incluso antes de que la primera haya salido de la CPU, al estilo de una línea de montaje , de modo que el decodificador de direcciones esté en uso constante. Cualquier instrucción en particular tarda la misma cantidad de tiempo en completarse, un tiempo conocido como latencia , pero la CPU puede procesar un lote completo de operaciones, de manera superpuesta, mucho más rápido y de manera más eficiente que si lo hiciera una a la vez.
Los procesadores vectoriales llevan este concepto un paso más allá. En lugar de canalizar solo las instrucciones, también canalizan los datos en sí. El procesador recibe instrucciones que dicen no solo agregar A a B, sino agregar todos los números "de aquí para aquí" a todos los números "de allí para allá". En lugar de tener que decodificar instrucciones constantemente y luego buscar los datos necesarios para completarlas, el procesador lee una sola instrucción de la memoria, y está simplemente implícito en la definición de la instrucción en sí misma que la instrucción operará nuevamente en otro elemento de datos. en una dirección un incremento mayor que el anterior. Esto permite un ahorro significativo en el tiempo de decodificación.
Para ilustrar la diferencia que esto puede hacer, considere la simple tarea de sumar dos grupos de 10 números. En un lenguaje de programación normal, uno escribiría un "bucle" que recogiera cada uno de los pares de números por turno y luego los sumara. Para la CPU, esto se vería así:
; Máquina RISC hipotética ; sume 10 números en a a 10 números en b, almacenando los resultados en c ; suponga que a, byc son ubicaciones de memoria en sus respectivos registros mover $ 10 , contar ; cuenta: = 10 bucle: carga r1 , una carga r2 , b suma r3 , r1 , r2 ; r3: = r1 + r2 almacenar r3 , c agregar a , a , $ 4 ; seguir adelante sumar b , b , $ 4 sumar c , c , $ 4 contar dec ; disminuir el recuento de jnez , bucle ; bucle hacia atrás si el recuento aún no es 0 ret
Pero para un procesador de vectores, esta tarea se ve considerablemente diferente:
; supongamos que tenemos registros vectoriales v1-v3 con un tamaño igual o mayor que 10 mover $ 10 , contar ; count = 10 vload v1 , a , count vload v2 , b , count vadd v3 , v1 , v2 vstore v3 , c , count ret
Las ISA vectoriales estilo Cray van un paso más allá y mantienen un registro de "recuento" global, llamado Longitud vectorial (VL):
; nuevamente supongamos que tenemos registros vectoriales v1-v3 con un tamaño mayor o igual a 10 setvli $ 10 # Establecer la longitud del vector VL = 10 vload v1 , a # vector de carga a (10 elementos) vload v2 , b # vector de carga b (10 elementos) vadd v3 , v1 , v2 # 10 agrega vstore v3 , c # 10 almacena en c ret
Hay varios ahorros inherentes a este enfoque. Por un lado, solo se necesitan tres traducciones de direcciones. Dependiendo de la arquitectura, esto puede representar un ahorro significativo en sí mismo. Otro ahorro es buscar y decodificar la instrucción en sí, que debe realizarse solo una vez en lugar de diez. El código en sí también es más pequeño, lo que puede conducir a un uso más eficiente de la memoria. En las ISA de Procesador Vectorial más modernas, se ha introducido "Falla primero" o "Falla primero" (ver más abajo), lo que trae aún más ventajas.
Pero más que eso, un procesador vectorial puede tener múltiples unidades funcionales agregando esos números en paralelo. La verificación de las dependencias entre esos números no es necesaria ya que una instrucción vectorial especifica múltiples operaciones independientes. Esto simplifica la lógica de control requerida y puede mejorar el rendimiento al evitar paradas. Por tanto, las operaciones matemáticas se completaron mucho más rápido en general, siendo el factor limitante el tiempo necesario para recuperar los datos de la memoria.
No todos los problemas pueden atacarse con este tipo de solución. Incluir este tipo de instrucciones necesariamente agrega complejidad al núcleo de la CPU. Esa complejidad normalmente hace que otras instrucciones se ejecuten más lentamente, es decir, cuando no suman muchos números seguidos. Las instrucciones más complejas también aumentan la complejidad de los decodificadores, lo que podría ralentizar la decodificación de las instrucciones más comunes, como la suma normal.
De hecho, los procesadores vectoriales funcionan mejor solo cuando hay grandes cantidades de datos en los que trabajar. Por esta razón, este tipo de CPU se encontraban principalmente en supercomputadoras , ya que las supercomputadoras mismas se encontraban, en general, en lugares como centros de predicción meteorológica y laboratorios de física, donde se "procesan" enormes cantidades de datos.
Instrucciones vectoriales
El ejemplo de pseudocódigo de vector anterior viene con una gran suposición de que la computadora de vector puede procesar más de diez números en un lote. Para un mayor número de números, no es factible que la computadora tenga un registro tan grande. Como resultado, el procesador vectorial obtiene la capacidad de realizar bucles por sí mismo o expone algún tipo de registro de control vectorial (estado) al programador.
Las instrucciones que se repiten automáticamente se encuentran en las primeras computadoras vectoriales como STAR, donde la acción anterior se describiría en una sola instrucción (algo así como vadd c, a, b, $10
). También se encuentran en la arquitectura x86 como REP
prefijo. Sin embargo, solo se pueden realizar cálculos muy simples de manera efectiva en hardware sin un aumento de costos muy grande. Dado que todos los operandos deben estar en la memoria, la latencia causada por el acceso también se vuelve enorme.
El Cray-1 introdujo la idea de utilizar registros de procesador para almacenar datos vectoriales en lotes. De esta manera, se puede hacer mucho más trabajo en cada lote, a costa de requerir que el programador cargue / almacene manualmente datos desde / hacia la memoria para cada lote. Las longitudes de los lotes (Vector Length, VL) se pueden configurar dinámicamente con una instrucción especial.
Las computadoras SIMD modernas afirman mejorar en los primeros Cray mediante el uso directo de múltiples ALU, para un mayor grado de paralelismo en comparación con solo usar la tubería escalar normal. Los procesadores vectoriales modernos combinan ambos, al emitir múltiples datos a múltiples ALU SIMD canalizadas internas, el número emitido es elegido dinámicamente por el programa Vector en tiempo de ejecución. Las máscaras se pueden usar para cargar y almacenar datos de manera selectiva en ubicaciones de memoria, y usar esas mismas máscaras para deshabilitar selectivamente el elemento de procesamiento de las ALU de SIMD. Algunos procesadores SIMD puros son capaces de este tipo de procesamiento selectivo por elemento ( "predicado" ), y son estos los que merecen la nomenclatura de "Procesador vectorial". Los procesadores SIMD sin predicación por elemento ( MMX , SSE , AltiVec ) categóricamente no lo hacen.
Las GPU, que tienen muchas unidades de cálculo pequeñas, utilizan una variante de SIMD llamada Single Instruction Multiple Threads (SIMT). Esto es similar al SIMD moderno, con la excepción de que los "registros vectoriales" son muy anchos y las tuberías tienden a ser largas. La parte de "subprocesamiento" afecta la forma en que se intercambian los datos entre las unidades de cálculo. Además, las GPU y otros procesadores vectoriales externos como el NEC SX-Aurora TSUBASA pueden usar menos unidades vectoriales de las que implica el ancho: en lugar de tener 64 unidades para un registro de 64 números de ancho, el hardware podría hacer un bucle canalizado sobre 16 unidades para un enfoque híbrido.
La diferencia entre un procesador vectorial tradicional y uno SIMD moderno se puede ilustrar con esta variante de la función "DAXPY":
void iaxpy ( size_t n , int una , const int x [], int y []) { para ( size_t i = 0 ; i < n ; i ++ ) y [ i ] = a * x [ i ] + y [ i ]; }
Nuestra versión escalar de esto cargaría uno de cada uno de xey, procesaría un cálculo, almacenaría un resultado:
bucle: load32 r1 , x load32 r2 , y mul32 r1 , a , r1 ; r1: = r1 * a add32 r3 , r1 , r2 ; r3: = r1 + r2 store32 r3 , y addl x , x , $ 4 ; a: = a + 4 adicionales y , y , $ 4 subl n , n , $ 1 ; n: = n - 1 jgz n , bucle ; bucle hacia atrás si n> 0 out: ret
El código tipo STAR sigue siendo conciso, pero ahora necesitamos una ranura adicional de memoria para procesar la información. También se necesita el doble de latencia debido al requisito adicional de acceso a la memoria.
; Suponga que tmp es vmul tmp , a , x , n preasignado ; tmp [i] = a * x [i] vadd y , y , tmp , n ; y [i] = y [i] + tmp [i] ret
Esta moderna máquina SIMD puede realizar la mayor parte de la operación en lotes. El código es mayormente similar a la versión escalar. Suponemos que tanto x como y están correctamente alineados aquí y que n es un múltiplo de 4, ya que de lo contrario se necesitaría algún código de configuración para calcular una máscara o ejecutar una versión escalar. El tiempo necesario sería básicamente el mismo que el de una implementación vectorial de la c = a + b
descrita anteriormente.
vloop: load32x4 v1 , x load32x4 v2 , y mul32x4 v1 , a , v1 ; v1: = v1 * a add32x4 v3 , v1 , v2 ; v3: = v1 + v2 store32x4 v3 , y addl x , x , $ 16 ; a: = a + 16 adicionales y , y , $ 16 subl n , n , $ 4 ; n: = n - 4 jgz n , vloop ; bucle hacia atrás si n> 0 out: ret
Desafortunadamente para SIMD, la clave estaba en el supuesto anterior, "que n es un múltiplo de 4". Siendo realistas, para bucles de propósito general donde n no está limitado de esta manera, la sobrecarga de configuración y limpieza de SIMD para hacer frente a no múltiplos del ancho de SIMD, puede exceder con creces el recuento de instrucciones dentro del bucle en sí. SIMD de ocho anchos requiere repetir el algoritmo de bucle interno primero con elementos SIMD de cuatro anchos, luego SIMD de dos anchos, luego uno (escalar), con una prueba y bifurcación entre cada uno, para cubrir los últimos elementos SIMD restantes ( 0 <= n <= 7). Esto da como resultado que ISA Architects agreguen SIMD de 2 anchos, luego SIMD de 4 anchos, luego de 8 anchos y hacia arriba. Empezamos a ver, por tanto, por qué existe AVX-512 en x86.
Los procesadores vectoriales, por otro lado, están diseñados para emitir cálculos de longitud variable para un recuento arbitrario, n, y por lo tanto requieren muy poca configuración y ninguna limpieza. Cuanto mayor es el ancho de SIMD, empeoran los problemas, lo que lleva a una proliferación masiva de códigos de operación. Las ISA vectoriales requieren solo un conjunto de operaciones, lo que proporciona una cobertura de datos de longitud variable independientemente del hardware de back-end. [8]
Para ISA de vector de estilo Cray, como RVV, se utiliza una instrucción llamada "setvl" (set Vector Length). El hardware primero define cuántos valores de datos puede procesar en un "Vector": esto podría ser registros reales o podría ser un ciclo interno (el enfoque híbrido, mencionado anteriormente). Esta cantidad máxima (el número de "carriles" de hardware) se denomina "MVL" (longitud máxima del vector).
Al llamar a setvl con el número de elementos de datos pendientes a procesar, "setvl" devuelve el número real a procesar en las instrucciones de Vector posteriores y establece el registro especial interno, "VL", en esa misma cantidad.
A continuación se muestra el ensamblador de vectores estilo Cray para el mismo bucle de estilo SIMD, arriba. Mire cuidadosamente cómo se usa t0 (que puede variar) en lugar de las constantes codificadas de forma rígida:
vloop: setvl t0 , n # VL = t0 = min (MVL, n) vld32 v0 , x # vector de carga x vld32 v1 , y # vector de carga y vmadd32 v1 , v0 , a # v1 + = v0 * a vst32 v1 , y # almacenar Y suma y , t0 * 4 # avanzar y en VL * 4 agregar x , t0 * 4 # avanzar x en VL * 4 sub n , t0 # n - = VL (t0) bnez n , vloop # repetir si n! = 0
Esto realmente no es tan diferente de la versión SIMD (procesa 4 elementos de datos por ciclo), o de la versión Scalar inicial (procesa solo uno). Podemos ver que n todavía contiene el número de elementos de datos restantes para ser procesado, pero que T0 contiene la copia de VL - el número que se va a procesar en cada iteración. t0 se resta de n después de cada iteración, y si n es cero, entonces se han procesado todos los elementos.
También tenga en cuenta que los punteros ax e y avanzan t0 multiplicado por cuatro porque ambos apuntan a datos de 32 bits, pero que n se reduce en t0 directo. En comparación con el ensamblador SIMD, hay muy poca diferencia aparente: xey avanzan mediante la constante codificada 16, n se reduce mediante un 4 codificado, por lo que inicialmente es difícil apreciar el significado. La diferencia viene al darse cuenta de que el hardware Vector podría ser capaz de realizar 4 operaciones simultáneas, o 64, o 10,000, sería exactamente el mismo Vector Assembler para todas y todavía no habría código de limpieza SIMD .
No solo tenemos un programa mucho más compacto (que ahorra en el tamaño de la caché L1), sino que, como se mencionó anteriormente, la versión Vector emite mucho más procesamiento de datos para las ALU, lo que nuevamente ahorra energía porque Instruction Decode and Issue puede permanecer inactivo.
Un aspecto fascinante aquí: la cantidad de elementos que entran en la función puede comenzar en cero . Esto establece la Longitud del vector en cero, lo que deshabilita efectivamente todas las instrucciones de Vector, convirtiéndolas en no-ops, en tiempo de ejecución. Por lo tanto, a diferencia de SIMD, incluso cuando no hay elementos para procesar, todavía no hay código de limpieza desperdiciado.
Características del procesador de vectores
Las características típicas que tendrá un buen procesador vectorial son: [9] [10]
- Carga y almacenamiento vectoriales : estos guardan de forma inherente las búsquedas de memoria virtual y están diseñados para colocar datos en registros con el mínimo de complicaciones. Las variantes avanzadas incluyen el empaquetado de estructuras, Fail-First, Gather-scatter , Indexed, Unit y Element.
- Operaciones enmascaradas : como ahora se encuentran comúnmente en las GPU, las máscaras de predicados permiten construcciones paralelas if / then / else sin ramas (que son escalares por naturaleza)
- Comprimir y expandir : por lo general, utilizando una máscara de bits, los datos se comprimen o expanden linealmente (redistribuidos) en función de si los bits de la máscara están configurados o borrados, mientras se conserva siempre el orden secuencial y nunca se duplican los valores (a diferencia de Gather-Scatter, también conocido como permute) . Estas instrucciones aparecen en AVX-512
- Register Gather, Scatter (también conocido como permute) [11] : una variación del tema Comprimir / Expandir que en su lugar toma un Vector para especificar los índices que se utilizarán para "reordenar" otro Vector. Gather / Scatter es más complejo de implementar que Compress / Expand y, al ser inherentemente no secuencial, interfiere con el encadenamiento de vectores . No debe confundirse con los modos Gather-scatter Memory Load / Store, las operaciones Gather / Scatter Vector actúan sobre los registros vectoriales y, en su lugar, a menudo se las denomina instrucción permute .
- Splat y Extract : útiles para la interacción entre Scalar y Vector, estos transmiten un solo valor a través de un Vector, o extraen un elemento de un Vector, respectivamente.
- Iota : una instrucción muy simple y estratégicamente útil que cae secuencialmente incrementos inmediatos en elementos sucesivos. Por lo general, comienza desde cero.
- Formatos matemáticos avanzados : a menudo incluyen aritmética de campo de Galois , pero pueden incluir decimal codificado en binario o decimal de punto fijo, y soporte para operaciones aritméticas mucho más grandes (precisión arbitraria) al admitir el acarreo y el procesamiento en paralelo
- Manipulación de bits : incluidas operaciones de permutación a nivel de bits, inserción y extracción de campos de bits, operaciones de centrifugado, recuento de población y muchos otros.
Falla (o falla) primero
Introducido en ARM SVE2 y RISC-V RVV es el concepto de cargas vectoriales secuenciales especulativas. ARM SVE2 tiene un registro especial llamado "Primer Registro de Fallas", [12] donde RVV modifica (trunca) la Longitud del Vector (VL). [13]
El principio básico de ffirst es intentar una gran carga vectorial secuencial, pero permitir que el hardware trunque arbitrariamente la cantidad real cargada a la cantidad que tendría éxito sin generar una falla de memoria o simplemente a una cantidad (mayor que cero) que es mas conveniente. El factor importante es que las instrucciones subsiguientes son notificadas o pueden determinar exactamente cuántas cargas realmente se realizaron correctamente, utilizando esa cantidad para realizar solo el trabajo en los datos que realmente se han cargado.
Compare esta situación con SIMD, que es un ancho de carga fijo (inflexible) y un ancho de procesamiento de datos fijo, incapaz de reaccionar a lo que realmente tuvo éxito, sin embargo, paradójicamente, si el programa SIMD intentara siquiera averiguarlo por adelantado (en cada ciclo, cada vez) lo que podría tener éxito de manera óptima, esas instrucciones solo sirven para obstaculizar el rendimiento porque serían, por necesidad, parte del ciclo interno crítico.
Esto comienza a insinuar la razón por la cual ffirst es tan innovador, y se ilustra mejor con memcpy o strcpy cuando se implementan con SIMD estándar de 128 bits no predicado y no ffirst. Para IBM POWER9, el número de instrucciones optimizadas a mano para implementar strncpy supera las 240. [14] . Por el contrario, la misma rutina de strncpy en el ensamblador RVV optimizado a mano son solo 22 instrucciones. [15]
El ejemplo anterior de SIMD podría potencialmente fallar y fallar al final de la memoria, debido a intentos de leer demasiados valores: también podría causar un número significativo de páginas o fallas desalineadas. Por el contrario, al permitir a la Arquitectura Vectorial la libertad de decidir cuántos elementos cargar, la primera parte de una strncpy, si comienza inicialmente en un límite de memoria subóptimo, puede devolver solo las cargas suficientes para que en iteraciones posteriores del bucle los lotes de lecturas de memoria vectorizada se alinean de manera óptima con las cachés subyacentes y las disposiciones de memoria virtual. Además, el hardware puede optar por aprovechar la oportunidad para finalizar las lecturas de memoria de cualquier iteración de bucle dado exactamente en un límite de página (evitando una costosa búsqueda de TLB), con la ejecución especulativa preparando la siguiente página de memoria virtual mientras los datos aún se están procesando en el bucle actual . [dieciséis]
Rendimiento y aceleración
Sea r la relación de velocidad del vector yf la relación de vectorización. Si el tiempo que tarda la unidad vectorial en agregar una matriz de 64 números es 10 veces más rápido que su contraparte escalar equivalente, r = 10. Además, si el número total de operaciones en un programa es 100, de las cuales solo 10 son escalares (después de la vectorización), entonces f = 0.9, es decir, el 90% del trabajo lo realiza la unidad vectorial. Sigue la aceleración alcanzable de:
Entonces, incluso si el rendimiento de la unidad vectorial es muy alto () obtenemos una aceleración menor que , lo que sugiere que la razón f es crucial para el desempeño. Esta relación depende de la eficiencia de la compilación como adyacencia de los elementos en la memoria.
Programación de arquitecturas informáticas heterogéneas
Se diseñaron varias máquinas para incluir tanto procesadores tradicionales como procesadores vectoriales, como Fujitsu AP1000 y AP3000. La programación de máquinas tan heterogéneas puede resultar difícil, ya que el desarrollo de programas que aprovechan al máximo las características de los diferentes procesadores aumenta la carga del programador. Aumenta la complejidad del código y disminuye la portabilidad del código al requerir que el código específico del hardware se intercale en todo el código de la aplicación. [17] Equilibrar la carga de trabajo de la aplicación en los procesadores puede ser problemático, especialmente dado que suelen tener diferentes características de rendimiento. Existen diferentes modelos conceptuales para abordar el problema, por ejemplo, utilizando un lenguaje de coordinación y bloques de construcción de programas (bibliotecas de programación o funciones de orden superior). Cada bloque puede tener una implementación nativa diferente para cada tipo de procesador. Los usuarios simplemente programan utilizando estas abstracciones y un compilador inteligente elige la mejor implementación en función del contexto. [18]
Ver también
- Arquitectura SX
- GPGPU
- Computar kernel
- Procesamiento de flujo
- SIMD
- Vectorización automática
- Encadenamiento (procesamiento de vectores)
- Computadora para operaciones con funciones
- RISC-V , un estándar ISA abierto con una extensión de vector de ancho variable asociada .
- Procesador de barril
- Unidad de procesamiento de tensor
Referencias
- ^ [1]
- ^ [2]
- ^ [3]
- ^ [4]
- ^ [5]
- ^ Aspex Linedancer HD
- ^ Malinovsky, BN (1995). La historia de la tecnología informática en sus caras (en ruso) . Kiew: "KIT" firme. ISBN 5-7707-6131-8.
- ^ [ https://www.sigarch.org/simd-instructions-considered-harmful/ SIMD considerado dañino]
- ^ [6]
- ^ [7]
- ^ [8]
- ^ Introducción a ARM SVE2
- ^ Cargas RVV en primer defecto
- ^ [ https://patchwork.ozlabs.org/project/glibc/patch/[email protected]/ PATCH a libc6 para agregar una cadena de POWER9 optimizada]
- ^ Ejemplo de strncpy RVV
- ^ Papel ARM SVE2 de N. Stevens
- ^ Kunzman, DM; Kale, LV (2011). "Programación de sistemas heterogéneos". 2011 IEEE International Symposium on Parallel and Distributed Processing Workshops and Phd Forum . pag. 2061. doi : 10.1109 / IPDPS.2011.377 . ISBN 978-1-61284-425-1.
- ^ John Darlinton; Moustafa Ghanem; Yike Guo; Hing Wing To (1996), "Organización guiada de recursos en computación paralela heterogénea", Journal of High Performance Computing , 4 (1): 13-23, CiteSeerX 10.1.1.37.4309
enlaces externos
- La historia del desarrollo de la computación paralela (de 1955 a 1993)
- Computación vectorial, pasado, presente y futuro, por Steve Scott, Cray Inc.