【问题标题】:Best way to compute max mask of sse var计算 sse var 的最大掩码的最佳方法
【发布时间】:2015-05-19 12:09:55
【问题描述】:

(我只对前 3 个组件感兴趣)

例如:
[ 1 2 3 ? ] 应该产生[ 0 0 -1 ? ]

此外,只设置一个“位”很重要,这样:
[ 1 2 2 ? ] 不应产生 [ 0 -1 -1 ? ]
而是
[ 0 -1 0 ? ][ 0 0 -1 ? ](不管是哪一个)

后面的(坏的)解决方案是可能的,例如通过提取水平最大值并与原始解决方案进行比较:

__m128 abcd; // input
__m128 ccac           = _mm_shuffle_ps(abcd, abcd, 0x8A);
__m128 abcd_ccac      = _mm_max_ps(abcd, ccac);
__m128 babb           = _mm_shuffle_ps(abcd, abcd, 0x51);
__m128 abcd_ccac_babb = _mm_max_ps(abcd_ccac, babb);
__m128 mask           = _mm_cmpeq_ps(abcd, abcd_ccac_babb);

也许一些按位运算来消除重复的设置位?

更新:

跟进,我已经提出了另一个(糟糕的)解决方案。
关键是将每个组件与另一个组件进行比较,避免等式(在其他地方没有 a >= bb >= a)。

a > b & a >= c
b > c & b >= a
c > a & c >= b

屈服:

([ a b c ? ] > [ b c a ? ]) & ([ a b c ? ] >= [ c a b ? ])

在代码中:

__m128 abcd; // input
__m128 bcad = _mm_shuffle_ps(abcd, abcd, 0xC9);
__m128 gt   = _mm_cmpgt_ps(abcd, bcad);
__m128 cabd = _mm_shuffle_ps(abcd, abcd, 0xD2);
__m128 ge   = _mm_cmpge_ps(abcd, cabd);
__m128 mask = _mm_and_ps(gt, ge);

[ x x x ? ] 的情况下失败(产生[ 0 0 0 ? ])。

接近:-)

有什么想法吗?

更新:

我现在正在使用以下解决方案:

__m128  abcd; // input
__m128  bcad = _mm_shuffle_ps(abcd, abcd, 0xC9);
__m128  gt   = _mm_cmpgt_ps(abcd, bcad);
__m128  cabd = _mm_shuffle_ps(abcd, abcd, 0xD2);
__m128  ge   = _mm_cmpge_ps(abcd, cabd);
__m128  and  = _mm_and_ps(gt, ge);
__m128i bits = _mm_setr_epi32(_mm_movemask_ps(and), -1, -1, -1);
__m128i dirt = _mm_cmpeq_epi32(bits, _mm_setzero_si128());
__m128i mask = _mm_or_si128(dirt, _mm_castps_si128(and));

【问题讨论】:

  • 我现在看不到任何有效或优雅的方法 - 到目前为止,我所能想到的就是使用 _mm_movemask_psmask 中提取 4 MS 位,然后处理这将获得一个唯一的单个位值,然后您可以在需要时将其转换回掩码向量。
  • 您必须按顺序执行此操作还是可以独立执行此操作,例如,如果您有一个像 {{1,2,3},{4,5,6},... } 你能独立找到 {1,2,3} 和 {4,5,6} 的最大值吗?如果您可以为不同的数字集独立执行此操作,那么使用 SIMD 会更有效。
  • @PaulR _mm_movemask_ps 作为一种按位运算的方法非常受欢迎。
  • @Zboson 不幸的是,我仅限于对同一个 SIMD 变量进行整个操作,无论与垂直操作相比效率低下

标签: c++ assembly x86 bit-manipulation sse


【解决方案1】:

我没有对此进行测试,但我相信它只会在最大值的第一个(最高阶)出现中为您提供 -1:

__m128 abcd; // input
__m128 ccac = _mm_shuffle_ps( abcd,abcd,0x8A );
__m128 abcd_ccac = _mm_max_ps( abcd,ccac );
__m128 babb = _mm_shuffle_ps( abcd,abcd,0x51 );
__m128 abcd_ccac_babb = _mm_max_ps( abcd_ccac,babb );
__m128 mask = _mm_cmpeq_ps( abcd,abcd_ccac_babb );

// set the '?' position in mask to zero
mask = _mm_blend_ps( mask,_mm_setzero_ps(),0x08 );
// shift mask left 32 bits shifting in zeros
__m128 maskSrl32 = _mm_shuffle_ps( mask,mask,_MM_SHUFFLE( 3,1,0,3 ) );
// shift mask left 64 bits shifting in zeros
__m128 maskSrl64 = _mm_shuffle_ps( mask,mask,_MM_SHUFFLE( 3,0,3,3 ) );
// andnot the shifted masks with mask
// in doing so, the higher order set bits will suppress any set bits which follow
mask = _mm_andnot_ps( maskSrl32,mask );
mask = _mm_andnot_ps( maskSrl64,mask );
// select -1 using the final mask
__m128 result = _mm_and_ps( mask,_mm_set1_ps( -1.0f ) );

反转移动方向以在最低阶最大位置产生 -1。

【讨论】:

  • 这对于常见示例失败:[ 4, 3, 2 x ](生成 [ 0 0 0 x ])。 (这个“?”位置的目的是什么?)
  • @GolanRoss 我修复了例程并对其进行了测试。它现在应该可以正常工作了(我最初在脑海中混淆了矢量元素的顺序)。这 '?' position 是你说不感兴趣的元素,需要清空,方便元素的移动。
  • 有趣的选择 blend_ps 以选择性地归零,而不是带有掩码的 and_ps。虽然它比我更有效:单个 uop,并且可以在 Intel SnB / Haswell 上的 2 或 3 个端口上运行(分别)。但是,您可以使用_mm_bslli_si128(src, byte_count) 而不是shuffle_ps 免费获得归零(您必须转换为__m128i。我认为转发来自cmpeq_ps 的结果不会有额外的延迟(绕过延迟)转换为整数 shuffle 而不是 FP shuffle。(pslldq 在 shuffle 单元上运行,与 shufps 相同)。
  • 如果您仍然使用整数洗牌,您也可以并且应该留在整数域中进行andnot 操作。使用最终的and_ps 将确保在 FP ALU 操作(如加法或乘法)中使用结果时没有绕过延迟。
猜你喜欢
  • 1970-01-01
  • 2015-04-24
  • 2021-10-23
  • 2011-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-01
  • 1970-01-01
相关资源
最近更新 更多