我正在尝试使用SSE内在函数优化一小段代码(我是关于该主题的完全初学者),但我对使用条件有点困惑。
我的原始代码是:
unsigned long c;
unsigned long constant = 0x12345678;
unsigned long table[256];
int n, k;
for( n = 0; n < 256; n++ )
{
c = n;
for( k = 0; k < 8; k++ )
{
if( c & 1 ) c = constant ^ (c >> 1);
else c >>= 1;
}
table[n] = c;
}
这段代码的目标是计算一个crc表(常量可以是任何多项式,它在这里不起作用),
我认为我的优化代码类似于:
__m128 x;
__m128 y;
__m128 *table;
x = _mm_set_ps(3, 2, 1, 0);
y = _mm_set_ps(3, 2, 1, 0);
//offset for incrementation
offset = _mm_set1_ps(4);
for( n = 0; n < 64; n++ )
{
y = x;
for( k = 0; k < 8; k++ )
{
//if do something with y
//else do something with y
}
table[n] = y;
x = _mm_add_epi32 (x, offset);
}
我不知道如何通过if-else语句,但我怀疑有一个聪明的伎俩。有人知道如何做到这一点吗?
(除此之外,我的优化可能非常差 - 任何有关它的建议或更正都会得到最大的同情)
答案 0 :(得分:12)
你可以完全摆脱if / else。回到我制作MMX汇编代码的日子,这是一个常见的编程活动。让我首先对“虚假”声明进行一系列转换:
c >>= 1;
c = c >> 1;
c = 0 ^ (c >> 1);
为什么我要介绍独家或?因为在“真实”声明中也可以找到 - 或者
c = constant ^ (c >> 1);
注意相似性?在“真实”部分中,我们用常数xor,在虚假部分中,我们xor为零。
现在,我将向您展示整个if / else语句的一系列转换:
if (c & 1)
c = constant ^ (c >> 1); // same as before
else
c = 0 ^ (c >> 1); // just different layout
if (c & 1)
c = constant ^ (c >> 1);
else
c = (constant & 0) ^ (c >> 1); // 0 == x & 0
if (c & 1)
c = (constant & -1) ^ (c >> 1); // x == x & -1
else
c = (constant & 0) ^ (c >> 1);
现在两个分支只在二进制的第二个参数中有所不同,并且可以从条件本身简单地计算,从而使我们能够摆脱if / else:
c = (constant & -(c & 1)) ^ (c >> 1);
免责声明:此解决方案仅适用于二进制架构,其中-1表示“所有位设置”。
答案 1 :(得分:2)
SSE的想法是建立两个结果,然后将结果混合在一起。
E.g。 :
__m128i mask = ...; // some way to build mask[n] = 0x1
__m128i constant = ...;
__m128i tmp_c = _mm_xor_si128( _mm_srli_epis32( c, 1 ), constant );
__m128i tmp_c2 = _mm_srli_epis32( c, 1 );
__m128i v = _mm_cmpeq_epi32( c, mask );
tmp_c = _mm_and_epi32( tmp_c, mask );
tmp_c2 = _mm_andnot_si128( mask, tmp_c2 );
c = _mm_or_si128( tmp_c, tmp_c2 );
// or in sse4_1
c = _mm_blendv_epi8( tmp_c, tmp_c2, mask );
旁边注意,这不是完整的代码,只是为了说明原理。
答案 2 :(得分:1)
有效计算CRC的第一步是使用比该位更宽的基本单元。有关如何每字节执行此字节的示例,请参阅here。