2017-03-12 5 views
0

現在、NEON命令を使用するために画像処理コードの一部を最適化しようとしています。NEONの実行時間が増加する

私は非常に大きなフロート配列を持っていて、最初のもののそれぞれの値に2番目のものの3つの連続する値を掛けたいとしましょう。 (もう一つは、大の3倍である。)

float*  l_ptrGauss_pf32 = [...]; 
float*  l_ptrLaplace_pf32 = [...]; // Three times as large 

for (uint64_t k = 0; k < l_numPixels_ui64; ++k) 
{ 
    float l_weight_f32 = *l_ptrGauss_pf32; 

    *l_ptrLaplace_pf32 *= l_weight_f32; 
    ++l_ptrLaplace_pf32; 
    *l_ptrLaplace_pf32 *= l_weight_f32; 
    ++l_ptrLaplace_pf32; 
    *l_ptrLaplace_pf32 *= l_weight_f32; 
    ++l_ptrLaplace_pf32; 

    ++l_ptrGauss_pf32; 
} 

私はNEON組み込み関数で上記のコードを交換するときに、実行時間は、約10%長いです。

float32x4_t l_gaussElem_f32x4; 
float32x4_t l_laplElem1_f32x4; 
float32x4_t l_laplElem2_f32x4; 
float32x4_t l_laplElem3_f32x4; 

for(uint64_t k=0; k<(l_lastPixelInBlock_ui64/4); ++k) 
{ 
    l_gaussElem_f32x4 = vld1q_f32(l_ptrGauss_pf32); 
    l_laplElem1_f32x4 = vld1q_f32(l_ptrLaplace_pf32); 
    l_laplElem2_f32x4 = vld1q_f32(l_ptrLaplace_pf32+4); 
    l_laplElem3_f32x4 = vld1q_f32(l_ptrLaplace_pf32+8); 

    l_laplElem1_f32x4 = vmulq_f32(l_gaussElem_f32x4, l_laplElem1_f32x4); 
    l_laplElem2_f32x4 = vmulq_f32(l_gaussElem_f32x4, l_laplElem2_f32x4); 
    l_laplElem3_f32x4 = vmulq_f32(l_gaussElem_f32x4, l_laplElem3_f32x4); 

    vst1q_f32(l_ptrLaplace_pf32, l_laplElem1_f32x4); 
    vst1q_f32(l_ptrLaplace_pf32+4, l_laplElem2_f32x4); 
    vst1q_f32(l_ptrLaplace_pf32+8, l_laplElem3_f32x4); 

    l_ptrLaplace_pf32 += 12; 
    l_ptrGauss_pf32 += 4; 
} 

両方のバージョンは、Apple LLVM 8.0を使用して-Ofastでコンパイルされます。コンパイラはNEON組み込み関数がなくてもこのコードを最適化することができますか?

+0

あなたは、コンパイラによって生成されたコードを点検しましたか? Clangは、組み込み関数に頼ることなくNeon命令を使用します(インライン展開を使用する場合よりも優れています) intrinsicsは "正確にこのようにする必要がある"とみなされる傾向があります)。 –

+1

手をアンロールしたためにループのアンロールやその他の再配置が異なることもあります。 –

+0

これはarmv7またはarm64用ですか?また、マイナータンジェントですが、 'k 'と' l_lastPixelInBlock_ui64'(armv7は32ビットであるため)に 'size_t'を使うほうが良いでしょう。さらに、ポインターエイリアスはできますか?そうでない場合、それらを 'restrict'とマークすると助けになるはずです。 – Cornstalks

答えて

0

あなたのコードには、比較的多くのベクトル読み込み操作といくつかの乗算操作が含まれています。だから私はベクトルの読み込みを最適化することをお勧めします。 2つのステップがあります:

  • アライメントされたメモリをアレイに使用します。
  • プリフェッチを使用します。私は次の関数を使用することをお勧めしますこれを行うために

inline float32x4_t Load(const float * p) 
{ 
    // use prefetch: 
    __builtin_prefetch(p + 256); 
    // tell compiler that address is aligned: 
    float * _p = (float *)__builtin_assume_aligned(p, 16); 
    return vld1q_f32(_p); 
} 
関連する問題