4

我对 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) 数组。由于我无法定义的原因,这对我来说似乎是错误的。有人对更好的方法有任何建议吗?

谢谢!

4

4 回答 4

8

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

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

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

于 2010-10-19T20:29:39.783 回答
2

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

于 2010-10-19T15:14:12.050 回答
2

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

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

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

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

于 2010-10-19T20:11:26.287 回答
0

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

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

然后根据需要分配字节 玩转
联合想法,也许用匿名结构替换 unsigned char ch[8]?也许你可以从这里
得到更多的想法

于 2010-10-19T15:28:45.377 回答