2010-07-13 27 views
13

cómo usar las intrínsecas Multiplicar-Acumular proporcionadas por GCC?¿Cómo usar los intrínsecos de multiplicación y acumulación en ARM Cortex-a8?

float32x4_t vmlaq_f32 (float32x4_t , float32x4_t , float32x4_t); 

¿Alguien puede explicar qué tres parámetros tengo que pasar a esta función. Me refiero a los registros de origen y destino y lo que devuelve la función?

Ayuda !!!

+6

Los documentos de GCC (y los documentos de RealView para los intrínsecos en los que parecen basarse los intrínsecos de GCC) son bastante escasos ... Si no obtiene una respuesta decente, sugiero que solo compile una algunas llamadas y echar un vistazo a la asamblea que sale. Eso debería darte una idea bastante buena (incluso si es una manera menos que ideal de ir). –

Respuesta

19

descripción sencilla de la instrucción VMLA hace lo siguiente:

struct 
{ 
    float val[4]; 
} float32x4_t 


float32x4_t vmla (float32x4_t a, float32x4_t b, float32x4_t c) 
{ 
    float32x4 result; 

    for (int i=0; i<4; i++) 
    { 
    result.val[i] = b.val[i]*c.val[i]+a.val[i]; 
    } 

    return result; 
} 

Y todo esto se compila en una instrucción de ensamblador de la chamusquina :-)

Puede utilizar esta NEON-ensamblador intrínseca entre otras cosas en las típicas multiplicaciones de matriz 4x4 para gráficos 3D como este:

float32x4_t transform (float32x4_t * matrix, float32x4_t vector) 
{ 
    /* in a perfect world this code would compile into just four instructions */ 
    float32x4_t result; 

    result = vml (matrix[0], vector); 
    result = vmla (result, matrix[1], vector); 
    result = vmla (result, matrix[2], vector); 
    result = vmla (result, matrix[3], vector); 

    return result; 
} 

Esto ahorra un par de ciclos porque no tiene que agregar los resultados después de la multiplicación. La adición se utiliza tan a menudo que se acumulan de manera múltiple en estos días (incluso x86 los ha agregado en algún conjunto reciente de instrucciones SSE).

También vale la pena mencionar: las operaciones de acumulación múltiple como esta son muy muy comunes en aplicaciones de álgebra lineal y DSP (procesamiento de señales digitales). ARM fue muy inteligente e implementó un fast-path dentro del Cortex-A8 NEON-Core. Este camino rápido entra en acción si el primer argumento (el acumulador) de una instrucción VMLA es el resultado de una instrucción VML o VMLA precedente. Podría entrar en detalles, pero en pocas palabras, una serie de instrucciones de este tipo funciona cuatro veces más rápido que una serie VML/VADD/VML/VADD.

Eche un vistazo a mi simple multiplicación de matriz: Hice exactamente eso. Debido a este camino rápido, se ejecutará aproximadamente cuatro veces más rápido que la implementación escrita usando VML y ADD en lugar de VMLA.

+0

Gracias por una respuesta tan detallada. Su respuesta no solo explica la funcionalidad de la instrucción sino también los pros y contras para usar esta instrucción. – HaggarTheHorrible

+0

Hola Nils, entendí cómo la multiplicación de la matriz se puede acelerar usando las instrucciones NEON. Es realmente adictivo ahora :) Quiero usar las instrucciones de NEON para hacer el inverso de una matriz, ¿me pueden indicar algunos buenos documentos que explican cómo usar las instrucciones de NEON para hacer la matriz inversa o pueden darme alguna idea, cómo para hacer eso? Gracias. – HaggarTheHorrible

+1

para matriz inversa Realizaría una búsqueda en google en "sse matrix inverse" y portaría el código sse a NEON. La forma habitual es calcular el inverso para matrices pequeñas (4x4) a través de la regla de Cramer. –

8

Google'd para vmlaq_f32, apareció the reference for the RVCT compiler tools. He aquí lo que dice:

Vector multiply accumulate: vmla -> Vr[i] := Va[i] + Vb[i] * Vc[i] 
... 
float32x4_t vmlaq_f32 (float32x4_t a, float32x4_t b, float32x4_t c); 

Y

Los siguientes tipos se definen para representar vectores. tipos de datos vector NEÓN se nombran de acuerdo con el siguiente patrón: <tipo> < tamaño > x < número de carriles > _t Por ejemplo, int16x4_t es un vector que contiene cuatro carriles que contienen cada uno un entero de 16 bits con signo. La Tabla E.1 enumera los tipos de datos vectoriales.

IOW, el valor de retorno de la función será un vector que contiene 4 flota de 32 bits, y cada elemento del vector se calcula multiplicando los elementos correspondientes de b y c, y la adición de los contenidos de a.

HTH

1
result = vml (matrix[0], vector); 
result = vmla (result, matrix[1], vector); 
result = vmla (result, matrix[2], vector); 
result = vmla (result, matrix[3], vector); 

Sin embargo, esta secuencia no funcionará.El problema es que el componente x solamente se acumula x modulada por las filas de la matriz y se puede expresar como:

result.x = vector.x * (matrix[0][0] + matrix[1][0] + matrix[2][0] + matrix[3][0]); 

...

La secuencia correcta sería:

result = vml (matrix[0], vector.xxxx); 
result = vmla(result, matrix[1], vector.yyyy); 

...

NEON y SSE no tienen una selección incorporada para los campos (esto requeriría 8 bits en la codificación de instrucciones, por registro vectorial). GLSL/HLSL, por ejemplo, tiene este tipo de instalaciones, por lo que la mayoría de las GPU también lo tienen.

forma alternativa para lograr esto sería:

result.x = dp4(vector, matrix[0]); 
result.y = dp4(vector, matrix[1]); 

... // y por supuesto, la matriz sería transponer para que esto dió mismo resultado

El mul, madd, madd, Por lo general, se prefiere la secuencia madd ya que no requiere una máscara de escritura para los campos de registro de destino.

De lo contrario, el código se ve bien. =)

Cuestiones relacionadas