x265中satd8和sa8d的MIPS向量指令实现

摘要

satd8表示以4x4为单位进行哈达玛变换,sa8d则是以8x8为单位。需要优化的尺寸分别有8x8、16x16、32x32、64x64。基本思想相同,但实现上存在一定的差异。

正文

satd8

首先是satd8,x86给出了8x4的实现方法,和4x4类似,但它可以作为后续的基础。因为对于4x4的尺寸,一个128位向量寄存器需要两行元素进行填充。而对于8x4的尺寸,主要是宽大于等于8时,一个向量寄存器只存储一行中的部分或全部元素。计算的形式就可以固定下来,比如若宽度为16时,就可以以8为单位分别计算。这里讲解下8x4尺寸的实现。当前只考虑在MIPS下的实现。

首先取每行pixel(64b)置于向量寄存器的低64位。将相邻元素相减和相加并扩展到16位。然后将相减得到的低64位插入相加得到的结果的高64位中。得到

{ c o l 7 − c o l 6 , c o l 5 − c o l 4 , c o l 3 − c o l 2 , c o l 1 − c o l 0 , c o l 7 + c o l 6 , c o l 5 + c o l 4 , c o l 3 + c o l 2 , c o l 1 + c o l 0 } \{ col_7-col_6, col_5-col_4, col_3-col_2, col_1-col_0, col_7 + col_6, col_5 + col_4, col_3 + col_2, col_1+col_0\} {col7col6,col5col4,col3col2,col1col0,col7+col6,col5+col4,col3+col2,col1+col0}

其实 c o l 4 . . . c o l 7 col_4...col_7 col4...col7 c o l 0 . . . c o l 3 col_0...col_3 col0...col3都为4x4的基本单元,它们之间并没有什么连续的关系。

这之后同样是行间加减操作,完成列变换。之后取绝对值,然后向量交错。要达到一个效果:得到的两个寄存器,对应位置包括所有列元素。只要达到这一要求,怎么交错都可以。我仍是分别取出奇偶位置元素,然后奇偶交错存储。原本在同一寄存器中相邻的元素,现在变为在两个寄存器中对应位置。

然后两个寄存器对应位置取大值,相邻元素求和扩展至32位,最后32位元素求和得到结果。这里也体现了绝对值取max的好处,原本需要四个元素经过计算得到结果,使用这种方法后,将这个过程划分为两个步骤,两对元素分别计算,最后求和得到结果。两对元素可以独立操作,将计算所需数据集划分的非常小,两个向量寄存器的对应16位为一个数据集,数据集的构造也就非常简单,可以得到较高的并行度。

satd_8x4就是这样一个过程,但是编译后观察*.S中间文件,该尺寸并没有最大限度的使用向量寄存器,表示还有可以展开的空间。因此,尝试16x4和8x8的尺寸。8x8是肯定需要的,将上面的8x4和下面的8x4同时进行运算,达到的效果是基本上使用了所有的32个向量寄存器,已经是最大程度展开。16x4也是一样,将左边的8x4和右边的8x4同时计算。对于16x4的需求,考虑到访存的结构,遵从横向访问顺序,同一缓存行中的内容连续访问,直到使用结束,这里是以四个缓存行为一个单元。

16x16、32x32、64x64的尺寸都是以循环调用16x4的形式完成。16x4的函数设置为内联的形式。然后观察这些尺寸在编译中间文件*.S文件中的情况,基本正常。但是经过编译器优化后出现了几个 move.v向量赋值指令,可能编译器为了省着用寄存器吧,虽然会增加指令间的相关性,但不会造成太大的影响。

sa8d

sa8d是以8x8为单位完成哈达玛变换,这里简单讲述一下实现过程,最基本的8x8的尺寸。

前面其实和8x4差不都,同样构造出这样一个数据形式

${ col_7-col_6, col_5-col_4, col_3-col_2, col_1-col_0, $
$ col_7 + col_6, col_5 + col_4, col_3 + col_2, col_1+col_0}$

后续列变换由于是以8x8为单位,需要将8行元素相互加减运算完成。不同之处也就多了几次加减运算。

然后是行变换。在这里 c o l 4 . . . c o l 7 col_4...col_7 col4...col7 c o l 0 . . . c o l 3 col_0...col_3 col0...col3就存在关联了。而且上面说过绝对值取max,照上面的理论反推需要的数据。8列元素,划分为两个子数据集,简单的划分方法就是 c o l 4 . . . c o l 7 col_4...col_7 col4...col7四个元素为一组, c o l 0 . . . c o l 3 col_0...col_3 col0...col3为一组。一组四个元素,共有4种加减组合,最终明确得到2x4=8个数据。8个数据两两组合(需要包括所有 c o l 0 . . . c o l 7 col_0...col_7 col0...col7),绝对值取max得到第一步骤的结果。而要得到这8个数据,首先需要向量交错,这里使用pckod指令,分别取出两个源寄存器的奇数位,一个置于高64位,另一个置于低64位,还有pckev指令,取的是偶数位。得到

c o l 7 − c o l 6 , c o l 3 − c o l 2 , c o l 7 + c o l 6 , c o l 3 + c o l 2 col_7 - col_6, col_3 - col_2, col_7 + col_6, col_3 + col_2 col7col6,col3col2,col7+col6,col3+col2
c o l 5 − c o l 4 , c o l 1 − c o l 0 , c o l 5 + c o l 4 , c o l 1 + c o l 0 col_5 - col_4, col_1 - col_0, col_5 + col_4, col_1 + col_0 col5col4,col1col0,col5+col4,col1+col0

两个寄存器对应位置的值,然后两个寄存器addvsubv得到8组数据。要构造两个寄存器对应位置就为同组数据,也就是对应位置组合包括 c o l 0 . . . c o l 7 col_0...col_7 col0...col7所有元素。仍然需要ilvodilvev指令,分别取奇数位或者偶数位,组合出新的寄存器值。举一组元素作为例子

c o l 3 + c o l 2 + c o l 1 + c o l 0 col_3+col_2+col_1+col_0 col3+col2+col1+col0
c o l 7 + c o l 6 + c o l 5 + c o l 4 col_7+col_6+col_5+col_4 col7+col6+col5+col4

最后同样hadd相邻求和,扩展32位,只是要操作的寄存器变多了而已。

这就是sa8d_8x8的内容,观察编译中间文件,已经达到可展开的极限。然后将其作为基本函数,供16x16等尺寸循环调用。但发现中间文件中,这些尺寸的优化结果非常糟糕,向量寄存器和定点寄存器都出现不够用的情况,导致循环中非常多的堆栈操作。

因此判断需要在一定程度上降低sa8d_8x8的并行度,当然这是针对需要内联的sa8d_8x8_internal,供外部调用的可以是原本的sa8d_8x8。而这里可以顺序执行的部分有从加载数据,到得到残差,也就是列变换之前。这个部分中都是对一行中的元素进行操作。降低一个级别,原本8行数据同时进行计算,现在4行数据并行计算。

测试得到向量寄存器满足要求,但定点寄存器仍存在问题。这里判断是访存结构的问题。定点寄存器的值都是 p o i n t e r + n ∗ s t r i d e pointer + n * stride pointer+nstride或者 n ∗ s t r i d e n * stride nstride等一些常量。可以确定是过多的连续load而不更新pointer导致的问题,原本是4个load更新一次pointer,虽然这样能提高运行速度,避免过多定点计算。但这里只能牺牲性能。最后采用的策略是load一次,更新一次,最低性能。保证内层循环不存在任何的堆栈操作。

总结

这些就是哈达玛变换的所有内容,已经保证编译的中间文件呈现出最优的形式,但后续还要在仿真软件上验证优化结果。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值