2015-05-13 63 views
3

我希望有人可以在这里帮忙。从simd基于mask的大型矢量加载矢量

我有一个大的字节向量,从中创建一个小字节向量(基于掩码),然后用simd处理。

当前掩码是baseOffset +子掩码(byte [256])的数组,对于存储进行了优化,因为存在> 10^8。我创建一个maxsize子向量,然后循环遍历mask数组,并将baseOffssetby 256乘以大值向量的掩码加载中的每个位偏移量,然后将这些值依次放入较小的向量中。然后通过多个VPMADDUBSW处理较小的矢量并进行累加。我可以改变这种结构。例如,走一遍使用8K位数组缓冲区,然后创建小向量。

有没有更快的方法可以创建子数组?

我把代码退出应用到测试程序,但原来是在变化的状态(移动到AVX2和拉动多出来的C#)

#include "stdafx.h" 
#include<stdio.h> 
#include <mmintrin.h> 
#include <emmintrin.h> 
#include <tmmintrin.h> 
#include <smmintrin.h> 
#include <immintrin.h> 


//from 
char N[4096] = { 9, 5, 5, 5, 9, 5, 5, 5, 5, 5 }; 
//W 
char W[4096] = { 1, 2, -3, 5, 5, 5, 5, 5, 5, 5 }; 

char buffer[4096] ; 





__declspec(align(2)) 
struct packed_destination{ 
    char blockOffset; 
    __int8 bitMask[32]; 

}; 

__m128i sum = _mm_setzero_si128(); 
packed_destination packed_destinations[10]; 



void process128(__m128i u, __m128i s) 
{ 
    __m128i calc = _mm_maddubs_epi16(u, s); // pmaddubsw 
    __m128i loints = _mm_cvtepi16_epi32(calc); 
    __m128i hiints = _mm_cvtepi16_epi32(_mm_shuffle_epi32(calc, 0x4e)); 
    sum = _mm_add_epi32(_mm_add_epi32(loints, hiints), sum); 
} 

void process_array(char n[], char w[], int length) 
{ 
    sum = _mm_setzero_si128(); 
    int length128th = length >> 7; 
    for (int i = 0; i < length128th; i++) 
    { 
     __m128i u = _mm_load_si128((__m128i*)&n[i * 128]); 
     __m128i s = _mm_load_si128((__m128i*)&w[i * 128]); 
     process128(u, s); 
    } 
} 


void populate_buffer_from_vector(packed_destination packed_destinations[], char n[] , int dest_length) 
{ 
    int buffer_dest_index = 0; 
    for (int i = 0; i < dest_length; i++) 
    { 
     int blockOffset = packed_destinations[i].blockOffset <<8 ; 
     // go through mask and copy to buffer 
     for (int j = 0; j < 32; j++) 
     { 
      int joffset = blockOffset + j << 3; 
      int mask = packed_destinations[i].bitMask[j]; 
      if (mask & 1 << 0) 
       buffer[buffer_dest_index++] = n[joffset + 1<<0 ]; 
      if (mask & 1 << 1) 
       buffer[buffer_dest_index++] = n[joffset + 1<<1]; 
      if (mask & 1 << 2) 
       buffer[buffer_dest_index++] = n[joffset + 1<<2]; 
      if (mask & 1 << 3) 
       buffer[buffer_dest_index++] = n[joffset + 1<<3]; 
      if (mask & 1 << 4) 
       buffer[buffer_dest_index++] = n[joffset + 1<<4]; 
      if (mask & 1 << 5) 
       buffer[buffer_dest_index++] = n[joffset + 1<<5]; 
      if (mask & 1 << 6) 
       buffer[buffer_dest_index++] = n[joffset + 1<<6]; 
      if (mask & 1 << 7) 
       buffer[buffer_dest_index++] = n[joffset + 1<<7]; 
     }; 

    } 


} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    for (int i = 0; i < 32; ++i) 
    { 
     packed_destinations[0].bitMask[i] = 0x0f; 
     packed_destinations[1].bitMask[i] = 0x04; 
    } 
    packed_destinations[1].blockOffset = 1; 

    populate_buffer_from_vector(packed_destinations, N, 1); 
    process_array(buffer, W, 256); 

    int val = sum.m128i_i32[0] + 
     sum.m128i_i32[1] + 
     sum.m128i_i32[2] + 
     sum.m128i_i32[3]; 
    printf("sum is %d" , val); 
    printf("Press Any Key to Continue\n"); 
    getchar(); 
    return 0; 
} 

通常掩模用法是5-一些工作负荷为15%,这将是25-100%。

MASKMOVDQU是接近,但那么我们将不得不根据保存前面罩重新包装/ SWL ..

+1

如果您发布现有代码,它可能会有所帮助。 –

+0

在 – user1496062

+0

中放入了一些代码你的'process128'函数看起来坏了 - 它实际上并没有使用传递给它的参数? –

回答

1

一对夫妇的优化你的现有代码:

如果你的数据是稀疏的话,那就可能是之前测试的附加位添加的每个8位掩码值的额外的测试是一个好主意,即

 int mask = packed_destinations[i].bitMask[j]; 
     if (mask != 0) 
     { 
      if (mask & 1 << 0) 
       buffer[buffer_dest_index++] = n[joffset + 1<<0 ]; 
      if (mask & 1 << 1) 
       buffer[buffer_dest_index++] = n[joffset + 1<<1]; 
      ... 

其次你process128功能可以显着优化:

inline __m128i process128(const __m128i u, const __m128i s, const __m128i sum) 
{ 
    const __m128i vk1 = _mm_set1_epi16(1); 
    __m128i calc = _mm_maddubs_epi16(u, s); 
    calc = _mm_madd_epi16(v, vk1); 
    return _mm_add_epi32(sum, calc); 
} 

注意,以及减少6 SSE指令数到3,我也做了sum参数,以获得从全局变量任何依赖性远(它总是一个好主意,以避免全局变量,而不是仅用于良好的软件工程,还因为它们可以抑制某些编译器优化)。

看到您的代码档案(使用体面的取样分析器,而不是通过仪器)会很有趣,因为这将有助于优先进行任何进一步的优化工作。

+0

谢谢保罗。没有意识到全局抑制选择。也想知道现有的流行矢量vs某种掩盖的移动到reg然后删除空间via_mm256_shuffle_epi8 ..单个字节移动1000s使我紧张,但应该是1级缓存将配置文件,但有很多代码来重写和提供一些结构。当前的代码是100%c#将繁重的转换转换为c。没有绝对的表现,但更快的神经元我可以使用,提高准确性。如果我可以从70%到85%,这将是一个巨大的胜利。 – user1496062

+0

确定 - 在尝试完成之后再回来,并用最新的代码,基准和配置文件提出一个新问题,我们可以看到进一步的优化是可能的。 –