2012-07-02 1 views
8

J'essaie de convertir un code c en un code optimisé en utilisant des intrinsèques néon.Néon équivalent à l'intrinsèque SSE

Voici les codes c qui fonctionnent sur 2 opérateurs et non sur les vecteurs d'opérateurs.

uint16_t mult_z216(uint16_t a,uint16_t b){ 
unsigned int c1 = a*b; 
    if(c1) 
    { 
     int c1h = c1 >> 16; 
     int c1l = c1 & 0xffff; 
     return (c1l - c1h + ((c1l<c1h)?1:0)) & 0xffff; 
    } 
    return (1-a-b) & 0xffff; 
} 

La SEE version optimisée de cette opération a déjà été mis en œuvre par ce qui suit:

#define MULT_Z216_SSE(a, b, c) \ 
    t0 = _mm_or_si128 ((a), (b)); \ //Computes the bitwise OR of the 128-bit value in a and the 128-bit value in b. 
    (c) = _mm_mullo_epi16 ((a), (b)); \ //low 16-bits of the product of two 16-bit integers 
    (a) = _mm_mulhi_epu16 ((a), (b)); \ //high 16-bits of the product of two 16-bit unsigned integers 
    (b) = _mm_subs_epu16((c), (a)); \ //Subtracts the 8 unsigned 16-bit integers of a from the 8 unsigned 16-bit integers of c and saturates 
    (b) = _mm_cmpeq_epi16 ((b), C_0x0_XMM); \ //Compares the 8 signed or unsigned 16-bit integers in a and the 8 signed or unsigned 16-bit integers in b for equality. (0xFFFF or 0x0) 
    (b) = _mm_srli_epi16 ((b), 15); \ //shift right 16 bits 
    (c) = _mm_sub_epi16 ((c), (a)); \ //Subtracts the 8 signed or unsigned 16-bit integers of b from the 8 signed or unsigned 16-bit integers of a. 
    (a) = _mm_cmpeq_epi16 ((c), C_0x0_XMM); \ ////Compares the 8 signed or unsigned 16-bit integers in a and the 8 signed or unsigned 16-bit integers in b for equality. (0xFFFF or 0x0) 
    (c) = _mm_add_epi16 ((c), (b)); \ // Adds the 8 signed or unsigned 16-bit integers in a to the 8 signed or unsigned 16-bit integers in b. 
    t0 = _mm_and_si128 (t0, (a)); \ //Computes the bitwise AND of the 128-bit value in a and the 128-bit value in b. 
    (c) = _mm_sub_epi16 ((c), t0); ///Subtracts the 8 signed or unsigned 16-bit integers of b from the 8 signed or unsigned 16-bit integers of a. 

J'ai presque converti celui-ci en utilisant intrinsics au néon:

#define MULT_Z216_NEON(a, b, out) \ 
    temp = vorrq_u16 (*a, *b); \ 
    // ?? 
    // ?? 
    *b = vsubq_u16(*out, *a); \ 
    *b = vceqq_u16(*out, vdupq_n_u16(0x0000)); \ 
    *b = vshrq_n_u16(*b, 15); \ 
    *out = vsubq_s16(*out, *a); \ 
    *a = vceqq_s16(*c, vdupq_n_u16(0x0000)); \ 
    *c = vaddq_s16(*c, *b); \ 
    *temp = vandq_u16(*temp, *a); \ 
    *out = vsubq_s16(*out, *a); 

Je suis ne manque que les équivalents de néon de _mm_mullo_epi16 ((a), (b)); et _mm_mulhi_epu16 ((a), (b));. Soit je me méprends quelque chose, soit il n'y a pas d'intrinsèque dans NEON. S'il n'y a pas d'équivalent comment archiver ces étapes en utilisant NEONS intrinsics?

UPDATE:

j'ai oublié de mettre l'accent sur le point suivant: les opérants de la fonction sont uint16x8_t vecteurs NEON (chaque élément est un uint16_t => entiers compris entre 0 et 65535). Dans une réponse quelqu'un a proposé d'utiliser le vqdmulhq_s16() intrinsèque. L'utilisation de celui-ci ne correspondra pas à l'implémentation donnée car la multiplication intrinsèque interprétera les vecteurs comme des valeurs signées et produira une mauvaise sortie.

+0

Si vous avez des valeurs supérieures à 32767, vous devez utiliser la multiplication d'élargissement suggérée ci-dessous (vmull_u16). Si vous savez que vos valeurs seront toutes <32768, vous pouvez utiliser vqdmulhq_s16. – BitBank

Répondre

5

Vous pouvez utiliser:

uint32x4_t vmull_u16 (uint16x4_t, uint16x4_t) 

qui retourne un vecteur de 32 produits bits. Si vous voulez décomposer le résultat en parties haute et basse, vous pouvez utiliser l'option NEON unzip intrinsic.

+0

Cette instruction est une multiplication 16x16 = 32 (élargit la sortie). Il y a des instructions plus proches (voir ma réponse). – BitBank

+1

@BitBank: L'OP a besoin des 16 bits les plus élevés et des 16 bits les plus bas, donc il a besoin d'un résultat de 32 bits. Une multiplication doublée/saturante n'est pas un substitut car vous perdez de la précision. –

1

vmulq_s16() est l'équivalent de _mm_mullo_epi16. Il n'y a pas d'équivalent exact de _mm_mulhi_epu16; l'instruction la plus proche est vqdmulhq_s16() qui est "saturation, doublage, multiplication, retour partie haute". Il fonctionne uniquement sur des valeurs 16 bits signées et vous devrez diviser l'entrée ou la sortie par 2 pour annuler le doublage.

+0

Depuis que vqdmulhq_s16() utilise des entrées signées, GCC se plaint de mauvais arguments typés ... Comment convertir efficacement uint16x8_t en int16x8_t? – Kami

+0

Il existe des macros de moulage; use vreinterpretq_s16_u16() – BitBank

+0

Voir mes éditions sur la multiplication signée! – Kami