c - 在只有 3 个颜色分量的浮点像素上使用 SSE

标签 c gcc assembly sse simd

我正在创建一个结构来存储图像中的单个 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/

相关文章:

c - Altivec编译错误

c - C 的 std::vector 替代品

C编程; GCC 在运行时崩溃

c - 解释汇编代码

macos - 为什么这段汇编代码会抛出段错误?

c - ISO C 是否允许分配的内存在程序终止后挂起?

c - gcc 函数属性会影响函数指针吗?

c++ - 有什么方法可以将我的程序与 Wine 编译的部分链接起来吗?

assembly - int 21h ah 02h 由于某种原因不起作用

c - 如何将 LISP 代码翻译或转换为 C 代码?