Lanczos SSE / AVX实施

时间:2015-12-10 23:15:45

标签: image-processing assembly sse avx lanczos

是否有人提供有关如何使用SSE / AVX(内部函数或汇编)实现Lanczos image resampling(升级和缩减)算法的任何提示?

我查看了一些C实现,但是有很多分支,我真的不知道,如何使用SSE / AVX巧妙地实现它。

示例 - 规范化的基数罪:

    var color = d3.scale.category20c();

如何为值x == 0返回1?在那些索引上,我在CMPPD之后有11 ... 11(真)。

另外,我正在为灰度,8位图片做这个,所以一个像素只有(0..255)。对质量的影响会使用浮动而不是双倍?是否可以在整个时间内使用u_int8并且根本不转换为实数(错误可能很大)?

1 个答案:

答案 0 :(得分:3)

如果您还不知道asm或SSE / AVX,一次学习一个可能会更容易。使用C / C ++内在函数编写矢量算法将比直接使用asm提供更便携的实现。 (编译为32对64位,以及Windows或其他所有内容,而不是需要2或4个不同的asm版本(或asm中的#ifdef等效宏)。

在编写C时查看编译器输出可能会有所帮助,以确保加载/存储按照您期望的方式发生,并且编译器由于别名/对齐而没有做任何臃肿代码的愚蠢(缺乏假设,或存储/生成常数。

调试矢量代码已经足够困难了(因为需要跟踪的状态要多得多,而且你必须通过改组来精神上跟踪事物)。

我首先找到可以向量化的C的一些部分,如果编译器已经不是自动向量化,并且在C中使用内在函数。那么一旦它工作,我可能会采用编译器输出和手 - 在编译器没有制作最佳代码的地方调整它。 (见http://agner.org/optimize/

至于将图像数据处理为float与int,如果你可以使用16位定点,那么它会更快(除非它需要更多指令)。请参阅my answer on another image-processing question关于使用浮动与定点的比较。

SSE中唯一的数学指令(超出基本的add / sub / mul / div)是sqrt。 Trig / log / exp是所有库函数。请注意,在英特尔的内在指南中,“指令”字段为空白,表示它映射到多个指令。只有英特尔的编译器甚至提供这些复合内在函数。

无论如何,您需要找到内联的sin实现,或者保存一些寄存器并进行函数调用。根据ABI(窗口或其他所有内容),某些或所有xmm寄存器可能被函数破坏。使用特定的sin实现可以让您知道它需要哪些寄存器,并且只会溢出它们。 (因为你是用asm编程的,所以你可以创建彼此了解更多的函数,而不是仅仅遵循ABI。)

如果您只需要calculate sin(x*PI),则可以制作自定义sin功能,从而省去了PI预乘的麻烦。由于sin chooses what algorithm to use based on the range of the input的理想实现,您可能无法获得精确到尾数最后一位的矢量化实现。幸运的是,你可能不需要它,所以google了SSE sin(x)实现。

SIMD向量中的条件通过比较来处理,所述比较使得元素的向量全为零或全为一。然后,您可以将AND或OR转换为其他向量。它适用于添加标识值为0的位置。 (x + 0 = x,因此您可以在将向量添加到累加器之前从向量中过滤掉一些元素)。如果你需要根据0 / -1的向量在两个源元素之间进行选择,你可以将AND / OR组合在一起,或者使用blendvps(变量混合打包标量)更快地完成相同的工作,而不是编译时常量混合)。

如果你想避免在一开始就避免计算慢除零,而不是通常只对一切进行计算,然后进行掩蔽/混合,这个想法就会分解。由于您希望结果在1时出现x == 0.0,因此您最好的选择是在计算任何{{1}之前将x的零元素设置为FLT_MIN * 16或其他内容}}。这样,你就可以避免除以零,并且除法的结果非常接近于1.如果你需要它得到恰好1.0f(并且没有sin(x*PI)/(x*PI)的值使{使用x实现{1}}然后你需要混合两次:一次在分子中,一次在分母中。 (将它们设置为相同的非零值。)

sin(x*PI) == x*PI

请注意,sin在AVX VEX编码版本中的谓词选择比在SSE版本中更广泛。