Download src/kernels/cpu/iq_avx512.cpp from WineryLabs/Winery-Strata: direct link, hf CLI and curl.
- Browser
- Download file 15.4 kB
-
https://huggingface.co/WineryLabs/Winery-Strata/resolve/main/src/kernels/cpu/iq_avx512.cpp
- Command line
-
hf download hf://WineryLabs/Winery-Strata/src/kernels/cpu/iq_avx512.cpp
-
curl -L -o iq_avx512.cpp https://huggingface.co/WineryLabs/Winery-Strata/resolve/main/src/kernels/cpu/iq_avx512.cpp
15.4 kB
| // src/kernels/cpu/iq_avx512.cpp - plan v0.3 P6: the i-quant expert rows in 512-bit lanes, several tokens at once. | |
| // | |
| // ggml-cpu's x86 dot products for these formats are AVX2 and single-token: every token re-decodes the weights | |
| // and a sign is applied with five instructions per 32 values. Here 64 values are decoded once (8 or 16 grid | |
| // lookups, one 64-bit sign mask, one vector of scales) and every token applies them with a masked subtract, a | |
| // `maddubs` and a `madd`. The arithmetic is ggml's (ggml-cpu/quants.c, the `_generic` references): integer sums | |
| // per block, times d_x * d_y * the format's constant - only the order of the float additions differs. | |
| // | |
| // Formats: IQ2_XXS (16), IQ2_XS (17), IQ3_XXS (18), IQ3_S (21), IQ2_S (22). IQ1_M stays on ggml-cpu (no shipped | |
| // model has IQ1_M expert rows: the 'Coder IQ1_M' pack's gate/up are IQ2_S / IQ3_XXS / IQ3_S). | |
| namespace strata::kernels::cpu { | |
| namespace { | |
| inline float h2f(uint16_t h) { return _mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128((int) h))); } | |
| inline uint32_t u32(const uint8_t* p) { uint32_t v; std::memcpy(&v, p, 4); return v; } | |
| inline uint16_t u16(const uint8_t* p) { uint16_t v; std::memcpy(&v, p, 2); return v; } | |
| inline uint64_t u64(const uint8_t* p) { uint64_t v; std::memcpy(&v, p, 8); return v; } | |
| // E-2: the IQ3 grids by one AVX-512 gather (the same words, the same results) - measured 3-5% SLOWER than the scalar | |
| // lookups on a Ryzen 5 7600 (Zen 4 gathers are microcoded), so opt-in: STRATA_IQ_GATHER=1 | |
| const bool gather = std::getenv("STRATA_IQ_GATHER") != nullptr; | |
| // E-2: software prefetch distance in bytes (STRATA_IQ_PREFETCH; 0 = off): -2-3% gate/up time on IQ3_S decode | |
| // (the rows are read at ~30 GB/s by 6 cores, near what DDR5 with 4 KB pages gives: the pool is memory-bound) | |
| const int prefetch_ahead = [] { | |
| const char* v = std::getenv("STRATA_IQ_PREFETCH"); | |
| return v ? std::atoi(v) : 2048; | |
| }(); | |
| // lanes 0-7 -> s0, 8-15 -> s1, 16-23 -> s2, 24-31 -> s3 (int16 lanes of a maddubs result: two values each) | |
| inline __m512i scales4(int s0, int s1, int s2, int s3) { | |
| const __m512i idx = _mm512_set_epi16(3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, | |
| 0, 0, 0, 0, 0, 0, 0, 0); | |
| const uint64_t packed = (uint64_t) (uint16_t) s0 | ((uint64_t) (uint16_t) s1 << 16) | | |
| ((uint64_t) (uint16_t) s2 << 32) | ((uint64_t) (uint16_t) s3 << 48); | |
| return _mm512_permutexvar_epi16(idx, _mm512_set1_epi64((long long) packed)); | |
| } | |
| // ---- per format: 64 values (chunk j of a 256-value block) -> grid magnitudes, sign mask, scales | |
| template <int TY> struct Fmt; | |
| template <> struct Fmt<16> { // IQ2_XXS: d, qs[32] u16 | |
| static constexpr int bytes = 66; | |
| static constexpr float K = 0.125f; | |
| static inline void decode(const uint8_t* b, int j, __m512i& g, __mmask64& m, __m512i& sc) { | |
| const uint8_t* q = b + 2 + 16 * j; | |
| const uint32_t a0 = u32(q), a1 = u32(q + 4), b0 = u32(q + 8), b1 = u32(q + 12); | |
| g = _mm512_set_epi64((long long) iq2xxs_grid[b0 >> 24], (long long) iq2xxs_grid[(b0 >> 16) & 255], | |
| (long long) iq2xxs_grid[(b0 >> 8) & 255], (long long) iq2xxs_grid[b0 & 255], | |
| (long long) iq2xxs_grid[a0 >> 24], (long long) iq2xxs_grid[(a0 >> 16) & 255], | |
| (long long) iq2xxs_grid[(a0 >> 8) & 255], (long long) iq2xxs_grid[a0 & 255]); | |
| const uint64_t s = (uint64_t) ksigns_iq2xs[a1 & 127] | ((uint64_t) ksigns_iq2xs[(a1 >> 7) & 127] << 8) | | |
| ((uint64_t) ksigns_iq2xs[(a1 >> 14) & 127] << 16) | ((uint64_t) ksigns_iq2xs[(a1 >> 21) & 127] << 24) | | |
| ((uint64_t) ksigns_iq2xs[b1 & 127] << 32) | ((uint64_t) ksigns_iq2xs[(b1 >> 7) & 127] << 40) | | |
| ((uint64_t) ksigns_iq2xs[(b1 >> 14) & 127] << 48) | ((uint64_t) ksigns_iq2xs[(b1 >> 21) & 127] << 56); | |
| m = _cvtu64_mask64(s); | |
| const int sa = 2 * (int) (a1 >> 28) + 1, sb = 2 * (int) (b1 >> 28) + 1; | |
| sc = scales4(sa, sa, sb, sb); | |
| } | |
| }; | |
| template <> struct Fmt<17> { // IQ2_XS: d, qs[32] u16 (9-bit grid index + 7-bit sign index), scales[8] | |
| static constexpr int bytes = 74; | |
| static constexpr float K = 0.125f; | |
| static inline void decode(const uint8_t* b, int j, __m512i& g, __mmask64& m, __m512i& sc) { | |
| const uint8_t* q = b + 2 + 16 * j; | |
| uint16_t v[8]; | |
| std::memcpy(v, q, 16); | |
| g = _mm512_set_epi64((long long) iq2xs_grid[v[7] & 511], (long long) iq2xs_grid[v[6] & 511], | |
| (long long) iq2xs_grid[v[5] & 511], (long long) iq2xs_grid[v[4] & 511], | |
| (long long) iq2xs_grid[v[3] & 511], (long long) iq2xs_grid[v[2] & 511], | |
| (long long) iq2xs_grid[v[1] & 511], (long long) iq2xs_grid[v[0] & 511]); | |
| uint64_t s = 0; | |
| for (int l = 0; l < 8; ++l) s |= (uint64_t) ksigns_iq2xs[v[l] >> 9] << (8 * l); | |
| m = _cvtu64_mask64(s); | |
| const uint8_t s0 = b[66 + 2 * j], s1 = b[66 + 2 * j + 1]; | |
| sc = scales4(2 * (s0 & 15) + 1, 2 * (s0 >> 4) + 1, 2 * (s1 & 15) + 1, 2 * (s1 >> 4) + 1); | |
| } | |
| }; | |
| template <> struct Fmt<22> { // IQ2_S: d, qs[64] (32 grid bytes, 32 sign bytes), qh[8], scales[8] | |
| static constexpr int bytes = 82; | |
| static constexpr float K = 0.125f; | |
| static inline void decode(const uint8_t* b, int j, __m512i& g, __mmask64& m, __m512i& sc) { | |
| const uint8_t* qs = b + 2 + 8 * j; | |
| const uint8_t h0 = b[66 + 2 * j], h1 = b[66 + 2 * j + 1]; | |
| g = _mm512_set_epi64((long long) iq2s_grid[qs[7] | ((h1 << 2) & 0x300)], (long long) iq2s_grid[qs[6] | ((h1 << 4) & 0x300)], | |
| (long long) iq2s_grid[qs[5] | ((h1 << 6) & 0x300)], (long long) iq2s_grid[qs[4] | ((h1 << 8) & 0x300)], | |
| (long long) iq2s_grid[qs[3] | ((h0 << 2) & 0x300)], (long long) iq2s_grid[qs[2] | ((h0 << 4) & 0x300)], | |
| (long long) iq2s_grid[qs[1] | ((h0 << 6) & 0x300)], (long long) iq2s_grid[qs[0] | ((h0 << 8) & 0x300)]); | |
| m = _cvtu64_mask64(u64(b + 2 + 32 + 8 * j)); | |
| const uint8_t s0 = b[74 + 2 * j], s1 = b[74 + 2 * j + 1]; | |
| sc = scales4(2 * (s0 & 15) + 1, 2 * (s0 >> 4) + 1, 2 * (s1 & 15) + 1, 2 * (s1 >> 4) + 1); | |
| } | |
| }; | |
| template <> struct Fmt<18> { // IQ3_XXS: d, qs[64] grid bytes, 8 x u32 (4 x 7-bit sign index + 4-bit scale) | |
| static constexpr int bytes = 98; | |
| static constexpr float K = 0.25f; | |
| static inline void decode(const uint8_t* b, int j, __m512i& g, __mmask64& m, __m512i& sc) { | |
| const uint8_t* q = b + 2 + 16 * j; | |
| // E-2: one gather of the 16 grid words (the scalar lookups assembled with set_epi32 cost ~30 uops) | |
| g = gather ? _mm512_i32gather_epi32(_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*) q)), iq3xxs_grid, 4) | |
| : _mm512_set_epi32((int) iq3xxs_grid[q[15]], (int) iq3xxs_grid[q[14]], (int) iq3xxs_grid[q[13]], (int) iq3xxs_grid[q[12]], | |
| (int) iq3xxs_grid[q[11]], (int) iq3xxs_grid[q[10]], (int) iq3xxs_grid[q[9]], (int) iq3xxs_grid[q[8]], | |
| (int) iq3xxs_grid[q[7]], (int) iq3xxs_grid[q[6]], (int) iq3xxs_grid[q[5]], (int) iq3xxs_grid[q[4]], | |
| (int) iq3xxs_grid[q[3]], (int) iq3xxs_grid[q[2]], (int) iq3xxs_grid[q[1]], (int) iq3xxs_grid[q[0]]); | |
| const uint32_t a = u32(b + 2 + 64 + 8 * j), c = u32(b + 2 + 64 + 8 * j + 4); | |
| const uint64_t s = (uint64_t) ksigns_iq2xs[a & 127] | ((uint64_t) ksigns_iq2xs[(a >> 7) & 127] << 8) | | |
| ((uint64_t) ksigns_iq2xs[(a >> 14) & 127] << 16) | ((uint64_t) ksigns_iq2xs[(a >> 21) & 127] << 24) | | |
| ((uint64_t) ksigns_iq2xs[c & 127] << 32) | ((uint64_t) ksigns_iq2xs[(c >> 7) & 127] << 40) | | |
| ((uint64_t) ksigns_iq2xs[(c >> 14) & 127] << 48) | ((uint64_t) ksigns_iq2xs[(c >> 21) & 127] << 56); | |
| m = _cvtu64_mask64(s); | |
| const int sa = 2 * (int) (a >> 28) + 1, sb = 2 * (int) (c >> 28) + 1; | |
| sc = scales4(sa, sa, sb, sb); | |
| } | |
| }; | |
| template <> struct Fmt<21> { // IQ3_S: d, qs[64], qh[8], signs[32], scales[4] | |
| static constexpr int bytes = 110; | |
| static constexpr float K = 1.0f; | |
| static inline void decode(const uint8_t* b, int j, __m512i& g, __mmask64& m, __m512i& sc) { | |
| const uint8_t* q = b + 2 + 16 * j; | |
| const uint32_t h0 = b[66 + 2 * j], h1 = b[66 + 2 * j + 1]; | |
| if (gather) { // E-2: the 9-bit indices (the high bit from qh under a mask), one gather | |
| __m512i idx = _mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*) q)); | |
| idx = _mm512_mask_add_epi32(idx, (__mmask16) (h0 | (h1 << 8)), idx, _mm512_set1_epi32(256)); | |
| g = _mm512_i32gather_epi32(idx, iq3s_grid, 4); | |
| } else { | |
| g = _mm512_set_epi32(G3(15, h1, 7), G3(14, h1, 6), G3(13, h1, 5), G3(12, h1, 4), | |
| G3(11, h1, 3), G3(10, h1, 2), G3(9, h1, 1), G3(8, h1, 0), | |
| G3(7, h0, 7), G3(6, h0, 6), G3(5, h0, 5), G3(4, h0, 4), | |
| G3(3, h0, 3), G3(2, h0, 2), G3(1, h0, 1), G3(0, h0, 0)); | |
| } | |
| m = _cvtu64_mask64(u64(b + 74 + 8 * j)); | |
| const uint8_t s = b[106 + j]; | |
| const int sa = 2 * (s & 15) + 1, sb = 2 * (s >> 4) + 1; | |
| sc = scales4(sa, sa, sb, sb); | |
| } | |
| }; | |
| template <int TY, int NT> | |
| inline void row_dot(const uint8_t* row, int nblocks, const block_q8_K* const* y, float* res) { | |
| __m512 accf[NT]; | |
| for (int t = 0; t < NT; ++t) accf[t] = _mm512_setzero_ps(); | |
| const __m512i zero = _mm512_setzero_si512(); | |
| for (int i = 0; i < nblocks; ++i) { | |
| const uint8_t* blk = row + (size_t) i * Fmt<TY>::bytes; | |
| // E-2: the weight rows stream from DRAM (4 KB pages when large pages are refused): ask for the bytes a few | |
| // blocks ahead (the next row's start included) before the decode needs them | |
| if (prefetch_ahead > 0) { | |
| const uint8_t* ahead = blk + (size_t) prefetch_ahead; | |
| _mm_prefetch((const char*) ahead, _MM_HINT_T0); | |
| _mm_prefetch((const char*) ahead + 64, _MM_HINT_T0); | |
| } | |
| __m512i acci[NT]; | |
| for (int t = 0; t < NT; ++t) acci[t] = _mm512_setzero_si512(); | |
| for (int j = 0; j < 4; ++j) { | |
| __m512i g, sc; | |
| __mmask64 m; | |
| Fmt<TY>::decode(blk, j, g, m, sc); | |
| for (int t = 0; t < NT; ++t) { | |
| const __m512i yv = _mm512_loadu_si512((const void*) (y[t][i].qs + 64 * j)); | |
| const __m512i ys = _mm512_mask_sub_epi8(yv, m, zero, yv); | |
| acci[t] = _mm512_add_epi32(acci[t], _mm512_madd_epi16(_mm512_maddubs_epi16(g, ys), sc)); | |
| } | |
| } | |
| const float dx = h2f(u16(blk)) * Fmt<TY>::K; | |
| for (int t = 0; t < NT; ++t) | |
| accf[t] = _mm512_fmadd_ps(_mm512_set1_ps(dx * y[t][i].d), _mm512_cvtepi32_ps(acci[t]), accf[t]); | |
| } | |
| for (int t = 0; t < NT; ++t) res[t] = _mm512_reduce_add_ps(accf[t]); | |
| } | |
| template <int TY, int NT> | |
| void gu_rows(const uint8_t* blob, size_t gu_row, size_t up_off, int n, const void* const* act, float* const* ff, | |
| int r0, int r1) { | |
| const block_q8_K* y[NT]; | |
| for (int t = 0; t < NT; ++t) y[t] = (const block_q8_K*) act[t]; | |
| const int nb = n / QK_K; | |
| float g[NT], u[NT]; | |
| for (int r = r0; r < r1; ++r) { | |
| row_dot<TY, NT>(blob + (size_t) r * gu_row, nb, y, g); | |
| row_dot<TY, NT>(blob + up_off + (size_t) r * gu_row, nb, y, u); | |
| for (int t = 0; t < NT; ++t) ff[t][r] = (g[t] / (1.f + std::exp(-g[t]))) * u[t]; | |
| } | |
| } | |
| template <int TY, int NT> | |
| void dot_rows(const uint8_t* w, size_t row_bytes, int n, const void* const* act, float* const* out, int r0, int r1) { | |
| const block_q8_K* y[NT]; | |
| for (int t = 0; t < NT; ++t) y[t] = (const block_q8_K*) act[t]; | |
| float res[NT]; | |
| for (int r = r0; r < r1; ++r) { | |
| row_dot<TY, NT>(w + (size_t) r * row_bytes, n / QK_K, y, res); | |
| for (int t = 0; t < NT; ++t) out[t][r] = res[t]; | |
| } | |
| } | |
| template <int TY> | |
| void gu_rows_nt(int nt, const uint8_t* blob, size_t gu_row, size_t up_off, int n, const void* const* act, | |
| float* const* ff, int r0, int r1) { | |
| switch (nt) { | |
| case 1: gu_rows<TY, 1>(blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 2: gu_rows<TY, 2>(blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 3: gu_rows<TY, 3>(blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 4: gu_rows<TY, 4>(blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 5: gu_rows<TY, 5>(blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 6: gu_rows<TY, 6>(blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 7: gu_rows<TY, 7>(blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| default: gu_rows<TY, 8>(blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| } | |
| } | |
| template <int TY> | |
| void dot_rows_nt(int nt, const uint8_t* w, size_t row_bytes, int n, const void* const* act, float* const* out, int r0, | |
| int r1) { | |
| switch (nt) { | |
| case 1: dot_rows<TY, 1>(w, row_bytes, n, act, out, r0, r1); break; | |
| case 2: dot_rows<TY, 2>(w, row_bytes, n, act, out, r0, r1); break; | |
| case 3: dot_rows<TY, 3>(w, row_bytes, n, act, out, r0, r1); break; | |
| case 4: dot_rows<TY, 4>(w, row_bytes, n, act, out, r0, r1); break; | |
| default: for (int t0 = 0; t0 < nt; t0 += 4) { | |
| const int k = nt - t0 < 4 ? nt - t0 : 4; | |
| dot_rows_nt<TY>(k, w, row_bytes, n, act + t0, out + t0, r0, r1); | |
| } | |
| } | |
| } | |
| } // namespace | |
| bool iq512_supported(int type) noexcept { | |
| return type == 16 || type == 17 || type == 18 || type == 21 || type == 22; | |
| } | |
| void iq512_gu_rows(int type, const uint8_t* blob, size_t gu_row, size_t up_off, int n, const void* const* act, int nt, | |
| float* const* ff, int r0, int r1) { | |
| switch (type) { | |
| case 16: gu_rows_nt<16>(nt, blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 17: gu_rows_nt<17>(nt, blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 18: gu_rows_nt<18>(nt, blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 21: gu_rows_nt<21>(nt, blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| case 22: gu_rows_nt<22>(nt, blob, gu_row, up_off, n, act, ff, r0, r1); break; | |
| default: break; | |
| } | |
| } | |
| void iq512_rows(int type, const uint8_t* w, size_t row_bytes, int n, const void* const* act, int nt, float* const* out, | |
| int r0, int r1) { | |
| switch (type) { | |
| case 16: dot_rows_nt<16>(nt, w, row_bytes, n, act, out, r0, r1); break; | |
| case 17: dot_rows_nt<17>(nt, w, row_bytes, n, act, out, r0, r1); break; | |
| case 18: dot_rows_nt<18>(nt, w, row_bytes, n, act, out, r0, r1); break; | |
| case 21: dot_rows_nt<21>(nt, w, row_bytes, n, act, out, r0, r1); break; | |
| case 22: dot_rows_nt<22>(nt, w, row_bytes, n, act, out, r0, r1); break; | |
| default: break; | |
| } | |
| } | |
| } // namespace strata::kernels::cpu | |