2012-07-02 30 views
8

Ben neon intrinsics kullanarak bir c kodu optimize edilmiş birine dönüştürmeye çalışıyorum.SSE intrinsics için Neon eşdeğeri

Burada, işleçlerin vektörleri üzerinde olmayan 2 operatör üzerinde çalışan c kodları vardır.

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; 
} 

bu operasyonun optimize BKZ sürümü zaten tarafından uygulamaya konmuştur aşağıdadır:

#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); 

Ben:

#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. 

Neredeyse neon intrinsics kullanarak bu bir dönüştürülen ettik sadece neon eşdeğerleri _mm_mullo_epi16 ((a), (b)); ve _mm_mulhi_epu16 ((a), (b)); eksik. Ya bir şeyi yanlış anladım ya da NEON'da böyle bir içsel yapı yok. NEONS intrinsics kullanarak tez basamaklarının nasıl arşivleneceği konusunda bir eşdeğer yok mu?

Update: fonksiyon operants uint16x8_t NEONU vektörleri (her bir eleman, 0 ile 65535 arasında bir uint16_t => tamsayı olduğu) şunlardır:

Aşağıdakilerin vurgulamak için unuttum. Bir cevapta birisi intrinsic vqdmulhq_s16() kullanmayı önerdi. Bunun kullanımı, verilen uygulama ile uyuşmayacaktır çünkü çarpım intrinsik, vektörleri imzalı değerler olarak yorumlayacak ve yanlış bir çıktı üretecektir.

+0

Değerler> 32767'ye sahip olacaksanız, aşağıda önerilen genişletme çarpanını kullanmanız gerekecektir (vmull_u16). Değerlerinizin hepsinin <32768 olacağını biliyorsanız, vqdmulhq_s16'yı kullanabilirsiniz. – BitBank

cevap

5

Sen kullanabilirsiniz: 32 bitlik bir ürün vektör döndürür

uint32x4_t vmull_u16 (uint16x4_t, uint16x4_t) 

. Sonucu yüksek ve düşük parçalara ayırmak istiyorsanız NEON unzip intrinsic'i kullanabilirsiniz.

+0

Bu komut bir 16x16 = 32 çarpıdır (çıkışı genişletir). Daha yakın talimatlar var (cevabıma bakın). – BitBank

+1

@BitBank: OP, 16 bitlik ve 16 bitlik daha düşük bir bit aralığına ihtiyaç duyuyor. Bir katlama/doyurucu çarpan, bir ikame değildir çünkü hassasiyeti kaybedersiniz. –

1

vmulq_s16() _mm_mullo_epi16 öğesinin karşılığıdır. _mm_mulhi_epu16'nın tam karşılığı yoktur; En yakın talimat vqdmulhq_s16() "doyurucu, iki katına çıkma, çarpma, yüksek kısım döndürme" dir. Yalnızca işaretli 16 bit değerlerde çalışır ve iki katına çıkartmak için girişi veya çıkışı 2'ye bölmeniz gerekir.

+0

vqdmulhq_s16() imzalı girişler kullandığından, GCC yanlış yazılan argümanlar hakkında şikayet ediyor ... Uint16x8_t'den int16x8_t'ye kadar verimli bir şekilde nasıl dönüştürülür? – Kami

+0

Döküm makroları var; vreinterpretq_s16_u16() kullanın – BitBank

+0

İmzalı çoğaltma ile ilgili düzenlemeye bakın! – Kami