@@ -640,7 +640,6 @@ static auto PIQUANT_HOT quant_bf16_to_uint4_nearest(
640640 auto qb {std::clamp (static_cast <std::int32_t >(std::round (static_cast <fp32_t >(b) * scale)) + zp, 0 , 15 )};
641641 return qa & 15 | (qb & 15 )<<4 ;
642642 }};
643-
644643 for (; i+1 < numel; i += 2 ) {
645644 auto a {x[i]};
646645 auto b {x[i+1 ]};
@@ -652,6 +651,81 @@ static auto PIQUANT_HOT quant_bf16_to_uint4_nearest(
652651 }
653652}
654653
654+ static auto PIQUANT_HOT quant_bf16_to_uint2_nearest (
655+ const bfp16_t * PIQUANT_RESTRICT x,
656+ uint2_t * PIQUANT_RESTRICT o,
657+ std::int64_t numel,
658+ fp32_t scale,
659+ std::int32_t zp
660+ ) noexcept -> void {
661+ scale = 1 .0f / scale;
662+ std::int64_t i {};
663+ #if defined(__AVX512F__) && defined(__AVX512BW__)
664+ __m512 vinv_scale {_mm512_set1_ps (scale)};
665+ __m512 vhalf {_mm512_set1_ps (0 .5f )};
666+ __m512 vneg_half {_mm512_set1_ps (-0 .5f )};
667+ __m512 vzero_ps {_mm512_setzero_ps ()};
668+ __m512i vzp {_mm512_set1_epi32 (zp)};
669+ __m512i vmin {_mm512_setzero_si512 ()};
670+ __m512i vmax {_mm512_set1_epi32 (3 )};
671+ __m128i mask03 {_mm_set1_epi8 (0x03 )};
672+ __m128i w0146 {_mm_setr_epi8 (
673+ 1 ,4 , 16 ,64 , 1 ,4 , 16 ,64 ,
674+ 1 ,4 , 16 ,64 , 1 ,4 , 16 ,64
675+ )};
676+ for (; i+15 < numel; i += 16 ) {
677+ #if defined(__AVX512BF16__)
678+ __m256i raw {_mm256_loadu_si256 (reinterpret_cast <const __m256i*>(x+i))};
679+ __m512 xf {_mm512_cvtpbh_ps (std::bit_cast<__m256bh>(raw))};
680+ #else
681+ __m256i raw {_mm256_loadu_si256 (reinterpret_cast <const __m256i*>(x + i))};
682+ __m512i u32s {_mm512_cvtepu16_epi32 (raw)};
683+ __m512i shl {_mm512_slli_epi32 (u32s, 16 )};
684+ __m512 xf {_mm512_castsi512_ps (shl)};
685+ #endif
686+ __m512 scaled {_mm512_mul_ps (xf, vinv_scale)};
687+ __m512 rnd {_mm512_mask_blend_ps (_mm512_cmp_ps_mask (scaled, vzero_ps, _CMP_GE_OQ), vneg_half, vhalf)};
688+ __m512i qi32 {_mm512_min_epi32 (
689+ _mm512_max_epi32 (
690+ _mm512_add_epi32 (
691+ _mm512_cvttps_epi32 (_mm512_add_ps (scaled, rnd)),
692+ vzp),
693+ vmin),
694+ vmax)};
695+ __m128i u8 {_mm512_cvtusepi32_epi8 (qi32)};
696+ u8 = _mm_and_si128 (u8 , mask03);
697+ __m128i pair_sums {_mm_maddubs_epi16 (u8 , w0146)};
698+ __m128i bytes16 {_mm_hadd_epi16 (pair_sums, _mm_setzero_si128 ())};
699+ __m128i packed4 {_mm_packus_epi16 (bytes16, _mm_setzero_si128 ())};
700+ _mm_storeu_si32 (o + (i>>2 ), packed4);
701+ }
702+ #endif
703+
704+ const auto quant_step_packed {[=](bfp16_t a, bfp16_t b, bfp16_t c, bfp16_t d) noexcept -> std::uint8_t {
705+ auto qa {std::clamp (static_cast <std::int32_t >(std::round (static_cast <fp32_t >(a) * scale)) + zp, 0 , 3 )};
706+ auto qb {std::clamp (static_cast <std::int32_t >(std::round (static_cast <fp32_t >(b) * scale)) + zp, 0 , 3 )};
707+ auto qc {std::clamp (static_cast <std::int32_t >(std::round (static_cast <fp32_t >(c) * scale)) + zp, 0 , 3 )};
708+ auto qd {std::clamp (static_cast <std::int32_t >(std::round (static_cast <fp32_t >(d) * scale)) + zp, 0 , 3 )};
709+ return (qa & 3 ) | ((qb & 3 )<<2 ) | ((qc & 3 )<<4 ) | ((qd & 3 )<<6 );
710+ }};
711+ for (; i+3 < numel; i += 4 ) {
712+ auto a {x[i]};
713+ auto b {x[i+1 ]};
714+ auto c {x[i+2 ]};
715+ auto d {x[i+3 ]};
716+ o[i>>2 ] = quant_step_packed (a, b, c, d);
717+ }
718+ if (numel & 3 ) { /* Handle 1-, 2- or 3-value tail */
719+ std::uint8_t p {};
720+ switch (numel & 3 ) {
721+ case 3 : p |= (quant_step_packed (x[i+2 ], 0 , 0 , 0 )&3 ) << 4 ;
722+ case 2 : p |= (quant_step_packed (x[i+1 ], 0 , 0 , 0 )&3 ) << 2 ;
723+ case 1 : p |= (quant_step_packed (x[i], 0 , 0 , 0 )&3 );
724+ }
725+ o[i>>2 ] = p;
726+ }
727+ }
728+
655729template <const reduce_op ReduceOp>
656730static auto PIQUANT_HOT dequant_uint8_to_f32 (
657731 const std::uint8_t * PIQUANT_RESTRICT x,
0 commit comments