【问题标题】:c++: Is table lookup vectorizable for small lookup-tablec ++:对于小型查找表,表查找是否可向量化
【发布时间】:2015-11-26 06:54:27
【问题描述】:

我想用 SIMD 内部函数对以下 sn-p 代码进行矢量化,这可能吗?

unsigned char chain[3][3] = { 
            3,  2, 1,    //  y    --> x
            4, -1, 0,    //  | 
            5,  6, 7     //  |
            };           //  v    

std::vector<int> x;
std::vector<int> y;    
//initialize x, y

std::vector<int> chain_code(x.size());

for(std::size_t i = 0; i < x.size(); ++i
     chain_code[i] = chain[x[i]][y[i]];    

编辑:

支持:SSE - SSE4.2 和 AVX

架构师:Sandy Bridge i5 2500

【问题讨论】:

  • 是的,可以使用收集说明。
  • 大多数 SIMD 架构都有permute 指令,可用于从多达 16 个甚至 32 个元素的表中进行快速查找。请指定您所针对的 CPU 架构和 SIMD 指令集以获得更具体的答案。
  • 这怎么可能?
  • 首先查看_mm_shuffle_epi8。还可以考虑将所有类型设为 8 位整数,这将节省大量打包/拆包。我希望有人会在适当的时候提供更完整的答案,但如果没有,我会在有更多时间时进一步扩展。
  • 考虑将整个数组打包成一个整数,然后进行寄存器内查找。不幸的是,您的数据包含 -1,否则您可以将其打包为 9 个适合 32 位整数的 3 位元素。寄存器内查找意味着您通过右移访问相关元素,然后使用 AND 进行屏蔽,这些都是可向量化的操作。

标签: c++ x86 sse simd avx


【解决方案1】:

如果您将xychain_node 设为 8 位整数(而不是 32 位整数),那么您可以一次处理 16 个值。 以下是使用 SSSE3 的代码:

std::vector<uint8_t> x;
std::vector<uint8_t> y;    
...
int n = x.size();
std::vector<uint8_t> chain_code(n);

//initialize table register
__m128i table = _mm_setr_epi8(
    chain[0][0], chain[0][1], chain[0][2], 99,
    chain[1][0], chain[1][1], chain[1][2], 99,
    chain[2][0], chain[2][1], chain[2][2], 99,
    99, 99, 99, 99
);

int b = (n / 16) * 16;
for (int i = 0; i < b; i += 16) {
    //load 16 X/Y bytes
    __m128i regX = _mm_loadu_si128((__m128i*)&x[i]);
    __m128i regY = _mm_loadu_si128((__m128i*)&y[i]);
    //shift all X values left by 2 bits (as 16-bit integers)
    __m128i regX4 = _mm_slli_epi16(regX, 2);
    //calculate linear indices (x * 4 + y)
    __m128i indices = _mm_add_epi8(regX4, regY);
    //perform 16 lookups
    __m128i res = _mm_shuffle_epi8(table, indices);
    //store results
    _mm_storeu_si128((__m128i*)&chain_code[i], res);
}
for (int i = b; i < n; i++)
    chain_code[i] = chain[x[i]][y[i]];

这段代码的完整版本是here。生成的程序集非常简单(MSVC2013 x64):

movdqu  xmm1, XMMWORD PTR [rdi+rax]
movdqu  xmm0, XMMWORD PTR [rax]
psllw   xmm1, 2
paddb   xmm1, xmm0
movdqa  xmm0, xmm6
pshufb  xmm0, xmm1
movdqu  XMMWORD PTR [rsi+rax], xmm0

附:我猜你会遇到std::vector 容器的各种性能问题。也许未对齐的访问不再昂贵,但是用零填充向量肯定会发生。而且它可能比矢量化代码花费更多的时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    • 2011-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多