3

我最近一直在使用 SSE 内在函数int _mm_extract_epi8 (__m128i src, const int ndx),根据参考“从索引选择的压缩整数数组元素中提取一个整数字节”。这正是我想要的。

但是,我通过 a _mm_cmpestrion a确定索引,该索引对_m128i具有显式长度的字符串数据执行打包比较并生成索引。该索引的范围是 0..16,其中 0..15 表示有效索引,16 表示未找到索引。现在要在索引位置提取整数,我想执行以下操作:

const int index = _mm_cmpestri(...);
if (index >= 0 && index < 16) {
  int intAtIndex = _mm_extract_epi8(..., index);
}

这给我们留下了 gcc (-O0) 编译器错误:

错误:选择器必须是 0..15 范围内的整数常量

解决此问题的一个讨厌的方法是switch在索引上设置一个并_mm_extract_epi8调用 0..15 范围内的每个索引。我的问题是是否有我看不到的更好/更好的方式。

更新:用-O3优化,没有编译错误;虽然仍然有 -O0 。

4

1 回答 1

3

只是为了总结和结束这个问题。

我们讨论了 3 个选项来提取 [0..15] 中索引 i 处的字节,_m128i sse其中 i 在编译时无法简化为文字:

1) 开关 & _mm_extract_epi8: 在 [0..15] 中的每个 i 都有一个switchover i 和一个 case _mm_extract_epi8(sse,i);像我现在一样工作是编译时文字。

2) Union hack:有一个union SSE128i { __m128i sse; char[16] array; },将其初始化为SSE128i sse = { _mm_loadu_si128(...) }并使用 访问索引 i 处的字节sse.array[i]

3) 将第 i 个元素打乱到位置 0 和_mm_extract_epi8:用于_mm_shuffle_epi8(sse,_mm_set1_epi8(i))将第 i 个元素打乱到位置 0;提取它_mm_extract_epi8(sse,0)

评估:我在 Intel Sandy Bridge 和 AMD Bulldozer 架构上对三个选项进行了基准测试。切换选项以微弱优势获胜。如果有人感兴趣,我可以发布更详细的数字和基准设置。

更新:评估 基准设置:解析 1GB 文件的每个字节。对于某些特殊字节,增加一个计数器。用于_mm_cmpistri查找特殊字节的索引;然后使用提到的三种方法之一“提取”字节,并区分计数器增加的情况。代码是使用 GCC 4.6 和-std=c++0x -O3 -march=native.

对于每种方法,基准测试在 Sandy Bridge 机器上运行 25 次。结果(以秒为单位的运行时间的平均值和标准偏差):

切换提取:平均值:1071.45 标准差:2.72006

Union hack:平均值:1078.61 标准差:2.87131

从位置 0 混洗和提取:平均值:1079.32 标准偏差:2.69808

差异是微不足道的。我还没有机会查看生成的 asm。不过,看到差异可能会很有趣。目前我无法发布基准测试的完整代码,因为它包含非公开来源。如果我有时间,我会提取这些并发​​布来源。

于 2012-10-26T09:24:03.220 回答