是否有 SIMD 指令来加快校验和计算?
Posted
技术标签:
【中文标题】是否有 SIMD 指令来加快校验和计算?【英文标题】:Are there SIMD instructions to speed up checksum calculations? 【发布时间】:2011-07-12 22:22:02 【问题描述】:我将不得不编写一个非常基本的校验和函数,例如:
char sum(const char * data, const int len)
char sum(0);
for (const char * end=data+len ; data<end ; ++data)
sum += *data;
return sum;
这很简单。现在,我应该如何优化它? 首先,我可能应该使用一些带有 lambda 或类似的 std::for_each :
char sum2(const char * data, const int len)
char sum(0);
std::for_each(data, data+len, [&sum](char b)sum+=b;);
return sum;
接下来,我可以使用多个线程/内核来汇总块,然后将结果相加。我不会把它写下来,而且我担心创建线程(或者无论如何从池中获取它们),然后切割数组,然后调度所有东西等的成本不会很好,因为我主要会计算小数组的校验和,大部分是 10-100 字节,很少达到 1000。
但我真正想要的是更低级别的东西,一些 SIMD 东西,它们可以对 128b 寄存器上的字节求和,或者可能在两个寄存器之间独立地求和字节而不携带进位,或两者兼而有之。
那里有这样的东西吗?
注意:这是实际的过早优化,但它很有趣,那么到底是什么?
编辑:我仍然需要一种方法来总结 SSE 寄存器中的所有字节,这比
char ptr[16];
_mm_storeu_si128((__m128i*)ptr, sum);
checksum += ptr[0] + ptr[1] + ptr[2] + ptr[3] + ptr[4] + ptr[5] + ptr[6] + ptr[7]
+ ptr[8] + ptr[9] + ptr[10] + ptr[11] + ptr[12] + ptr[13] + ptr[14] + ptr[15];
【问题讨论】:
我没有实际证据证明这一点,但我怀疑std::for_each
版本会比手动方法慢...
我假设您希望英特尔 opps 在这里?
@Mike 视情况而定,小数据包实际上速度较慢,但大数据包速度更快。我用 100b 和 900b 测试过,我不知道我的设置的限制在哪里。我想创建对象的成本与此有关。不出所料,SSE 在这两种规模上都击败了两者。
@Gabriel:这不是过早的优化。例如,在我的项目中,这是一个最慢的地方。起初我将它展开 4,这给了很多提升,但它仍然是最慢的。所以我不得不使用 SSE 内在函数重写它。这很有趣! :)
【参考方案1】:
是的,MMX指令集中有这样的指令,叫做“Packed ADD”:
_mm_add_pi8
在 Visual C++ 中
__builtin_ia32_paddb
在 gcc 中
在SSE2指令集中:
_mm_add_epi8
在 Visual C++ 中
__builtin_ia32_paddb128
在 gcc 中
编辑:添加部分和的更快方法:
__m128i sums;
sums = _mm_add_epi8(sums, _mm_srli_si128(sums, 1));
sums = _mm_add_epi8(sums, _mm_srli_si128(sums, 2));
sums = _mm_add_epi8(sums, _mm_srli_si128(sums, 4));
sums = _mm_add_epi8(sums, _mm_srli_si128(sums, 8));
checksum += _mm_cvtsi128_si32(sums);
【讨论】:
现在,在给定上述循环的情况下,任何编译器都足以生成正确的代码吗? 我无法想象大多数编译器会费心为第一个循环发出 SSE。尝试从任意指针加载很烦人——编译器不知道目标是否对齐,是否可以加载 SSE 大小的块,等等。 这里不需要使用编译器名称。 GCC 以及 Visual C++ 支持标准的 MMX/SSE 扩展头和函数。我不是 MMX/SSE 用户,但请检查例如<mmintrin.h>
的原型。【参考方案2】:
看看 _mm_add_ps。同时添加 128 位连续块。您需要对数组进行零填充或处理最后几个非 SIMD 样式。
【讨论】:
以上是关于是否有 SIMD 指令来加快校验和计算?的主要内容,如果未能解决你的问题,请参考以下文章