如何使用RVV1.0进行矩阵转置?

问题描述 投票:0回答:1

描述

我是 RVV 的新手,正在使用 RVV1.0 重写一些汇编函数。现在我遇到了一些关于矩阵转置的问题。 Arm NEON 提供 vtrn 指令,帮助进行转置:

# element wide = 8
.macro TRANSPOSE4x4 r0 r1 r2 r3
    vtrn.16         \r0, \r2
    vtrn.16         \r1, \r3
    vtrn.8          \r0, \r1
    vtrn.8          \r2, \r3
.endm

# element wide = 16
.macro TRANSPOSE4x4_16  d0 d1 d2 d3
    vtrn.32     \d0, \d2
    vtrn.32     \d1, \d3
    vtrn.16     \d0, \d1
    vtrn.16     \d2, \d3
.endm

# element wide = 8
.macro TRANSPOSE8x8 r0 r1 r2 r3 r4 r5 r6 r7
    vtrn.32         \r0, \r4
    vtrn.32         \r1, \r5
    vtrn.32         \r2, \r6
    vtrn.32         \r3, \r7
    vtrn.16         \r0, \r2
    vtrn.16         \r1, \r3
    vtrn.16         \r4, \r6
    vtrn.16         \r5, \r7
    vtrn.8          \r0, \r1
    vtrn.8          \r2, \r3
    vtrn.8          \r4, \r5
    vtrn.8          \r6, \r7
.endm

但是在RVV中,我没有找到像vtrn这样的指令,为什么RVV不提供这个?如果我有一个4*4的矩阵,每一行分别存储在v1,v2,v3,v4中,并且SEW=16,就像

TRANSPOSE4x4_16

在arm NEON中,如何转置它?

我尝试使用这个博客中的一些方法,但失败了。

一些背景

我正在重写一个名为x264的视频编解码器库,它有一个名为sub4x4_dct的汇编函数:

.macro SUMSUB_AB sum, diff, a, b
    vadd.s16    \sum,  \a, \b
    vsub.s16    \diff, \a, \b
.endm

.macro DCT_1D d0 d1 d2 d3  d4 d5 d6 d7
    SUMSUB_AB       \d1, \d6, \d5, \d6
    SUMSUB_AB       \d3, \d7, \d4, \d7
    vadd.s16        \d0, \d3, \d1
    vadd.s16        \d4, \d7, \d7
    vadd.s16        \d5, \d6, \d6
    vsub.s16        \d2, \d3, \d1
    vadd.s16        \d1, \d4, \d6
    vsub.s16        \d3, \d7, \d5
.endm

function sub4x4_dct_neon
    mov             r3, #FENC_STRIDE
    mov             ip, #FDEC_STRIDE
    vld1.32         {d0[]}, [r1,:32], r3
    vld1.32         {d1[]}, [r2,:32], ip
    vld1.32         {d2[]}, [r1,:32], r3
    vsubl.u8        q8,  d0,  d1
    vld1.32         {d3[]}, [r2,:32], ip
    vld1.32         {d4[]}, [r1,:32], r3
    vsubl.u8        q9,  d2,  d3
    vld1.32         {d5[]}, [r2,:32], ip
    vld1.32         {d6[]}, [r1,:32], r3
    vsubl.u8        q10, d4,  d5
    vld1.32         {d7[]}, [r2,:32], ip
    vsubl.u8        q11, d6,  d7

    DCT_1D          d0, d1, d2, d3, d16, d18, d20, d22
    TRANSPOSE4x4_16 d0, d1, d2, d3
    DCT_1D          d4, d5, d6, d7, d0, d1, d2, d3
    vst1.64         {d4-d7}, [r0,:128]
    bx              lr
endfunc

C 实现是:

// dctcoef : int16_t
// pixel: uint8_t
static inline void pixel_sub_wxh( dctcoef *diff, int i_size,
                                  pixel *pix1, int i_pix1, pixel *pix2, int i_pix2 )
{
    for( int y = 0; y < i_size; y++ )
    {
        for( int x = 0; x < i_size; x++ )
            diff[x + y*i_size] = pix1[x] - pix2[x];
        pix1 += i_pix1;
        pix2 += i_pix2;
    }
}

static void sub4x4_dct( dctcoef dct[16], pixel *pix1, pixel *pix2 )
{
    dctcoef d[16];
    dctcoef tmp[16];

    pixel_sub_wxh( d, 4, pix1, FENC_STRIDE, pix2, FDEC_STRIDE );

    for( int i = 0; i < 4; i++ )
    {
        int s03 = d[i*4+0] + d[i*4+3];
        int s12 = d[i*4+1] + d[i*4+2];
        int d03 = d[i*4+0] - d[i*4+3];
        int d12 = d[i*4+1] - d[i*4+2];

        tmp[0*4+i] =   s03 +   s12;
        tmp[1*4+i] = 2*d03 +   d12;
        tmp[2*4+i] =   s03 -   s12;
        tmp[3*4+i] =   d03 - 2*d12;
    }

    for( int i = 0; i < 4; i++ )
    {
        int s03 = tmp[i*4+0] + tmp[i*4+3];
        int s12 = tmp[i*4+1] + tmp[i*4+2];
        int d03 = tmp[i*4+0] - tmp[i*4+3];
        int d12 = tmp[i*4+1] - tmp[i*4+2];

        dct[i*4+0] =   s03 +   s12;
        dct[i*4+1] = 2*d03 +   d12;
        dct[i*4+2] =   s03 -   s12;
        dct[i*4+3] =   d03 - 2*d12;
    }
}

其实我并没有弄清楚这个算法在汇编中是如何实现的,所以打算直接模仿NEON的汇编方法来写。这是我已经实现的:

#define FENC_STRIDE 16
#define FDEC_STRIDE 32

.macro SUMSUB_AB sum, diff, a, b
    vadd.vv \sum, \a, \b
    vsub.vv \diff, \a, \b
.endm

.macro DCT_1D d0 d1 d2 d3 d4 d5 d6 d7
    SUMSUB_AB \d1, \d6, \d5, \d6
    SUMSUB_AB \d3, \d7, \d4, \d7
    vadd.vv \d0, \d3, \d1
    vadd.vv \d4, \d7, \d7
    vadd.vv \d5, \d6, \d6
    vsub.vv \d2, \d3, \d1
    vadd.vv \d1, \d4, \d6
    vsub.vv \d3, \d7, \d5
.endm

function sub4x4_dct_rvv
    vsetivli t0, 4, e8, mf2, ta, ma
      
    vle8.v v24, (a1)
    add a1, a1, FENC_STRIDE   
    vle8.v v25, (a2)
    add a2, a2, FDEC_STRIDE
    vle8.v v26, (a1)
    add a1, a1, FENC_STRIDE
    vle8.v v27, (a2)
    add a2, a2, FDEC_STRIDE
    vle8.v v5, (a1)  
    add a1, a1, FENC_STRIDE
    vle8.v v6, (a2)
    add a2, a2, FDEC_STRIDE  
    vle8.v v7, (a1)
    vle8.v v8, (a2)

    vwsubu.vv v9, v24, v25
    vwsubu.vv v10, v26, v27
    vwsubu.vv v11, v27, v5
    vwsubu.vv v30, v7, v8
      
    DCT_1D v24, v25, v26, v27, v9, v10, v11, v30

    # TRANSPOSE4x4_16 v24, v25, v26, v27
    lui     t1, 11
    addi    t1, t1, -1366
    vsetivli        zero, 4, e16, m1, tu, mu
    vmv.s.x v0, t1
    lui     t1, 5
    addi    t1, t1, 1365
    vmv.s.x v12, t1
    vmv1r.v v15, v24
    vslideup.vi     v15, v25, 1, v0.t
    vmv1r.v v14, v26
    vslideup.vi     v14, v27, 1, v0.t
    vmv1r.v v0, v12
    vslidedown.vi   v25, v24, 1, v0.t
    vslidedown.vi   v27, v26, 1, v0.t
    vmv1r.v v12, v15
    vslideup.vi     v12, v14, 2
    vmv1r.v v13, v25
    vslideup.vi     v13, v27, 2
    vsetivli        zero, 2, e16, m1, tu, ma
    vslidedown.vi   v14, v15, 2
    vslidedown.vi   v27, v25, 2
    vmv1r.v v15, v27
    vmv1r.v v24, v12

    DCT_1D v5, v6, v7, v8, v24, v25, v26, v27
    
    li t1, 8
    vlsseg4e16.v v5, (a0), t1
      
    ret
endfunc

我终于得到了错误的答案。而且看起来无论输入如何,输出都保持不变: 我发现我对手臂寄存器有一些误解,我正在修复它。

关于转置大小,4x4和8x8就可以了。

riscv
1个回答
0
投票

我使用 vrgather_vv、vslideup_vx 和 vmerge_vvm 指令实现了 4x4 矩阵转置。然而,表现却不尽如人意!看来向量寄存器之间的数据交换成本相当高。

© www.soinside.com 2019 - 2024. All rights reserved.