只是为了总结并结束问题。
我们讨论了从 [0..15] 中提取索引 i 处的 3 个选项_m128i sse
其中 i 无法在编译时简化为文字:
1) 开关&_mm_extract_epi8
: 有一个switch
在 i 和 [0..15] 中每个 i 的情况下,执行_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
: use _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
联盟黑客:
平均值:1078.61
标准差:2.87131
从位置 0 开始进行混音和提取:
平均值:1079.32
标准差:2.69808
差异是微乎其微的。我还没有机会查看生成的汇编。不过,看到差异可能会很有趣。目前我无法发布基准测试的完整代码,因为它包含非公开来源。如果我有时间,我会提取这些并发布来源。