【问题标题】:How To Store Values In Non-Contiguous Memory Locations With SSE Intrinsics?如何使用 SSE 内在函数将值存储在不连续的内存位置?
【发布时间】:2011-04-27 13:22:31
【问题描述】:

我对 SSE 非常陌生,并且使用内部函数优化了一段代码。我对操作本身很满意,但我正在寻找一种更好的方法来编写结果。结果以三个_m128i 变量结束。

我想要做的是将结果值中的特定字节存储到非连续的内存位置。我目前正在这样做:

__m128i values0,values1,values2;

/*Do stuff and store the results in values0, values1, and values2*/

y[0]        = (BYTE)_mm_extract_epi16(values0,0);
cb[2]=cb[3] = (BYTE)_mm_extract_epi16(values0,2);
y[3]        = (BYTE)_mm_extract_epi16(values0,4);
cr[4]=cr[5] = (BYTE)_mm_extract_epi16(values0,6);

cb[0]=cb[1] = (BYTE)_mm_extract_epi16(values1,0);
y[1]        = (BYTE)_mm_extract_epi16(values1,2);
cr[2]=cr[3] = (BYTE)_mm_extract_epi16(values1,4);
y[4]        = (BYTE)_mm_extract_epi16(values1,6);

cr[0]=cr[1] = (BYTE)_mm_extract_epi16(values2,0);
y[2]        = (BYTE)_mm_extract_epi16(values2,2);
cb[4]=cb[5] = (BYTE)_mm_extract_epi16(values2,4);
y[5]        = (BYTE)_mm_extract_epi16(values2,6);

其中ycbcr 是字节(unsigned char) 数组。由于我无法定义的原因,这对我来说似乎是错误的。有人对更好的方法有什么建议吗?

谢谢!

【问题讨论】:

  • 我认为您正在寻找 _mm_packXxx 内在函数之一。
  • 我看了那些,但似乎没有一个能做我想做的事。我再去看看,谢谢。

标签: c sse intrinsics sse2


【解决方案1】:

我不具体了解 SSE,但通常矢量化单元的全部意义在于,只要数据遵循特定的对齐和格式,它们可以非常快速地运行。因此,您需要以正确的格式和对齐方式提供和提取数据。

【讨论】:

    【解决方案2】:

    您可以尝试使用联合来提取字节。

    union
    {
        float value;
        unsigned char ch[8];
    };
    

    然后根据需要分配字节
    玩转 union-idea,也许用匿名结构替换 unsigned char ch[8]?
    或许你可以从here获得更多想法

    【讨论】:

    • 我不知道;最简单有效的解决方案每次都会得到我的投票
    • 除非在这种情况下,它会导致巨大的 load-hit-store 停顿。
    • @Crashworks:上面的好答案/解释(+1)我可能误解了原始问题(看起来他想要应用程序本地内存缓冲区中的值)
    【解决方案3】:

    SSE 没有您需要的分散/收集功能,尽管这可能会出现在未来的 SIMD 架构中。

    正如已经建议的那样,您可以使用联合,例如:

    typedef union
    {
        __m128i v;
        uint8_t a8[16];
        uint16_t a16[8];
        uint32_t a32[4];
    } U128;
    

    理想情况下,这种操作只发生在任何关键循环之外,因为与对连续数据元素的直接 SIMD 操作相比,它的效率非常低。

    【讨论】:

      【解决方案4】:

      你基本上不能——SSE 没有分散存储,它的设计都是围绕在连续数据流上进行矢量化工作的想法而设计的。实际上,制作 SIMD 所涉及的大部分工作是重新排列您的数据,使其连续且可矢量化。所以最好的办法是重新排列你的数据结构,这样你就可以一次写入 16 个字节。不要忘记,您可以在将 SIMD 向量中的组件提交到内存之前对其进行重新排序。

      如果做不到这一点,PEXTRW op(_mm_extract_epi16 内在函数)几乎是从 SSE 寄存器中拉出短路并将其存储到整数寄存器中的唯一方法。您可用的另一种方法是使用解包和随机播放操作(_mm_shuffle_ps 等)将数据旋转到寄存器的低位字,然后 MOVSS/_mm_store_ss() 一次将低位字存储到内存中.

      您可能会发现使用联合,或在 SSE 和通用寄存器之间移动数据,将提供非常差的性能,因为一个微妙的 CPU 实现细节称为 load-hit-store 停顿。基本上,没有直接的方法在寄存器类型之间移动数据。处理器必须先将 SSE 数据写入内存,然后再将其读回 GPR。在许多情况下,这意味着它必须暂停加载操作并等待存储清除,然后才能运行任何进一步的指令。

      【讨论】:

      • 感谢您的解释。当您说我可以重新排序 SIMD 向量中的组件时,您指的是解包和随机播放操作吗?
      • 别忘了你也可以使用蒙面移动/洗牌。例如,如果您有一个 SSE 寄存器 a=ABCD> 想要写入内存行 XYZW,这样只有第一个和第三个单词被覆盖(在内存中产生 AYCW),那么您可以加载将目标行放入寄存器 b=XYZW,将它们与掩码移动组合到 c=(ABCD & 1010) | (XYZW & 0101)=AYCW,然后将 c 保存回内存。
      • 在 AVX-512 (Xeon Phi) 中引入了分散操作,但此代码不需要分散操作,因为存储值的索引在编译时是已知的。现在(与撰写问题的时间相反)我们知道 AVX-512 中的分散操作(例如 _mm512_i32scatter_epi32)仅用于存储 32 位或 64 位整数。
      猜你喜欢
      • 2015-10-25
      • 2014-03-29
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 2016-03-13
      • 2011-10-03
      • 1970-01-01
      • 2020-09-26
      相关资源
      最近更新 更多