2016-02-09 20 views
8

Ich habe mit Bildverarbeitung zu tun. Ich brauche um 16-Bit-Integer SSE Vektor dividieren 255.Wie teilen Sie 16-Bit-Ganzzahl durch 255 mit SSE?

I nicht Verschiebungsoperator wie _mm_srli_epi16 verwenden können(), da 255 nicht ein Vielfaches der Potenz von 2

I ist selbstverständlich wissen, dass Es ist möglich, Ganzzahl in Float zu konvertieren, Division durchzuführen und dann die Konvertierung in Ganzzahl zurückzusetzen.

Aber vielleicht kennt jemand eine andere Lösung ...

+1

Hilft [dies] (http://stackoverflow.com/q/16822757/3959454)? –

+1

In der Regel würden Sie durch 256 teilen (mit Abrundung statt Verkürzung) - gibt es einen Grund, warum es 255 und nicht 256 sein muss? –

+1

Vielleicht ist diese Frage (http://stackoverflow.com/questions/31575833/fastest-method-of-vectorized-integer-division-by-non-constant-divisor) auch für Sie interessant. Wenn Sie in Zukunft mit nichtkonstanten Ganzzahldivisionen umgehen müssen, ist die Konvertierung in Float ebenfalls eine schnelle Option. – Youka

Antwort

10

Es gibt eine ganze Zahl Annäherung der Division durch 255:

inline int DivideBy255(int value) 
{ 
    return (value + 1 + (value >> 8)) >> 8; 
} 

So mit der SSE2 mit ihm aussehen wird:

inline __m128i DivideI16By255(__m128i value) 
{ 
    return _mm_srli_epi16(_mm_add_epi16(
     _mm_add_epi16(value, _mm_set1_epi16(1)), _mm_srli_epi16(value, 8)), 8); 
} 

Für AVX2:

inline __m256i DivideI16By255(__m256i value) 
{ 
    return _mm256_srli_epi16(_mm256_add_epi16(
     _mm256_add_epi16(value, _mm256_set1_epi16(1)), _mm256_srli_epi16(value, 8)), 8); 
} 

Für Altivec (Power):

typedef __vector int16_t v128_s16; 
const v128_s16 K16_0001 = {1, 1, 1, 1, 1, 1, 1, 1}; 
const v128_s16 K16_0008 = {8, 8, 8, 8, 8, 8, 8, 8}; 

inline v128_s16 DivideBy255(v128_s16 value) 
{ 
    return vec_sr(vec_add(vec_add(value, K16_0001), vec_sr(value, K16_0008)), K16_0008); 
} 

für Neon (ARM):

inline int16x8_t DivideI16By255(int16x8_t value) 
{ 
    return vshrq_n_s16(vaddq_s16(
     vaddq_s16(value, vdupq_n_s16(1)), vshrq_n_s16(value, 8)), 8); 
} 
+0

Das ist falsch für 'value == 65535' und für alle negativen Zahlen (so funktioniert weder für 16-Bit-Ganzzahlen mit Vorzeichen noch ohne Vorzeichen) –

+1

Ich weiß, dass es perfekt für Alpha-Blending funktioniert. Aber ich schließe Fehler in anderen Fällen nicht aus. – ErmIg

+0

@AntonSavin: Ich habe eine Antwort basierend auf dem Link zu der anderen Frage, die Sie gefunden haben, gepostet. gcc vektorisiert eine Version mit perfekter Genauigkeit und nur ein paar Operationen mehr. –

3

GCC optimiert x/255 mit xunsigned short zu DWORD(x * 0x8081) >> 0x17 ist, die weiter HWORD(x * 0x8081) >> 7 und schließlich HWORD((x << 15) + (x << 7) + x) >> 7 vereinfacht werden kann. SSE integer division?

Verwendung GNU C nativer:

SIMD-Makros kann wie folgt aussehen: Anton verbunden

#define MMX_DIV255_U16(x) _mm_srli_pi16(_mm_mulhi_pu16(x, _mm_set1_pi16((short)0x8081)), 7) 
#define SSE2_DIV255_U16(x) _mm_srli_epi16(_mm_mulhi_epu16(x, _mm_set1_epi16((short)0x8081)), 7) 
#define AVX2_DIV255_U16(x) _mm256_srli_epi16(_mm256_mulhi_epu16(x, _mm256_set1_epi16((short)0x8081)), 7) 
6

Wenn Sie ein genau richtiges Ergebnis für alle Fälle wollen, auf die Frage, den Rat von Marc Glisse's Kommentar folgen Vektor-Syntax Teilung eines Vektors von Ihren gegebenen skalaren auszudrücken, and see what it does:

typedef short vec_s16 __attribute__((vector_size(16))); 

vec_s16 div255(vec_s16 x){ return x/255; } // signed division 

    ; function arg x starts in xmm0 
    vpmulhw xmm1, xmm0, XMMWORD PTR .LC3[rip] ; a vector of set1(0x8081) 
    vpaddw xmm1, xmm1, xmm0 
    vpsraw xmm0, xmm0, 15  ; shift the original 
    vpsraw xmm1, xmm1, 7  ; shift the mulhi-and-add result 
    vpsubw xmm0, xmm1, xmm0 

.LC3: 
     .value -32639 
     .value -32639 
     ; repeated 

Bei t er Gefahr, dass die Antwort Blähungen, hier ist es wieder mit intrinsics:

__m128i div255_si128(__m128i x) { 
    __m128i tmp = _mm_mulhi_epi16(x, _mm_set1_epi16(0x8081)); 
    tmp = _mm_add_epi16(tmp, x); // There's no integer FMA that's usable here 
    x = _mm_srai_epi16(x, 15); // broadcast the sign bit 
    tmp = _mm_srai_epi16(tmp, 7); 
    return _mm_sub_epi16(tmp, x); 
} 

Im Godbolt Ausgang zu beachten, dass gcc intelligent genug ist, um die gleichen 16B konstant im Speicher für die set1 und für die man verwenden es selbst erzeugt für div255. AFAIK, das funktioniert wie string-constant merging.