2016-02-09 13 views
8

私は画像処理を扱います。私は当然知っている255は2SSEを使用して16ビット整数を255で分ける方法は?

のパワーの倍数ではないので、私は、)(_mm_srli_epi16ようにシフト演算子を使用することはできません255

により16ビット整数SSEベクトルを分割する必要 整数を浮動小数点に変換し、除算を行った後、整数に変換することができます。

しかし、誰かが別の解決策を知っているかもしれない...

+1

[this](http://stackoverflow.com/q/16822757/3959454)は役に立ちますか? –

+1

通常、256で除算します(切り捨てではなく丸めます)。256でなく255でなければならない理由は何ですか? –

+1

おそらく[これ](http://stackoverflow.com/questions/31575833/fastest-method-of-vectorized-integer-division-by-non-constant-divisor)質問はあなたにとっても面白いです。将来、非定数整数除算に対処する必要がある場合は、floatへの変換も高速オプションです。 – Youka

答えて

10

除算の整数近似値が255である:

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

だから、SSE2を使用して、それは次のようになります。

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

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); 
} 
Altivecに0

(パワー):NEONについて

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

(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

これは 'value == 65535'とすべての負の数 –

+1

私はそれがアルファブレンディングのために完全に機能することを知っています。しかし、別のケースではエラーを除外しません。 – ErmIg

+0

@AntonSavin:あなたが見つけた他の質問へのリンクに基づいて回答を投稿しました。 gccは完璧に正確なバージョンをうまくベクトル化します。 –

3

GCCはさらにHWORD(x * 0x8081) >> 7に簡略化することができるDWORD(x * 0x8081) >> 0x17、最終的にHWORD((x << 15) + (x << 7) + x) >> 7からunsigned shortあるx/255 X で最適化します。

#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

あなたはすべてのケースのために正確に正しい結果をしたい場合は、アントンがリンクされ、質問にMarc Glisse'sコメントからの助言に従ってください:SSE integer division?

利用GNU Cネイティブ

SIMDマクロは次のように見ることができ

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 

トンで:あなたの与えられたスカラー、and see what it doesによるベクトルの分割を発現するためのベクターの構文彼は答えを膨満感のリスク、ここでは、組み込み関数で再びです:godbolt出力で

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

、gccがset1のためにメモリ内の同じ16Bの定数を使用するのに十分スマートで、それ自体を生成したためであることに注意してくださいdiv255です。 AFAIK、これは文字列定数マージのように機能します。

関連する問題