Skip to content

Commit cbe733a

Browse files
committed
Uint2 improvements and fixes
1 parent 906cf74 commit cbe733a

6 files changed

Lines changed: 83 additions & 8 deletions

File tree

python/benchmark/benchmark.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
QUANT_DTYPES_TO_BENCH: list[torch.dtype] = [
2020
torch.quint8,
2121
torch.quint4x2,
22+
torch.quint2x4
2223
]
2324

2425
def quantize_torch(t: torch.Tensor, scale: float, zp: int, dtype: torch.dtype) -> torch.tensor:
@@ -63,7 +64,11 @@ def _bench_piquant() -> None:
6364
dq_piquant = piquant.torch.dequantize(results_piquant[i], scale=scale, zero_point=zp, dtype=torch.float32)
6465
assert dq_torch.numel() == dq_piquant.numel()
6566
assert dq_torch.dtype == dq_piquant.dtype
66-
assert torch.allclose(dq_torch, dq_piquant, atol=1e-1)
67+
if not torch.allclose(dq_torch, dq_piquant, atol=1e-1):
68+
print(f"Results differ for dtype {torch_d} at run {i}")
69+
for j in range(dq_torch.numel()):
70+
if not torch.isclose(dq_torch[j], dq_piquant[j], atol=1e-1):
71+
print(f" Index {j}: torch={dq_torch[j]}, piquant={dq_piquant[j]}")
6772
print(f'{dtype_labels[-1]:<10} | torch: {torch_time:.6f}s | piquant: {piquant_time:.6f}s')
6873

6974

python/benchmark/throughput_avg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
TOTAL_GIB = 4
1010
ITERATIONS = 10
1111
SRC_DTYPE = torch.bfloat16
12-
Q_DTYPE = torch.quint4x2
12+
Q_DTYPE = torch.quint2x4
1313
OUT_DTYPE = torch.bfloat16
1414

1515
B_PER_ELEM = torch.tensor([], dtype=SRC_DTYPE).element_size()

python/quant_benchmark.png

1.68 KB
Loading

src/kernels/dequantize.inl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,20 @@ static auto PIQUANT_HOT dequant_uint2(
6969
static_assert(ReduceOp == reduce_op::set || ReduceOp == reduce_op::add, "Invalid reduce operation");
7070
}
7171
}
72-
if (numel & 3) { /* Handle 1-, 2- or 3-value tail */
73-
auto p {x[i>>2].bits};
74-
if (numel & 1) o[i] = dequant_step<In, Out>(scale, zp, In{p & 3});
75-
if (numel & 2) o[i+1] = dequant_step<In, Out>(scale, zp, In{p>>2 & 3});
76-
if (numel & 3) o[i+((numel & 3) == 3 ? 2 : 0)] = dequant_step<In, Out>(scale, zp, In{p>>4 & 3});
72+
auto p {x[i>>2].bits};
73+
switch (numel&3) {
74+
case 1:
75+
o[i] = dequant_step<In, Out>(scale, zp, p&3);
76+
break;
77+
case 2:
78+
o[i] = dequant_step<In, Out>(scale, zp, p&3);
79+
o[i+1] = dequant_step<In, Out>(scale, zp, (p>>2)&3);
80+
break;
81+
case 3:
82+
o[i] = dequant_step<In, Out>(scale, zp, p&3);
83+
o[i+1] = dequant_step<In, Out>(scale, zp, (p>>2)&3);
84+
o[i+2] = dequant_step<In, Out>(scale, zp, (p>>4)&3);
85+
break;
7786
}
7887
}
7988

test/dequant.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ using namespace piquant;
2929
data_in.resize(numel); \
3030
quantized.resize(numel_out); \
3131
std::ranges::generate(data_in, [&] { return dist(gen); }); \
32-
piquant::context ctx {std::max(1u, 4u)}; \
32+
piquant::context ctx {4}; \
3333
auto [scale, zero_point] {ctx.compute_quant_config_from_data(data_in, dtype_traits<to>::type_code)}; \
3434
ctx.quantize_generic<ti, to>(data_in, quantized, scale, zero_point, piquant::round_mode::rnd); \
3535
std::vector<ti> dequantized {}; \

test/quant.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,67 @@ template <const std::uint8_t IDX, typename T>
121121
} \
122122
}
123123

124+
#define test_quant_int2(ti, to, rnd, is_stochastic, is_signed) \
125+
TEST(quantize, quantize_##ti##_to_##to##_##rnd) { \
126+
std::mt19937 gen {0x9032002}; \
127+
std::uniform_real_distribution<fp32_t> dist {-1.0, 1.0}; \
128+
\
129+
for (std::size_t n {}; n < iters; ++n) { \
130+
std::cout << "Iteration " << n << std::endl; \
131+
fp32_t scale {std::uniform_real_distribution<fp32_t>{0.1, 1.0}(gen)}; \
132+
std::size_t numel {std::uniform_int_distribution<std::size_t>{5000, 1'5000}(gen)}; \
133+
std::size_t numel_out {std::is_same_v<uint2_t, to> ? (numel+3)>>2 : numel}; \
134+
std::int32_t zero_point {std::is_same_v<uint2_t, to> ? std::uniform_int_distribution<std::int32_t>{-1, 2}(gen) : \
135+
std::uniform_int_distribution<std::int32_t>{-128, 127}(gen)}; \
136+
\
137+
std::vector<ti> data_in {}; \
138+
std::vector<to> data_out_naive {}; \
139+
std::vector<to> data_out {}; \
140+
data_in.resize(numel); \
141+
data_out.resize(numel_out); \
142+
data_out_naive.resize(numel_out); \
143+
std::ranges::generate(data_in, [&] { return dist(gen); }); \
144+
quantize_naive<ti, to, piquant::round_mode::rnd>(data_in, data_out_naive, scale, zero_point); \
145+
piquant::context ctx {std::max(1u, std::thread::hardware_concurrency())}; \
146+
ctx.quantize_generic<ti, to>(data_in, data_out, scale, zero_point, piquant::round_mode::rnd); \
147+
for (std::size_t i {}; i < numel_out; ++i) { \
148+
bool eq {eq = data_out[i] == data_out_naive[i]}; \
149+
std::int32_t a {unpack_nibble<0>(data_out[i], is_signed)}; \
150+
std::int32_t b {unpack_nibble<1>(data_out[i], is_signed)}; \
151+
std::int32_t a_naive {unpack_nibble<0>(data_out_naive[i], is_signed)}; \
152+
std::int32_t b_naive {unpack_nibble<1>(data_out_naive[i], is_signed)}; \
153+
if (is_stochastic) { \
154+
eq |= std::abs(a - a_naive) <= stochastic_epsilon; \
155+
eq |= std::abs(b - b_naive) <= stochastic_epsilon; \
156+
} else { \
157+
eq = eq && (a == a_naive) && (b == b_naive); \
158+
} \
159+
if (!eq) { \
160+
std::cout << "Mismatch at index " << i << ": " << "(" << a << ", " << b << ") != (" << a_naive << ", " << b_naive << ") -> " << (std::int32_t)(data_out[i].bits) << " != " << (std::int32_t)(data_out_naive[i].bits) << std::endl; \
161+
std::cout << "Input: " << static_cast<fp32_t>(data_in[i]) << std::endl; \
162+
std::cout << "Data in: ["; \
163+
for (std::size_t j {}; j < numel; ++j) { \
164+
std::cout << static_cast<fp32_t>(data_in[j]) << " "; \
165+
} \
166+
std::cout << "]" << std::endl; \
167+
std::cout << "Data out (f): ["; \
168+
for (std::size_t j {}; j < numel_out; ++j) { \
169+
std::cout << static_cast<std::int32_t>(data_out[j].bits) << " "; \
170+
} \
171+
std::cout << "]" << std::endl; \
172+
std::cout << "Data out (n): ["; \
173+
for (std::size_t j {}; j < numel_out; ++j) { \
174+
std::cout << static_cast<std::int32_t>(data_out_naive[j].bits) << " "; \
175+
} \
176+
std::cout << "]" << std::endl; \
177+
std::cout << "Num el out: " << numel_out << std::endl; \
178+
std::cout << "Num el in: " << numel << std::endl; \
179+
ASSERT_TRUE(eq); \
180+
} \
181+
} \
182+
} \
183+
}
184+
124185
test_quant_int4(fp32_t, uint4_t, nearest, false, false)
125186
test_quant_int4(fp32_t, uint4_t, stochastic, true, false)
126187
test_quant_int4(bfp16_t, uint4_t, nearest, false, false)

0 commit comments

Comments
 (0)