| 1 | #include "cpu_types.hpp" |
| 2 | |
| 3 | namespace { |
| 4 | template <typename scalar_t> |
| 5 | void rms_norm_impl(scalar_t* __restrict__ out, |
| 6 | const scalar_t* __restrict__ input, |
| 7 | const scalar_t* __restrict__ weight, const bool has_weight, |
| 8 | const float epsilon, const int num_tokens, |
| 9 | const int hidden_size) { |
| 10 | using scalar_vec_t = vec_op::vec_t<scalar_t>; |
| 11 | constexpr int VEC_ELEM_NUM = scalar_vec_t::get_elem_num(); |
| 12 | TORCH_CHECK(hidden_size % VEC_ELEM_NUM == 0); |
| 13 | |
| 14 | #pragma omp parallel for |
| 15 | for (int i = 0; i < num_tokens; ++i) { |
| 16 | vec_op::FP32Vec8 variance(0.0); |
| 17 | auto input_p = input + i * hidden_size; |
| 18 | auto output_p = out + i * hidden_size; |
| 19 | for (int j = 0; j < hidden_size; j += VEC_ELEM_NUM) { |
| 20 | scalar_vec_t x(input_p + j); |
| 21 | vec_op::FP32Vec8 fp32_x(x); |
| 22 | variance = variance + fp32_x * fp32_x; |
| 23 | } |
| 24 | |
| 25 | float s_variance = |
| 26 | 1.0f / sqrtf(variance.reduce_sum() / (float)hidden_size + epsilon); |
| 27 | vec_op::FP32Vec8 fp32_s_variance(s_variance); |
| 28 | |
| 29 | for (int j = 0; j < hidden_size; j += VEC_ELEM_NUM) { |
| 30 | scalar_vec_t x(input_p + j); |
| 31 | vec_op::FP32Vec8 fp32_x(x); |
| 32 | vec_op::FP32Vec8 fp32_out; |
| 33 | if (has_weight) { |
| 34 | scalar_vec_t w(weight + j); |
| 35 | vec_op::FP32Vec8 fp32_w(w); |
| 36 | fp32_out = fp32_x * fp32_s_variance * fp32_w; |
| 37 | } else { |
| 38 | fp32_out = fp32_x * fp32_s_variance; |
| 39 | } |
| 40 | |
| 41 | scalar_vec_t out(fp32_out); |
| 42 | out.save(output_p + j); |
| 43 | } |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | template <typename scalar_t> |
| 48 | void fused_add_rms_norm_impl(scalar_t* __restrict__ input, |
| 49 | scalar_t* __restrict__ residual, |
| 50 | const scalar_t* __restrict__ weight, |
| 51 | const bool has_weight, const float epsilon, |
| 52 | const int num_tokens, const int hidden_size) { |
| 53 | using scalar_vec_t = vec_op::vec_t<scalar_t>; |
| 54 | constexpr int VEC_ELEM_NUM = scalar_vec_t::get_elem_num(); |
| 55 | TORCH_CHECK(hidden_size % VEC_ELEM_NUM == 0); |
| 56 | |
| 57 | #pragma omp parallel for |
| 58 | for (int i = 0; i < num_tokens; ++i) { |
| 59 | vec_op::FP32Vec8 variance(0.0); |
| 60 | auto input_p = input + i * hidden_size; |
| 61 | auto residual_p = residual + i * hidden_size; |
| 62 | for (int j = 0; j < hidden_size; j += VEC_ELEM_NUM) { |
| 63 | scalar_vec_t x(input_p + j); |
| 64 | scalar_vec_t res(residual_p + j); |
| 65 | vec_op::FP32Vec8 fp32_x(x); |
| 66 | vec_op::FP32Vec8 fp32_res(res); |
| 67 | |
| 68 | fp32_x = fp32_x + fp32_res; |
| 69 | variance = variance + fp32_x * fp32_x; |
| 70 | scalar_vec_t out(fp32_x); |
| 71 | out.save(residual_p + j); |
| 72 | } |
| 73 | |
| 74 | float s_variance = |
| 75 | 1.0f / sqrtf(variance.reduce_sum() / (float)hidden_size + epsilon); |
| 76 | vec_op::FP32Vec8 fp32_s_variance(s_variance); |
| 77 | |
| 78 | for (int j = 0; j < hidden_size; j += VEC_ELEM_NUM) { |
| 79 | vec_op::FP32Vec8 fp32_out; |
| 80 | if (has_weight) { |
| 81 | scalar_vec_t w(weight + j); |
| 82 | scalar_vec_t res(residual_p + j); |
| 83 | vec_op::FP32Vec8 fp32_w(w); |
| 84 | vec_op::FP32Vec8 fp32_res(res); |
| 85 | fp32_out = fp32_res * fp32_s_variance * fp32_w; |
| 86 | } else { |
| 87 | scalar_vec_t res(residual_p + j); |
| 88 | vec_op::FP32Vec8 fp32_res(res); |
| 89 | fp32_out = fp32_res * fp32_s_variance; |
| 90 | } |
| 91 | |
| 92 | scalar_vec_t out(fp32_out); |
| 93 | out.save(input_p + j); |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | } // namespace |
| 98 | |
| 99 | void rms_norm(torch::Tensor& out, torch::Tensor& input, |
| 100 | std::optional<torch::Tensor> weight, double epsilon) { |
| 101 | int hidden_size = input.size(-1); |
| 102 | int num_tokens = input.numel() / hidden_size; |
| 103 | const bool has_weight = weight.has_value(); |
| 104 | if (has_weight) { |
| 105 | TORCH_CHECK(weight->is_contiguous()); |
| 106 | } |
| 107 | |
| 108 | VLLM_DISPATCH_FLOATING_TYPES(input.scalar_type(), "rms_norm_impl", [&] { |
| 109 | CPU_KERNEL_GUARD_IN(rms_norm_impl) |
| 110 | rms_norm_impl(out.data_ptr<scalar_t>(), input.data_ptr<scalar_t>(), |
| 111 | has_weight ? weight->data_ptr<scalar_t>() : nullptr, |
| 112 | has_weight, epsilon, num_tokens, hidden_size); |
| 113 | CPU_KERNEL_GUARD_OUT(rms_norm_impl) |
| 114 | }); |
| 115 | } |
| 116 | |
| 117 | void fused_add_rms_norm(torch::Tensor& input, torch::Tensor& residual, |
| 118 | std::optional<torch::Tensor> weight, double epsilon) { |
| 119 | int hidden_size = input.size(-1); |
| 120 | int num_tokens = input.numel() / hidden_size; |
| 121 | const bool has_weight = weight.has_value(); |
| 122 | if (has_weight) { |
| 123 | TORCH_CHECK(weight->scalar_type() == input.scalar_type()); |
| 124 | TORCH_CHECK(weight->is_contiguous()); |
| 125 | } |
| 126 | |
| 127 | VLLM_DISPATCH_FLOATING_TYPES( |
| 128 | input.scalar_type(), "fused_add_rms_norm_impl", [&] { |
| 129 | CPU_KERNEL_GUARD_IN(fused_add_rms_norm_impl) |
| 130 | fused_add_rms_norm_impl( |
| 131 | input.data_ptr<scalar_t>(), residual.data_ptr<scalar_t>(), |
| 132 | has_weight ? weight->data_ptr<scalar_t>() : nullptr, has_weight, |
| 133 | epsilon, num_tokens, hidden_size); |
| 134 | CPU_KERNEL_GUARD_OUT(fused_add_rms_norm_impl) |
| 135 | }); |
| 136 | } |