【问题标题】:if/else statement in SSE intrinsicsSSE 内在函数中的 if/else 语句
【发布时间】:2011-09-11 12:55:54
【问题描述】:

我正在尝试使用 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 语句,但我怀疑有一个聪明的技巧。有没有人知道如何做到这一点?

(除此之外,我的优化可能很差 - 任何关于它的建议或更正都会受到最大的同情)

【问题讨论】:

  • 如果循环不是很大,那么你应该尽量保持外循环小于内循环。这将导致内部循环的初始化较少(即它应该是 n &lt; 8k &lt; 64
  • for 语句使用分号而不是逗号来分隔其组成部分。
  • @Fred,哎呀,当我复制到编辑器时出错,已更正。谢谢。
  • 难道您不能直接使用称为“复制粘贴”的传统方法,而不是自己重新输入代码吗?堆栈溢出编辑器确实支持 Ctrl+V,你知道 ;)
  • 这复制粘贴的东西听起来是个高端技术,估计不太靠谱。当早期采用者对其进行彻底测试时,我将使用它。

标签: c++ if-statement sse intrinsics mmx


【解决方案1】:

您可以完全摆脱 if/else。早在我编写 MMX 汇编代码的时候,那是一种常见的编程活动。让我从对“假”陈述的一系列转换开始:

c >>= 1;

c = c >> 1;

c = 0 ^ (c >> 1);

我为什么要引入异或?因为在“真”语句中也可以找到异或:

c = constant ^ (c >> 1);

注意相似之处?在“真”部分,我们与一个常数异或,而在假部分,我们与零异或。

现在我将向您展示整个 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 表示“所有位设置”。

【讨论】:

    【解决方案2】:

    SSE 的想法是构建两个结果,然后将结果混合在一起。

    例如:

    __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 );
    

    另外注意,这不是完整的代码,只是为了演示原理。

    【讨论】:

      【解决方案3】:

      高效计算 CRC 的第一步是使用比位更宽的基本单位。请参阅here,了解如何按字节执行此字节的示例。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-17
        • 2019-02-28
        • 1970-01-01
        • 2012-04-21
        • 1970-01-01
        • 2021-11-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多