在仅具有3个颜色分量的浮点像素上使用SSE

cha*_*255 5 c assembly gcc sse simd

我正在创建一个结构来在图像中存储单个RGB像素.

struct Pixel
{
    // color values range from 0.0 to 1.0
    float r, g, b;
}__attribute__((aligned(16));
Run Code Online (Sandbox Code Playgroud)

我想使用128位SSE指令来执行添加,乘法等操作.这样我就可以同时对所有3个颜色通道执行操作.因此,我的SSE寄存器中的第一个打包浮点数将是红色,然后是绿色,然后是蓝色,但我不太确定将进入我的第四个寄存器.我真的不在乎额外的32位填充中的哪些位.当我将一个像素加载到SSE寄存器中时,我会想象它包含零或垃圾值.这有问题吗?我应该添加第四个alpha通道,即使我真的不需要吗?我认为这是一个问题的唯一方法是,如果我除以一个像素,并且在第四个点中有一个零值,或者我正在使用一个负数的根等等.

Pet*_*des 9

对于未初始化的值,整数运算完全没有问题,因为延迟从不依赖于数据.浮点不同.一些FPU减慢了非正规数,NaN和无穷大(在任何一个向量元素中).

在使用非正常输入/输出和FP下溢/溢出进行数学运算时,英特尔Nehalem和更早的速度会慢下来.Sandybridge有一个很好的FPU,可以为任何输入提供快速加/ (根据Agner Fog的指令表),但乘法仍然可以放慢速度.

使用零可以添加/ sub/multiply,但是可能代表NaN或其他东西的未初始化垃圾可能存在问题.

除非你没有除以零,否则要小心.这甚至可能引发FPU异常,具体取决于硬件设置.

所以是的,保持未使用的元素归零可能是一个好主意.根据你最初的生成方式,这可能相当便宜.(例如,movd/pinsrd/pinsrd(或insertps)将三个32位元素放入一个向量中,初始movd将高位置为96b.)

一种解决方法可能是在第4个元素中存储蓝色通道的第二个副本.(或者最方便在那里洗牌的东西.)你可以用movsldup(SSE3)/ 加载向量movlps.之后movsldup,您的注册将成立{ b b r r }. movlps将重新加载较低的64位,所以你有{ b b g r }.(这相当于movsd,BTW.)或者如果shuffle端口不如加载端口忙,则执行一次16B加载然后shufps.(movsldup在Intel CPU上是一个在加载端口上运行的单个uop,即使它内置了重复项.)

另一种选择是将像素打包成12个字节,因此16B负载将获得下一个像素的一个分量.根据你正在做的事情,重叠商店会破坏下一个像素的一个元素可能会或可能不会.在存储当前值之前加载下一个像素可以解决某些操作的问题.缓存或带宽限制非常容易,因此以偶然的缓存行拆分加载/存储的低成本节省1/4空间可能是值得的.

  • Sandybridge没有完全消除SSE/AVX低于正常的失速(它们在Broadwell的某些情况下仍然存在),尽管在一些最常见的情况下它确实消除了它们.除以零在SSE/AVX上是安全的; 它将设置一个标志,但它不会陷入默认的浮点环境.从下一个像素打包和加载的风险是,如果您的像素数不是4的倍数,则缓冲区末尾的最终加载读取(可能未映射)您无法控制的数据.(抱歉将一堆不相关的笔记打包成评论). (3认同)
  • Agner Fog最初只测试了加法/减法的非正规数,它们在Sandy Bridge上很快.直到有人[注意到这里](http://stackoverflow.com/questions/9314534/why-does-changing-0-1f-to-0-slow-down-performance-by-10x/9314926#评论11910304_9314926),这个故事是否一直传播到Agner的博客,在那里他重新测试并发现乘法仍然容易受到Sandy Bridge上的非正规影响. (2认同)