Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions csrc/include/aiter_opus_plus.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,23 +111,24 @@ OPUS_D decltype(auto) fp32_to_bf8_scaled_x4(const S& s, float inverted_scale)
}

// fp32x2 -> i8x2 with scale
// ISA: v_pk_mul_f32 + v_cvt_i32_f32 x2
// ISA: v_pk_mul_f32 + round-to-nearest-even conversion x2
template <typename S, std::enable_if_t<std::is_same_v<S, fp32x2_t>, bool> = true>
OPUS_D decltype(auto) fp32_to_i8_scaled_x2(const S& s, float inverted_scale)
{
fp32x2_t tmp = pk_mul_f32(s, fp32x2_t{inverted_scale, inverted_scale});
return i8x2_t{static_cast<i8_t>(tmp[0]), static_cast<i8_t>(tmp[1])};
return i8x2_t{static_cast<i8_t>(__builtin_rintf(tmp[0])),
static_cast<i8_t>(__builtin_rintf(tmp[1]))};
}

template <typename S, std::enable_if_t<std::is_same_v<S, fp32x4_t>, bool> = true>
OPUS_D decltype(auto) fp32_to_i8_scaled_x4(const S& s, float inverted_scale)
{
fp32x2_t tmp0 = pk_mul_f32(fp32x2_t{s[0], s[1]}, fp32x2_t{inverted_scale, inverted_scale});
fp32x2_t tmp1 = pk_mul_f32(fp32x2_t{s[2], s[3]}, fp32x2_t{inverted_scale, inverted_scale});
return i8x4_t{static_cast<i8_t>(tmp0[0]),
static_cast<i8_t>(tmp0[1]),
static_cast<i8_t>(tmp1[0]),
static_cast<i8_t>(tmp1[1])};
return i8x4_t{static_cast<i8_t>(__builtin_rintf(tmp0[0])),
static_cast<i8_t>(__builtin_rintf(tmp0[1])),
static_cast<i8_t>(__builtin_rintf(tmp1[0])),
static_cast<i8_t>(__builtin_rintf(tmp1[1]))};
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
45 changes: 45 additions & 0 deletions op_tests/test_rmsnorm2dFusedAddQuant.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,49 @@ def run_hip(
return output, residual_out, scale, None


def test_i8_group_quant_rounds_to_nearest():
group_size = 64
target_ratios = torch.tensor(
[
-15.75,
-15.25,
-7.75,
-7.25,
-3.75,
-3.25,
-2.75,
-2.25,
-1.75,
-1.25,
-0.75,
-0.25,
0.25,
0.75,
1.25,
127.0,
],
dtype=torch.float32,
)
group_weight = (target_ratios / 127.0).to(torch.bfloat16).repeat(4)
hidden_size = 1024
groups = hidden_size // group_size
weight = group_weight.repeat(groups)
input = torch.ones((1, hidden_size), dtype=torch.bfloat16)
output = torch.empty_like(input, dtype=torch.int8)
scale = torch.empty((1, groups), dtype=torch.float32)

aiter.rmsnorm_quant(output, input, scale, weight, 1e-6, group_size)

realized_weight = weight.float().reshape(groups, group_size)
realized_ratio = (
realized_weight / realized_weight.abs().amax(dim=-1, keepdim=True) * 127.0
)
expected = torch.round(realized_ratio).clamp(-127, 127).to(torch.int8)
assert torch.equal(
output.reshape_as(expected), expected
), "rmsnorm_quant INT8 output must use round-to-nearest conversion"


@benchmark()
def test_rmsnorm(
m,
Expand Down Expand Up @@ -331,6 +374,8 @@ def calculateTensorsSize(*args):
choices=[dtypes.d_dtypes["bf16"], dtypes.d_dtypes["fp16"]],
)
args = parser.parse_args()
if args.quant_dtype == dtypes.i8:
test_i8_group_quant_rounds_to_nearest()
if args.mode == 1:
test_rmsnorm_func = partial(
test_rmsnorm, quant_type=QuantType.No, add_residual=False
Expand Down
Loading