Download tmk.c from SyzygyResearch/Mach-Boom: direct link, hf CLI and curl.
- Browser
- Download file 18.5 kB
-
https://huggingface.co/SyzygyResearch/Mach-Boom/resolve/main/tmk.c
- Command line
-
hf download hf://SyzygyResearch/Mach-Boom/tmk.c
-
curl -L -o tmk.c https://huggingface.co/SyzygyResearch/Mach-Boom/resolve/main/tmk.c
18.5 kB
| static inline uint64_t be64(const uint8_t* p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v); } | |
| static inline void row_words(const uint8_t* tp, int step, int r, uint64_t* w, uint32_t* b8) { | |
| const int TB = 16 * step; | |
| if (step == 6) { | |
| if (r < 15) *w = be64(tp + 6 * r); | |
| else { uint8_t t[8]; memcpy(t, tp + 90, 6); memcpy(t + 6, tp, 2); *w = be64(t); } | |
| *b8 = 0; | |
| } else { | |
| *w = be64(tp + 8 * r); | |
| *b8 = tp[(8 * r + 8) % TB]; | |
| } | |
| } | |
| static inline uint32_t state_k(uint64_t w, uint32_t b8, int step, int k) { | |
| if (step == 6) return (uint32_t)(w >> (48 - 6 * k)) & 0xFFFF; | |
| if (k < 7) return (uint32_t)(w >> (48 - 8 * k)) & 0xFFFF; | |
| return (uint32_t)(((w & 0xFF) << 8) | b8); | |
| } | |
| void tmk_tlut10(const float* tl, float* t10) { | |
| for (int e = 0; e < 1024; e++) { | |
| t10[2 * e] = (e >> 9) ? -tl[2 * (e & 511)] : tl[2 * (e & 511)]; | |
| t10[2 * e + 1] = tl[2 * (e & 511) + 1]; | |
| } | |
| } | |
| static inline __m512 row16(uint64_t w, uint32_t b8, int step, const uint64_t* t10) { | |
| __m512i s; | |
| if (step == 6) { | |
| const __m512i sh = _mm512_setr_epi64(48, 42, 36, 30, 24, 18, 12, 6); | |
| s = _mm512_srlv_epi64(_mm512_set1_epi64((long long)w), sh); | |
| } else { | |
| const __m512i sh = _mm512_setr_epi64(48, 40, 32, 24, 16, 8, 0, 0); | |
| __m512i wv = _mm512_mask_set1_epi64(_mm512_set1_epi64((long long)w), 0x80, (long long)((w << 8) | b8)); | |
| s = _mm512_srlv_epi64(wv, sh); | |
| } | |
| s = _mm512_and_si512(s, _mm512_set1_epi64(0xFFFF)); | |
| __m512i p = _mm512_mul_epu32(s, _mm512_add_epi64(s, _mm512_set1_epi64(1))); | |
| __m512i ix = _mm512_and_si512(_mm512_srli_epi64(p, 6), _mm512_set1_epi64(1023)); | |
| return _mm512_castsi512_ps(_mm512_i64gather_epi64(ix, (const long long*)t10, 8)); | |
| } | |
| static inline __m256i row_idx8(const uint8_t* tp, int step, int r) { | |
| if (step == 6) { | |
| uint64_t raw; | |
| if (r < 15) memcpy(&raw, tp + 6 * r, 8); | |
| else { uint8_t t[8]; memcpy(t, tp + 90, 6); memcpy(t + 6, tp, 2); memcpy(&raw, t, 8); } | |
| const __m256i shuf = _mm256_setr_epi8(1, 0, -1, -1, 2, 1, 0, -1, 3, 2, 1, -1, 4, 3, 2, -1, | |
| 4, 3, 2, -1, 5, 4, 3, -1, 6, 5, 4, -1, 7, 6, 5, -1); | |
| __m256i v = _mm256_shuffle_epi8(_mm256_set1_epi64x((long long)raw), shuf); | |
| __m256i s = _mm256_and_si256(_mm256_srlv_epi32(v, _mm256_setr_epi32(0, 2, 4, 6, 0, 2, 4, 6)), _mm256_set1_epi32(0xFFFF)); | |
| return _mm256_srli_epi32(_mm256_mullo_epi16(s, _mm256_add_epi32(s, _mm256_set1_epi32(1))), 6); | |
| } else { | |
| __m128i raw; | |
| if (r < 15) raw = _mm_loadu_si128((const __m128i*)(tp + 8 * r)); | |
| else { uint8_t t[16] = {0}; memcpy(t, tp + 120, 8); t[8] = tp[0]; raw = _mm_loadu_si128((const __m128i*)t); } | |
| const __m256i shuf = _mm256_setr_epi8(1, 0, -1, -1, 2, 1, -1, -1, 3, 2, -1, -1, 4, 3, -1, -1, | |
| 5, 4, -1, -1, 6, 5, -1, -1, 7, 6, -1, -1, 8, 7, -1, -1); | |
| __m256i s = _mm256_shuffle_epi8(_mm256_broadcastsi128_si256(raw), shuf); | |
| return _mm256_srli_epi32(_mm256_mullo_epi16(s, _mm256_add_epi32(s, _mm256_set1_epi32(1))), 6); | |
| } | |
| } | |
| static inline void row16(const uint8_t* tp, int step, int r, const uint64_t* t10, __m256* lo, __m256* hi) { | |
| __m256i ix = row_idx8(tp, step, r); | |
| *lo = _mm256_castsi256_ps(_mm256_i32gather_epi64((const long long*)t10, _mm256_castsi256_si128(ix), 8)); | |
| *hi = _mm256_castsi256_ps(_mm256_i32gather_epi64((const long long*)t10, _mm256_extracti128_si256(ix, 1), 8)); | |
| } | |
| static inline void row16_scalar(uint64_t w, uint32_t b8, int step, const float* t10, float* out) { | |
| for (int k = 0; k < 8; k++) { | |
| uint32_t s = state_k(w, b8, step, k), e = ((s * (s + 1)) & 0xFFFF) >> 6; | |
| out[2 * k] = t10[2 * e]; out[2 * k + 1] = t10[2 * e + 1]; | |
| } | |
| } | |
| static __m512i PI6[8], PI8[8], MS6; | |
| static volatile int vb_ready = 0; | |
| __attribute__((constructor)) static void vb_init(void) { | |
| uint8_t b[64]; | |
| for (int p = 0; p < 8; p++) { | |
| for (int l = 0; l < 8; l++) { int r = 2 * p + (l >= 4); for (int q = 0; q < 8; q++) b[8 * l + q] = (uint8_t)(6 * r + 7 - q); } | |
| PI6[p] = _mm512_loadu_si512(b); | |
| for (int d = 0; d < 16; d++) { | |
| int r = 2 * p + (d >= 8), k = d % 8; | |
| b[4 * d] = (uint8_t)((8 * r + k + 1) % 128); b[4 * d + 1] = (uint8_t)(8 * r + k); b[4 * d + 2] = 0; b[4 * d + 3] = 0; | |
| } | |
| PI8[p] = _mm512_loadu_si512(b); | |
| } | |
| for (int l = 0; l < 8; l++) for (int j = 0; j < 2; j++) { | |
| int k = 2 * (l % 4) + j, o = 48 - 6 * k; | |
| b[8 * l + 4 * j] = (uint8_t)o; b[8 * l + 4 * j + 1] = (uint8_t)(o + 8); b[8 * l + 4 * j + 2] = 0; b[8 * l + 4 * j + 3] = 0; | |
| } | |
| MS6 = _mm512_loadu_si512(b); | |
| vb_ready = 1; | |
| } | |
| static inline void tile_regs(const uint8_t* tp, int step, __m512i* lo, __m512i* hi) { | |
| *lo = _mm512_loadu_si512(tp); | |
| if (step == 6) | |
| *hi = _mm512_inserti64x4(_mm512_castsi256_si512(_mm256_loadu_si256((const __m256i*)(tp + 64))), _mm512_castsi512_si256(*lo), 1); | |
| else | |
| *hi = _mm512_loadu_si512(tp + 64); | |
| } | |
| static inline __m512i pair_idx(__m512i lo, __m512i hi, int step, int p) { | |
| __m512i s; | |
| if (step == 6) s = _mm512_and_si512(_mm512_multishift_epi64_epi8(MS6, _mm512_permutex2var_epi8(lo, PI6[p], hi)), _mm512_set1_epi32(0xFFFF)); | |
| else s = _mm512_maskz_permutex2var_epi8(0x3333333333333333ULL, lo, PI8[p], hi); | |
| return _mm512_srli_epi32(_mm512_mullo_epi16(s, _mm512_add_epi32(s, _mm512_set1_epi32(1))), 6); | |
| } | |
| const char* tmk_isa(void) { | |
| return "avx512vbmi"; | |
| return "avx512"; | |
| return "avx2"; | |
| return "scalar"; | |
| } | |
| void tmk_qgemv_rows(const uint8_t* codes, int step, int n, const float* tlut, const float* x, float* z, int t0, int t1) { | |
| const int nb = n / 16, TB = 16 * step; | |
| if (!vb_ready) vb_init(); | |
| for (int t = t0; t < t1; t++) { | |
| const uint8_t* strip = codes + (size_t)t * nb * TB; | |
| __m512 acc[16]; | |
| for (int r = 0; r < 16; r++) acc[r] = _mm512_setzero_ps(); | |
| for (int j = 0; j < nb; j++) { | |
| __m512i lo, hi; tile_regs(strip + (size_t)j * TB, step, &lo, &hi); | |
| const __m512 xv = _mm512_loadu_ps(x + 16 * j); | |
| for (int p = 0; p < 8; p++) { | |
| __m512i ix = pair_idx(lo, hi, step, p); | |
| acc[2 * p] = _mm512_fmadd_ps(_mm512_castsi512_ps(_mm512_i32gather_epi64(_mm512_castsi512_si256(ix), (const long long*)tlut, 8)), xv, acc[2 * p]); | |
| acc[2 * p + 1] = _mm512_fmadd_ps(_mm512_castsi512_ps(_mm512_i32gather_epi64(_mm512_extracti64x4_epi64(ix, 1), (const long long*)tlut, 8)), xv, acc[2 * p + 1]); | |
| } | |
| } | |
| for (int r = 0; r < 16; r++) z[16 * t + r] = _mm512_reduce_add_ps(acc[r]); | |
| } | |
| for (int t = t0; t < t1; t++) { | |
| const uint8_t* strip = codes + (size_t)t * nb * TB; | |
| for (int r = 0; r < 16; r++) { | |
| __m512 a = _mm512_setzero_ps(), b = _mm512_setzero_ps(); | |
| int j = 0; | |
| for (; j + 1 < nb; j += 2) { | |
| uint64_t w0, w1; uint32_t c0, c1; | |
| row_words(strip + j * TB, step, r, &w0, &c0); row_words(strip + (j + 1) * TB, step, r, &w1, &c1); | |
| a = _mm512_fmadd_ps(row16(w0, c0, step, (const uint64_t*)tlut), _mm512_loadu_ps(x + 16 * j), a); | |
| b = _mm512_fmadd_ps(row16(w1, c1, step, (const uint64_t*)tlut), _mm512_loadu_ps(x + 16 * j + 16), b); | |
| } | |
| for (; j < nb; j++) { | |
| uint64_t w0; uint32_t c0; row_words(strip + j * TB, step, r, &w0, &c0); | |
| a = _mm512_fmadd_ps(row16(w0, c0, step, (const uint64_t*)tlut), _mm512_loadu_ps(x + 16 * j), a); | |
| } | |
| z[16 * t + r] = _mm512_reduce_add_ps(_mm512_add_ps(a, b)); | |
| __m256 a0 = _mm256_setzero_ps(), a1 = _mm256_setzero_ps(); | |
| for (int j = 0; j < nb; j++) { | |
| __m256 lo, hi; | |
| row16(strip + (size_t)j * TB, step, r, (const uint64_t*)tlut, &lo, &hi); | |
| a0 = _mm256_fmadd_ps(lo, _mm256_loadu_ps(x + 16 * j), a0); | |
| a1 = _mm256_fmadd_ps(hi, _mm256_loadu_ps(x + 16 * j + 8), a1); | |
| } | |
| __m256 s = _mm256_add_ps(a0, a1); | |
| __m128 h = _mm_add_ps(_mm256_castps256_ps128(s), _mm256_extractf128_ps(s, 1)); | |
| h = _mm_hadd_ps(h, h); h = _mm_hadd_ps(h, h); | |
| z[16 * t + r] = _mm_cvtss_f32(h); | |
| float acc = 0.f, v[16]; | |
| for (int j = 0; j < nb; j++) { | |
| uint64_t w; uint32_t c; row_words(strip + j * TB, step, r, &w, &c); | |
| row16_scalar(w, c, step, tlut, v); | |
| for (int q = 0; q < 16; q++) acc += v[q] * x[16 * j + q]; | |
| } | |
| z[16 * t + r] = acc; | |
| } | |
| } | |
| } | |
| typedef struct { const uint8_t* codes; const float* tlut; const float* x; float* z; int step, n, t0, t1; } gjob; | |
| static gjob jobs[MAXT]; | |
| static pthread_t thr[MAXT]; | |
| static int nthr = 1; | |
| static atomic_int gen = 0, pending = 0, quit = 0; | |
| static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER; | |
| static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; | |
| static inline void cpu_relax(void) { | |
| __builtin_ia32_pause(); | |
| } | |
| static void* worker(void* arg) { | |
| const int id = (int)(intptr_t)arg; | |
| int seen = 0; | |
| for (;;) { | |
| int g, spins = 0; | |
| while ((g = atomic_load_explicit(&gen, memory_order_acquire)) == seen && spins < 20000) { cpu_relax(); spins++; } | |
| if (g == seen) { | |
| pthread_mutex_lock(&mu); | |
| while ((g = atomic_load_explicit(&gen, memory_order_acquire)) == seen) pthread_cond_wait(&cv, &mu); | |
| pthread_mutex_unlock(&mu); | |
| } | |
| seen = g; | |
| if (atomic_load(&quit)) return NULL; | |
| const gjob* j = &jobs[id]; | |
| if (j->t1 > j->t0) tmk_qgemv_rows(j->codes, j->step, j->n, j->tlut, j->x, j->z, j->t0, j->t1); | |
| atomic_fetch_sub_explicit(&pending, 1, memory_order_acq_rel); | |
| } | |
| } | |
| int tmk_threads(int n) { | |
| if (n < 1) n = 1; | |
| if (n > MAXT) n = MAXT; | |
| if (n == nthr) return nthr; | |
| if (nthr > 1) { | |
| atomic_store(&quit, 1); | |
| pthread_mutex_lock(&mu); atomic_fetch_add(&gen, 1); pthread_cond_broadcast(&cv); pthread_mutex_unlock(&mu); | |
| for (int i = 1; i < nthr; i++) pthread_join(thr[i], NULL); | |
| atomic_store(&quit, 0); | |
| } | |
| atomic_store(&gen, 0); | |
| nthr = n; | |
| for (int i = 1; i < nthr; i++) pthread_create(&thr[i], NULL, worker, (void*)(intptr_t)i); | |
| return nthr; | |
| } | |
| void tmk_qgemv(const uint8_t* codes, int step, int m, int n, const float* tlut, const float* x, float* z) { | |
| const int tiles = m / 16, T = nthr; | |
| if (T == 1 || tiles < 2 * T || (size_t)m * n < (1u << 15)) { tmk_qgemv_rows(codes, step, n, tlut, x, z, 0, tiles); return; } | |
| const int per = (tiles + T - 1) / T; | |
| for (int w = 1; w < T; w++) { | |
| int t0 = w * per, t1 = (w + 1) * per; | |
| if (t0 > tiles) t0 = tiles; | |
| if (t1 > tiles) t1 = tiles; | |
| jobs[w] = (gjob){codes, tlut, x, z, step, n, t0, t1}; | |
| } | |
| atomic_store_explicit(&pending, T - 1, memory_order_release); | |
| pthread_mutex_lock(&mu); | |
| atomic_fetch_add_explicit(&gen, 1, memory_order_acq_rel); | |
| pthread_cond_broadcast(&cv); | |
| pthread_mutex_unlock(&mu); | |
| tmk_qgemv_rows(codes, step, n, tlut, x, z, 0, per < tiles ? per : tiles); | |
| while (atomic_load_explicit(&pending, memory_order_acquire) > 0) cpu_relax(); | |
| } | |
| void tmk_qdense(const uint8_t* codes, int step, int m, int n, const float* tlut, float scale, float* out) { | |
| const int nb = n / 16, TB = 16 * step; | |
| if (!vb_ready) vb_init(); | |
| const __m512 sc = _mm512_set1_ps(scale); | |
| for (int t = 0; t < m / 16; t++) | |
| for (int j = 0; j < nb; j++) { | |
| __m512i lo, hi; tile_regs(codes + ((size_t)t * nb + j) * TB, step, &lo, &hi); | |
| float* o = out + (size_t)(16 * t) * n + 16 * j; | |
| for (int p = 0; p < 8; p++) { | |
| __m512i ix = pair_idx(lo, hi, step, p); | |
| _mm512_storeu_ps(o + (size_t)(2 * p) * n, _mm512_mul_ps(_mm512_castsi512_ps(_mm512_i32gather_epi64(_mm512_castsi512_si256(ix), (const long long*)tlut, 8)), sc)); | |
| _mm512_storeu_ps(o + (size_t)(2 * p + 1) * n, _mm512_mul_ps(_mm512_castsi512_ps(_mm512_i32gather_epi64(_mm512_extracti64x4_epi64(ix, 1), (const long long*)tlut, 8)), sc)); | |
| } | |
| } | |
| for (int t = 0; t < m / 16; t++) | |
| for (int j = 0; j < nb; j++) { | |
| const uint8_t* tp = codes + ((size_t)t * nb + j) * TB; | |
| for (int r = 0; r < 16; r++) { | |
| uint64_t w; uint32_t c; | |
| row_words(tp, step, r, &w, &c); | |
| float* o = out + (size_t)(16 * t + r) * n + 16 * j; | |
| _mm512_storeu_ps(o, _mm512_mul_ps(row16(w, c, step, (const uint64_t*)tlut), _mm512_set1_ps(scale))); | |
| __m256 lo, hi; row16(tp, step, r, (const uint64_t*)tlut, &lo, &hi); (void)w; (void)c; | |
| _mm256_storeu_ps(o, _mm256_mul_ps(lo, _mm256_set1_ps(scale))); | |
| _mm256_storeu_ps(o + 8, _mm256_mul_ps(hi, _mm256_set1_ps(scale))); | |
| float v[16]; row16_scalar(w, c, step, tlut, v); | |
| for (int q = 0; q < 16; q++) o[q] = v[q] * scale; | |
| } | |
| } | |
| } | |
| static inline __m512 fwht16(__m512 v) { | |
| __m512 b; | |
| b = _mm512_permute_ps(v, 0xB1); v = _mm512_mask_sub_ps(_mm512_add_ps(v, b), 0xAAAA, b, v); | |
| b = _mm512_permute_ps(v, 0x4E); v = _mm512_mask_sub_ps(_mm512_add_ps(v, b), 0xCCCC, b, v); | |
| b = _mm512_shuffle_f32x4(v, v, 0xB1); v = _mm512_mask_sub_ps(_mm512_add_ps(v, b), 0xF0F0, b, v); | |
| b = _mm512_shuffle_f32x4(v, v, 0x4E); v = _mm512_mask_sub_ps(_mm512_add_ps(v, b), 0xFF00, b, v); | |
| return v; | |
| } | |
| static inline __m512 signs16(__m512 v, const uint8_t* bits, int i) { | |
| __mmask16 k = (__mmask16)~(bits[i >> 3] | (bits[(i >> 3) + 1] << 8)); | |
| __m512i vi = _mm512_castps_si512(v); | |
| return _mm512_castsi512_ps(_mm512_mask_xor_epi32(vi, k, vi, _mm512_set1_epi32((int)0x80000000))); | |
| } | |
| static inline __m256 fwht8(__m256 v) { | |
| __m256 b; | |
| b = _mm256_permute_ps(v, 0xB1); v = _mm256_blend_ps(_mm256_add_ps(v, b), _mm256_sub_ps(b, v), 0xAA); | |
| b = _mm256_permute_ps(v, 0x4E); v = _mm256_blend_ps(_mm256_add_ps(v, b), _mm256_sub_ps(b, v), 0xCC); | |
| b = _mm256_permute2f128_ps(v, v, 0x01); v = _mm256_blend_ps(_mm256_add_ps(v, b), _mm256_sub_ps(b, v), 0xF0); | |
| return v; | |
| } | |
| static inline __m256 signs8(__m256 v, const uint8_t* bits, int i) { | |
| const __m256i bitv = _mm256_setr_epi32(1, 2, 4, 8, 16, 32, 64, 128); | |
| __m256i on = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_set1_epi32(bits[i >> 3]), bitv), _mm256_setzero_si256()); | |
| return _mm256_xor_ps(v, _mm256_castsi256_ps(_mm256_and_si256(on, _mm256_set1_epi32((int)0x80000000)))); | |
| } | |
| static void rot_row(float* v, int n, const uint8_t* pre, const uint8_t* post, float scale) { | |
| if (n >= VW) { | |
| const __m512 sc = _mm512_set1_ps(scale); | |
| for (int i = 0; i < n; i += 16) { | |
| __m512 a = _mm512_loadu_ps(v + i); | |
| if (pre) a = signs16(a, pre, i); | |
| _mm512_storeu_ps(v + i, fwht16(_mm512_mul_ps(a, sc))); | |
| } | |
| for (int h = 16; h < n; h <<= 1) | |
| for (int i = 0; i < n; i += 2 * h) | |
| for (int j = i; j < i + h; j += 16) { | |
| __m512 a = _mm512_loadu_ps(v + j), b = _mm512_loadu_ps(v + j + h); | |
| _mm512_storeu_ps(v + j, _mm512_add_ps(a, b)); _mm512_storeu_ps(v + j + h, _mm512_sub_ps(a, b)); | |
| } | |
| if (post) for (int i = 0; i < n; i += 16) _mm512_storeu_ps(v + i, signs16(_mm512_loadu_ps(v + i), post, i)); | |
| const __m256 sc = _mm256_set1_ps(scale); | |
| for (int i = 0; i < n; i += 8) { | |
| __m256 a = _mm256_loadu_ps(v + i); | |
| if (pre) a = signs8(a, pre, i); | |
| _mm256_storeu_ps(v + i, fwht8(_mm256_mul_ps(a, sc))); | |
| } | |
| for (int h = 8; h < n; h <<= 1) | |
| for (int i = 0; i < n; i += 2 * h) | |
| for (int j = i; j < i + h; j += 8) { | |
| __m256 a = _mm256_loadu_ps(v + j), b = _mm256_loadu_ps(v + j + h); | |
| _mm256_storeu_ps(v + j, _mm256_add_ps(a, b)); _mm256_storeu_ps(v + j + h, _mm256_sub_ps(a, b)); | |
| } | |
| if (post) for (int i = 0; i < n; i += 8) _mm256_storeu_ps(v + i, signs8(_mm256_loadu_ps(v + i), post, i)); | |
| return; | |
| } | |
| uint32_t* u = (uint32_t*)v; | |
| if (pre) for (int i = 0; i < n; i++) u[i] ^= (uint32_t)(~(pre[i >> 3] >> (i & 7)) & 1) << 31; | |
| for (int i = 0; i < n; i++) v[i] *= scale; | |
| for (int h = 1; h < n; h <<= 1) | |
| for (int i = 0; i < n; i += 2 * h) | |
| for (int j = i; j < i + h; j++) { float a = v[j], b = v[j + h]; v[j] = a + b; v[j + h] = a - b; } | |
| if (post) for (int i = 0; i < n; i++) u[i] ^= (uint32_t)(~(post[i >> 3] >> (i & 7)) & 1) << 31; | |
| } | |
| void tmk_rot(float* x, int T, int n, const uint8_t* pre, const uint8_t* post, float scale) { | |
| for (int t = 0; t < T; t++) rot_row(x + (size_t)t * n, n, pre, post, scale); | |
| } | |
| void tmk_fwht(float* x, int T, int n) { tmk_rot(x, T, n, NULL, NULL, 1.0f / sqrtf((float)n)); } | |
| void tmk_signs(float* x, int T, int n, const uint8_t* bits) { | |
| for (int t = 0; t < T; t++) { | |
| uint32_t* v = (uint32_t*)(x + (size_t)t * n); | |
| for (int i = 0; i < n; i++) v[i] ^= (uint32_t)(~(bits[i >> 3] >> (i & 7)) & 1) << 31; | |
| } | |
| } | |
| void tmk_linear_vec(const uint8_t* codes, int step, int m, int n, const float* tlut, const uint8_t* su, | |
| const uint8_t* sv, float wscale, const float* x, float* y, float* tmp) { | |
| memcpy(tmp, x, sizeof(float) * n); | |
| rot_row(tmp, n, su, NULL, 1.0f / sqrtf((float)n)); | |
| tmk_qgemv(codes, step, m, n, tlut, tmp, y); | |
| rot_row(y, m, NULL, sv, wscale / sqrtf((float)m)); | |
| } | |