SSE 添加的字符
Posted
技术标签:
【中文标题】SSE 添加的字符【英文标题】:SSE additions of chars 【发布时间】:2014-04-15 00:55:47 【问题描述】:我有一个包含 0 或 1 的 16 个字符的向量,我想使用 SSE 添加每 4 个不重叠的元素。
没有矢量化的简化版代码如下所示
char a[16]=1,0,0,1 ,0,0,1,0, 0,1,0,0, 0,0,0,1;
char sum1 = a[0] + a[1] + a[2] + a[3];
char sum2 = a[4] + a[5] + a[6] + a[7];
char sum3 = a[8] + a[9] + a[10] + a[11];
char sum4 = a[12] + a[13] + a[14] + a[15];
在我的应用程序中,向量的长度远大于 16,但它始终是 16 的倍数。我使用其他 SSE 逻辑运算获得该向量,这些运算为我提供了良好的加速,所以我想知道我是如何可以矢量化这些添加。下面是完整代码,其中 vec1、vec2 和 vec3 具有相同的长度 n(16 的倍数),向量计数为 n/4。
void myfunc( const char *vec1, const char *vec2, char *vec3, int *counts, int n)
__m128i *r1 = (__m128i*)vec1;
__m128i *r2 = (__m128i*)vec2;
char *a = vec3;
char temp[16] __attribute__ ((aligned (16)));
for ( int i = 0; i < n; i+=16, r1++, r2++, a+=16 )
_mm_store_si128((__m128i*)a, _mm_and_si128(*r1, *r2));
_mm_store_si128((__m128i*)temp, _mm_or_si128(*r1, *r2));
char size = a[0]+a[1]+a[2]+a[3];
if( size == 0 )
memcpy(a, temp, 4*sizeof(char));
counts[k]++;
k++;
size = a[4]+a[5]+a[6]+a[7];
if( size == 0 )
memcpy(a+4, temp+4, 4*sizeof(char));
counts[k]++;
k++;
size = a[8]+a[9]+a[10]+a[11];
if( size == 0 )
memcpy(a+8, temp+8, 4*sizeof(char));
counts[k]++;
k++;
size = a[12]+a[13]+a[14]+a[15];
if( size == 0 )
memcpy(a+12, temp+12, 4*sizeof(char));
counts[k]++;
k++;
任何帮助将不胜感激。
【问题讨论】:
您希望如何打包输出 sum1..sum4? uint8? uint32? @Nayuki 我只需要测试这些变量是否等于 0。在这种情况下,它们是字符(在我的第一篇文章中是 int) 这种情况下,可以测试每个32位字是否为0。当且仅当32位字为0时,4个字节之和为0。 避免将char
用于非文本数据。 char
可以是有符号或无符号的,具体取决于编译器甚至编译器选项。更好的选择是使用int8_t
或uint8_t
中的stdint.h
/cstdint
。
如果您的字符只有 0x00 或 0x01,您还可以在每组四个字符上使用 __popcnt
内在函数,重新解释为 unsigned int
。
【参考方案1】:
您可以比较整数,而不是比较字节。将来自a
、temp
和counts
的四个整数加载到SSE 寄存器中(在下面的代码中将它们称为a4
、tmp4
和counts4
)。然后您可以使用 SSE 一次处理四个整数。这假设 counts 是一个 int32 数组。
例如,假设 a4 = 0,3,0,4,counts4 = 1,2,3,4,tmp4 = 5,6,7,8。在下面的代码中
test
将是 -1, 0, -1, 0。从计数中减去它得到计数 = 2,2,4,4。 test
与 tmp4
的逻辑与是 5,0,7,0。将其添加到 a4 得到 a4 = 5,3,7,4。这应该可以满足您的需求。
for ( int i = 0; i < n; i+=16, r1++, r2++, a+=16, k+=4 )
_mm_store_si128((__m128i*)a, _mm_and_si128(*r1, *r2));
_mm_store_si128((__m128i*)temp, _mm_or_si128(*r1, *r2));
__m128i a4 = _mm_load_si128((__m128i*)a);
__m128i tmp4 = _mm_load_si128((__m128i*)tmp);
__m128i counts4 = _mm_load_si128((__m128i*)&counts[k]);
__m128i test = _mm_cmpeq_epi32(_mm_set1_epi32(0), a4);
a4 = _mm_add_epi32(a4, _mm_and_si128(tmp4,test));
counts4 = _mm_sub_epi32(counts4, test);
_mm_store_si128((__m128i*)a, a4);
_mm_store_si128((__m128i*)counts, counts4);
【讨论】:
这仍然有一些冗余存储 -> 负载。但是,是的,这里的主要优化是,如果字节元素是无符号的,则测试整个 32 位的非零就足够了。以上是关于SSE 添加的字符的主要内容,如果未能解决你的问题,请参考以下文章