| 1 | #ifndef CPU_MICRO_GEMM_RVV_HPP |
| 2 | #define CPU_MICRO_GEMM_RVV_HPP |
| 3 | |
| 4 | #include "cpu/micro_gemm/cpu_micro_gemm_impl.hpp" |
| 5 | |
| 6 | #if defined(__riscv_v) |
| 7 | |
| 8 | namespace cpu_micro_gemm { |
| 9 | namespace { |
| 10 | |
| 11 | constexpr int32_t RVV_MGEMM_N8 = 8; |
| 12 | constexpr int32_t RVV_MGEMM_B_GROUP_STRIDE = 16; |
| 13 | |
| 14 | template <typename scalar_t> |
| 15 | FORCE_INLINE fixed_fp32x8_t load_row8_b_as_f32(const scalar_t* ptr); |
| 16 | |
| 17 | template <> |
| 18 | FORCE_INLINE fixed_fp32x8_t load_row8_b_as_f32<float>(const float* ptr) { |
| 19 | return RVVI(__riscv_vle32_v_f32, LMUL_256)(ptr, RVV_MGEMM_N8); |
| 20 | } |
| 21 | |
| 22 | template <> |
| 23 | FORCE_INLINE fixed_fp32x8_t |
| 24 | load_row8_b_as_f32<c10::Half>(const c10::Half* ptr) { |
| 25 | #if defined(__riscv_zvfh) |
| 26 | fixed_fp16x8_t vec = RVVI(__riscv_vle16_v_f16, LMUL_128)( |
| 27 | reinterpret_cast<const _Float16*>(ptr), RVV_MGEMM_N8); |
| 28 | return RVVI(__riscv_vfwcvt_f_f_v_f32, LMUL_256)(vec, RVV_MGEMM_N8); |
| 29 | #else |
| 30 | alignas(32) float values[RVV_MGEMM_N8]; |
| 31 | for (int32_t i = 0; i < RVV_MGEMM_N8; ++i) { |
| 32 | values[i] = static_cast<float>(ptr[i]); |
| 33 | } |
| 34 | return RVVI(__riscv_vle32_v_f32, LMUL_256)(values, RVV_MGEMM_N8); |
| 35 | #endif |
| 36 | } |
| 37 | |
| 38 | template <> |
| 39 | FORCE_INLINE fixed_fp32x8_t |
| 40 | load_row8_b_as_f32<c10::BFloat16>(const c10::BFloat16* ptr) { |
| 41 | #if defined(__riscv_zvfbfmin) |
| 42 | fixed_u16x8_t raw = RVVI(__riscv_vle16_v_u16, LMUL_128)( |
| 43 | reinterpret_cast<const uint16_t*>(ptr), RVV_MGEMM_N8); |
| 44 | fixed_bf16x8_t vec = |
| 45 | RVVI4(__riscv_vreinterpret_v_u16, LMUL_128, _bf16, LMUL_128)(raw); |
| 46 | return RVVI(__riscv_vfwcvtbf16_f_f_v_f32, LMUL_256)(vec, RVV_MGEMM_N8); |
| 47 | #else |
| 48 | fixed_u16x8_t raw = RVVI(__riscv_vle16_v_u16, LMUL_128)( |
| 49 | reinterpret_cast<const uint16_t*>(ptr), RVV_MGEMM_N8); |
| 50 | auto wide = RVVI(__riscv_vzext_vf2_u32, LMUL_256)(raw, RVV_MGEMM_N8); |
| 51 | auto shifted = RVVI(__riscv_vsll_vx_u32, LMUL_256)(wide, 16, RVV_MGEMM_N8); |
| 52 | return RVVI4(__riscv_vreinterpret_v_u32, LMUL_256, _f32, LMUL_256)(shifted); |
| 53 | #endif |
| 54 | } |
| 55 | |
| 56 | // Mx8 RVV kernel. B points at one 8-channel half of a 16-channel packed group, |
| 57 | // with rows separated by RVV_MGEMM_B_GROUP_STRIDE scalar elements. |
| 58 | template <int32_t M, typename scalar_t> |
| 59 | FORCE_INLINE void gemm_micro_rvv_fma_mx8_ku4(const scalar_t* __restrict__ a_ptr, |
| 60 | const scalar_t* __restrict__ b_ptr, |
| 61 | float* __restrict__ c_ptr, |
| 62 | const int64_t lda, |
| 63 | const int64_t ldc, const int32_t k, |
| 64 | const bool accum_c) { |
| 65 | static_assert(0 < M && M <= 8); |
| 66 | |
| 67 | #define RVV_ROWS_APPLY(OP) OP(0) OP(1) OP(2) OP(3) OP(4) OP(5) OP(6) OP(7) |
| 68 | #define RVV_IF_M(i) if constexpr (M > (i)) |
| 69 | |
| 70 | #define RVV_DECL_A(i) const scalar_t* __restrict__ a##i = a_ptr + (i) * lda; |
| 71 | RVV_ROWS_APPLY(RVV_DECL_A) |
| 72 | #undef RVV_DECL_A |
| 73 | |
| 74 | #define RVV_DECL_ACC(i) fixed_fp32x8_t acc##i; |
| 75 | RVV_ROWS_APPLY(RVV_DECL_ACC) |
| 76 | #undef RVV_DECL_ACC |
| 77 | |
| 78 | #define RVV_INIT_ACC(i) \ |
| 79 | RVV_IF_M(i) { \ |
| 80 | if (accum_c) { \ |
| 81 | acc##i = RVVI(__riscv_vle32_v_f32, LMUL_256)(c_ptr + (i) * ldc, \ |
| 82 | RVV_MGEMM_N8); \ |
| 83 | } else { \ |
| 84 | acc##i = RVVI(__riscv_vfmv_v_f_f32, LMUL_256)(0.0f, RVV_MGEMM_N8); \ |
| 85 | } \ |
| 86 | } |
| 87 | RVV_ROWS_APPLY(RVV_INIT_ACC) |
| 88 | #undef RVV_INIT_ACC |
| 89 | |
| 90 | int32_t k_idx = 0; |
| 91 | for (; k_idx + 3 < k; k_idx += 4) { |
| 92 | #define RVV_FMA_ROW(i, K_OFFSET) \ |
| 93 | RVV_IF_M(i) { \ |
| 94 | acc##i = RVVI(__riscv_vfmacc_vf_f32, LMUL_256)( \ |
| 95 | acc##i, static_cast<float>(*(a##i + k_idx + (K_OFFSET))), b, \ |
| 96 | RVV_MGEMM_N8); \ |
| 97 | } |
| 98 | |
| 99 | #define RVV_STEP_K(K_OFFSET) \ |
| 100 | { \ |
| 101 | fixed_fp32x8_t b = load_row8_b_as_f32<scalar_t>( \ |
| 102 | b_ptr + (k_idx + (K_OFFSET)) * RVV_MGEMM_B_GROUP_STRIDE); \ |
| 103 | RVV_FMA_ROW(0, K_OFFSET) \ |
| 104 | RVV_FMA_ROW(1, K_OFFSET) \ |
| 105 | RVV_FMA_ROW(2, K_OFFSET) \ |
| 106 | RVV_FMA_ROW(3, K_OFFSET) \ |
| 107 | RVV_FMA_ROW(4, K_OFFSET) \ |
| 108 | RVV_FMA_ROW(5, K_OFFSET) \ |
| 109 | RVV_FMA_ROW(6, K_OFFSET) \ |
| 110 | RVV_FMA_ROW(7, K_OFFSET) \ |
| 111 | } |
| 112 | |
| 113 | RVV_STEP_K(0) |
| 114 | RVV_STEP_K(1) |
| 115 | RVV_STEP_K(2) |
| 116 | RVV_STEP_K(3) |
| 117 | #undef RVV_STEP_K |
| 118 | #undef RVV_FMA_ROW |
| 119 | } |
| 120 | |
| 121 | for (; k_idx < k; ++k_idx) { |
| 122 | fixed_fp32x8_t b = |
| 123 | load_row8_b_as_f32<scalar_t>(b_ptr + k_idx * RVV_MGEMM_B_GROUP_STRIDE); |
| 124 | #define RVV_TAIL_ROW(i) \ |
| 125 | RVV_IF_M(i) { \ |
| 126 | acc##i = RVVI(__riscv_vfmacc_vf_f32, LMUL_256)( \ |
| 127 | acc##i, static_cast<float>(*(a##i + k_idx)), b, RVV_MGEMM_N8); \ |
| 128 | } |
| 129 | RVV_ROWS_APPLY(RVV_TAIL_ROW) |
| 130 | #undef RVV_TAIL_ROW |
| 131 | } |
| 132 | |
| 133 | #define RVV_STORE_ROW(i) \ |
| 134 | RVV_IF_M(i) { \ |
| 135 | RVVI(__riscv_vse32_v_f32, LMUL_256)(c_ptr + (i) * ldc, acc##i, \ |
| 136 | RVV_MGEMM_N8); \ |
| 137 | } |
| 138 | RVV_ROWS_APPLY(RVV_STORE_ROW) |
| 139 | #undef RVV_STORE_ROW |
| 140 | |
| 141 | #undef RVV_ROWS_APPLY |
| 142 | #undef RVV_IF_M |
| 143 | } |
| 144 | |
| 145 | template <int32_t M, typename scalar_t> |
| 146 | FORCE_INLINE void gemm_micro_rvv_mx32_ku4(DEFINE_CPU_MICRO_GEMM_PARAMS) { |
| 147 | static_assert(0 < M && M <= 8); |
| 148 | scalar_t* __restrict__ curr_b_0 = b_ptr; |
| 149 | scalar_t* __restrict__ curr_b_1 = b_ptr + b_n_group_stride; |
| 150 | |
| 151 | gemm_micro_rvv_fma_mx8_ku4<M>(a_ptr, curr_b_0, c_ptr, lda, ldc, k, accum_c); |
| 152 | gemm_micro_rvv_fma_mx8_ku4<M>(a_ptr, curr_b_0 + RVV_MGEMM_N8, |
| 153 | c_ptr + RVV_MGEMM_N8, lda, ldc, k, accum_c); |
| 154 | gemm_micro_rvv_fma_mx8_ku4<M>(a_ptr, curr_b_1, c_ptr + 16, lda, ldc, k, |
| 155 | accum_c); |
| 156 | gemm_micro_rvv_fma_mx8_ku4<M>(a_ptr, curr_b_1 + RVV_MGEMM_N8, c_ptr + 24, lda, |
| 157 | ldc, k, accum_c); |
| 158 | } |
| 159 | |
| 160 | class TileGemmRVV { |
| 161 | public: |
| 162 | template <typename scalar_t> |
| 163 | FORCE_INLINE static void gemm(DEFINE_CPU_MICRO_GEMM_PARAMS) { |
| 164 | switch (m) { |
| 165 | case 1: |
| 166 | gemm_micro_rvv_mx32_ku4<1>(CPU_MICRO_GEMM_PARAMS); |
| 167 | break; |
| 168 | case 2: |
| 169 | gemm_micro_rvv_mx32_ku4<2>(CPU_MICRO_GEMM_PARAMS); |
| 170 | break; |
| 171 | case 3: |
| 172 | gemm_micro_rvv_mx32_ku4<3>(CPU_MICRO_GEMM_PARAMS); |
| 173 | break; |
| 174 | case 4: |
| 175 | gemm_micro_rvv_mx32_ku4<4>(CPU_MICRO_GEMM_PARAMS); |
| 176 | break; |
| 177 | case 5: |
| 178 | gemm_micro_rvv_mx32_ku4<5>(CPU_MICRO_GEMM_PARAMS); |
| 179 | break; |
| 180 | case 6: |
| 181 | gemm_micro_rvv_mx32_ku4<6>(CPU_MICRO_GEMM_PARAMS); |
| 182 | break; |
| 183 | case 7: |
| 184 | gemm_micro_rvv_mx32_ku4<7>(CPU_MICRO_GEMM_PARAMS); |
| 185 | break; |
| 186 | case 8: |
| 187 | gemm_micro_rvv_mx32_ku4<8>(CPU_MICRO_GEMM_PARAMS); |
| 188 | break; |
| 189 | } |
| 190 | } |
| 191 | }; |
| 192 | |
| 193 | } // namespace |
| 194 | |
| 195 | template <typename scalar_t> |
| 196 | class MicroGemm<cpu_utils::ISA::RVV, scalar_t> { |
| 197 | public: |
| 198 | static constexpr int32_t MaxMSize = 8; |
| 199 | static constexpr int32_t NSize = 32; |
| 200 | |
| 201 | public: |
| 202 | void gemm(DEFINE_CPU_MICRO_GEMM_PARAMS) { |
| 203 | TileGemmRVV::gemm<scalar_t>(CPU_MICRO_GEMM_PARAMS); |
| 204 | } |
| 205 | |
| 206 | static void pack_weight(const scalar_t* __restrict__ weight, |
| 207 | scalar_t* __restrict__ packed_weight, |
| 208 | const int32_t output_size, const int32_t input_size) { |
| 209 | TORCH_CHECK_EQ(output_size % 16, 0); |
| 210 | for (int32_t o_idx = 0; o_idx < output_size; ++o_idx) { |
| 211 | const scalar_t* __restrict__ curr_weight = weight + o_idx * input_size; |
| 212 | scalar_t* __restrict__ curr_packed_weight = |
| 213 | packed_weight + (o_idx / 16) * (16 * input_size) + o_idx % 16; |
| 214 | for (int32_t i_idx = 0; i_idx < input_size; ++i_idx) { |
| 215 | *curr_packed_weight = *curr_weight; |
| 216 | |
| 217 | curr_packed_weight += 16; |
| 218 | ++curr_weight; |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | }; |
| 223 | |
| 224 | } // namespace cpu_micro_gemm |
| 225 | |
| 226 | #endif // defined(__riscv_v) |
| 227 | |
| 228 | #endif // CPU_MICRO_GEMM_RVV_HPP |