2017-01-18 7 views
0

効率的にメモリのブロック全体をシフト:ビット私はソーベル演算を実行した後、コードを次している

short* tempBufferVert = new short[width * height]; 
ippiFilterSobelVertBorder_8u16s_C1R(pImg, width, tempBufferVert, width * 2, dstSize, IppiMaskSize::ippMskSize3x3, IppiBorderType::ippBorderConst, 0, pBufferVert); 
for (int i = 0; i < width * height; i++) 
    tempBufferVert[i] >>= 2; 

イライラする事は、ビットシフトは、それをすべての最長撮影操作でされ、IPPのソーベルがそうですそれは私の愚かなビットシフトより速く実行されます。ビットシフトを最適化するにはどうすればよいですか?また、IPPや他のオプション(AVX?)を使用してメモリ全体をビットシフトすることもできます(ただし、Visual Studioの実装では>> =が使用します)。

+0

'short * bufPtr = tempBufferVert;'を追加して、 '[i]'演算子を使うのではなく、ループ内の 'bufPtr'ポインタをインクリメントしてみることができます。私はそれが速くなることを保証することはできませんが、それは私が試みることの一つです。 – Steeve

+0

'width' /' height'の型は何ですか、 'tempBufferVert'でエイリアスにすることはできますか?ループの生成されたasmを見てきましたか? – Jarod42

答えて

1

まず、最適化を有効にしてコンパイルしていることを確認して(たとえば-O3)、コンパイラが正しいシフトループを自動ベクトル化しているかどうかを確認します。そうでない場合、あなたはおそらく、SSEとの有意な改善を得ることができます。

#include <emmintrin.h> // SSE2 

for (int i = 0; i < width * height; i += 8) 
{ 
    __m128i v = _mm_loadu_si128((__m128i *)&tempBufferVert[i]); 
    v = _mm_srai_epi16(v, 2); // v >>= 2 
    _mm_storeu_si128((__m128i *)&tempBufferVert[i], v); 
} 

:あなたはおそらく、いくつかのループの展開をさらに良く行うことができます

(注width*heightを想定すると、8の倍数である)、および/またはAVX2を使用していますが、これはあなたのニーズに十分対応できます。

1

C++オプティマイザは、インデックスループよりもイテレータベースのループでより優れたパフォーマンスを発揮します。

これは、インデックスオーバーフロー時にアドレス演算がどのように機能するかをコンパイラーが前提とするためです。配列にインデックスを使用するときに同じ前提を作るためには、運のよってインデックスの正しいデータ型を選択する必要があります。

シフトコードのように表すことができる。

(正しい最適化を有効にして)をベクトル化する
void shift(short* first, short* last, int bits) 
{ 
    while (first != last) { 
    *first++ >>= bits; 
    } 
} 

int test(int width, int height) 
{ 
    short* tempBufferVert = new short[width * height]; 
    shift(tempBufferVert, tempBufferVert + (width * height), 2); 

} 

https://godbolt.org/g/oJ8Boj

メモをループの中央になる方法:

.L76: 
     vmovdqa ymm0, YMMWORD PTR [r9+rdx] 
     add  r8, 1 
     vpsraw ymm0, ymm0, 2 
     vmovdqa YMMWORD PTR [r9+rdx], ymm0 
     add  rdx, 32 
     cmp  rsi, r8 
     ja  .L76 
     lea  rax, [rax+rdi*2] 
     cmp  rcx, rdi 
     je  .L127 
     vzeroupper 
+0

良いですが、イテレータとインデックス作成の主張は赤字です - 私は配列インデックス作成版のshiftを試しました。イテレータバージョン(gcc 6.3)と同じコードを生成しました。現代のコンパイラでは、エイリアシングの問題を防ぐことができない限り、イテレータ/ポインタベースのループを配列索引付きのループに/から変換します。 –

+0

@PaulRはエイリアシングの問題だけでなく、インデックス変数のワードサイズの特性も示します。そこにはcppcon2016の話があります。 –

+0

はい、いい点です。私は、インデックス変数に 'size_t'や' ssize_t'を使う傾向があります。 'int'を使うと効率が悪くなることがあります。 –

関連する問題