我正在创建一个结构来存储图像中的单个 RGB 像素。
struct Pixel
{
// color values range from 0.0 to 1.0
float r, g, b;
}__attribute__((aligned(16));
我想使用 128 位 SSE 指令来执行加法、乘法等操作。这样我可以同时对所有 3 个颜色 channel 执行操作。因此,我的 SSE 寄存器中的第一个打包 float 将是红色,然后是绿色,然后是蓝色,但我不太确定第四个寄存器中的内容。我真的不在乎额外的 32 位填充中有哪些位。当我将一个像素加载到 SSE 寄存器时,我会想象它包含零值或垃圾值。这有问题吗?我是否应该添加第四个 alpha channel ,即使我真的不需要一个?我认为这是一个问题的唯一方式是,如果我除以一个像素并且在第四个位置有一个零值,或者我正在取一个负数的根,等等。
最佳答案
整数操作对于未初始化的值完全没有问题,因为延迟从不依赖于数据。 float 不同。某些 FPU 在处理非正规数、NaN 和无穷大(在任何一个 vector 元素中)时会变慢。
Intel Nehalem 和更早版本在使用非规范输入/输出和 FP 下溢/溢出进行数学运算时速度会大大降低。 Sandybridge 有一个很好的 FPU,可以为任何输入快速添加/订阅(根据 Agner Fog's instruction tables ),但是 multiply can still slow down .
加法/减法/乘法对零没问题,但对于可能代表 NaN 或其他东西的未初始化垃圾可能会出现问题。
除法时要小心,不要除以零。这甚至可能引发 FPU 异常,具体取决于硬件设置。
所以是的,将未使用的元素置零可能是个好主意。取决于您最初生成事物的方式,这可能很容易实现。 (例如 movd/pinsrd/pinsrd(或 insertps)将三个 32 位元素放入一个 vector 中,初始 movd 将高位 96b 归零。)
一种解决方法是在第四个元素中存储蓝色 channel 的第二个副本。 (或任何在那里洗牌最方便的东西。)您可以使用 movsldup
(SSE3)/movlps
加载 vector 。在 movsldup
之后,您的寄存器将保存 { b b r r }
。 movlps
将重新加载较低的 64 位,因此您将拥有 { b b g r }
。 (这等同于 movsd
,顺便说一句。)或者如果 shuffle 端口不如加载端口繁忙,则执行一个 16B 加载,然后进行 shufps。 (英特尔 CPU 上的 movsldup
是在加载端口上运行的单个 uop,即使它内置了复制。)
另一种选择是将您的像素打包成 12 个字节,这样 16B 的负载将获得下一个像素的一个组件。根据您正在做的事情,重叠存储会破坏下一个像素的一个元素可能会也可能不会。在存储当前像素之前加载下一个像素可以解决某些操作的问题。很容易受到缓存或带宽限制,因此以偶尔缓存行拆分加载/存储的小成本节省 1/4 空间可能是值得的。
关于c - 在只有 3 个颜色分量的浮点像素上使用 SSE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32933722/