@clutor/cli 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/c/Makefile +34 -2
- package/c/src/forward.c +354 -0
- package/c/src/forward.h +69 -0
- package/c/src/main.c +337 -6
- package/c/src/meta.c +118 -0
- package/c/src/meta.h +22 -0
- package/c/src/quant.c +360 -0
- package/c/src/quant.h +43 -0
- package/c/src/stream.c +6 -3
- package/c/src/stream.h +2 -0
- package/c/src/tokenizer.c +287 -0
- package/c/src/tokenizer.h +25 -0
- package/c/src/xs.c +215 -0
- package/c/src/xs.h +39 -0
- package/dist/bin.js +2 -1
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-YL63VENN.js → chunk-2FBPYNHP.js} +238 -33
- package/dist/chunk-2FBPYNHP.js.map +1 -0
- package/dist/{chunk-RBSFKJ6L.js → chunk-BZZUO7XX.js} +35 -6
- package/dist/chunk-BZZUO7XX.js.map +1 -0
- package/dist/index.d.ts +26 -7
- package/dist/index.js +8 -2
- package/dist/{repl-OVODHQI5.js → repl-DOWMDXFR.js} +2 -2
- package/package.json +2 -2
- package/dist/chunk-RBSFKJ6L.js.map +0 -1
- package/dist/chunk-YL63VENN.js.map +0 -1
- /package/dist/{repl-OVODHQI5.js.map → repl-DOWMDXFR.js.map} +0 -0
package/c/src/quant.c
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
#include "quant.h"
|
|
2
|
+
#include "kernel.h"
|
|
3
|
+
#include <math.h>
|
|
4
|
+
#include <pthread.h>
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
#include <unistd.h>
|
|
8
|
+
|
|
9
|
+
/* Block layouts are ggml's (llama.cpp/ggml-common.h); the maths below follows its dequantize_row_* functions. */
|
|
10
|
+
#define QK_K 256
|
|
11
|
+
typedef struct { uint16_t d; uint8_t qs[16]; } b_q4_0;
|
|
12
|
+
typedef struct { uint16_t d; uint8_t qh[4]; uint8_t qs[16]; } b_q5_0;
|
|
13
|
+
typedef struct { uint16_t d; int8_t qs[32]; } b_q8_0;
|
|
14
|
+
typedef struct { uint16_t d, dmin; uint8_t scales[12]; uint8_t qs[QK_K / 2]; } b_q4_K;
|
|
15
|
+
typedef struct { uint16_t d, dmin; uint8_t scales[12]; uint8_t qh[QK_K / 8]; uint8_t qs[QK_K / 2]; } b_q5_K;
|
|
16
|
+
typedef struct { uint8_t ql[QK_K / 2]; uint8_t qh[QK_K / 4]; int8_t scales[QK_K / 16]; uint16_t d; } b_q6_K;
|
|
17
|
+
_Static_assert(sizeof(b_q4_K) == 144 && sizeof(b_q5_K) == 176 && sizeof(b_q6_K) == 210 && sizeof(b_q5_0) == 22 && sizeof(b_q8_0) == 34, "block sizes");
|
|
18
|
+
|
|
19
|
+
int cl_type_supported(uint32_t t) { return cl_type_block(t) != 0; }
|
|
20
|
+
uint32_t cl_type_block(uint32_t t) {
|
|
21
|
+
switch (t) { case CL_T_F32: case CL_T_F16: case CL_T_BF16: return 1; case CL_T_Q4_0: case CL_T_Q5_0: case CL_T_Q8_0: return 32; case CL_T_Q4_K: case CL_T_Q5_K: case CL_T_Q6_K: return 256; default: return 0; }
|
|
22
|
+
}
|
|
23
|
+
uint32_t cl_type_bytes(uint32_t t) {
|
|
24
|
+
switch (t) { case CL_T_F32: return 4; case CL_T_F16: case CL_T_BF16: return 2; case CL_T_Q4_0: return 18; case CL_T_Q5_0: return 22; case CL_T_Q8_0: return 34; case CL_T_Q4_K: return 144; case CL_T_Q5_K: return 176; case CL_T_Q6_K: return 210; default: return 0; }
|
|
25
|
+
}
|
|
26
|
+
uint64_t cl_row_bytes(uint32_t t, uint64_t n) { return n / cl_type_block(t) * cl_type_bytes(t); }
|
|
27
|
+
|
|
28
|
+
static float bf16(uint16_t h) { uint32_t b = (uint32_t)h << 16; float f; memcpy(&f, &b, 4); return f; }
|
|
29
|
+
|
|
30
|
+
/* Q4_K / Q5_K store 8 six-bit (scale, min) pairs packed into 12 bytes. */
|
|
31
|
+
static void scale_min_k4(int j, const uint8_t *q, uint8_t *d, uint8_t *m) {
|
|
32
|
+
if (j < 4) { *d = q[j] & 63; *m = q[j + 4] & 63; }
|
|
33
|
+
else { *d = (q[j + 4] & 0xF) | ((q[j - 4] >> 6) << 4); *m = (q[j + 4] >> 4) | ((q[j] >> 6) << 4); }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static void deq_q4_K(const b_q4_K *x, float *y) {
|
|
37
|
+
const float d = cl_f16_to_f32(x->d), min = cl_f16_to_f32(x->dmin);
|
|
38
|
+
const uint8_t *q = x->qs;
|
|
39
|
+
int is = 0;
|
|
40
|
+
for (int j = 0; j < QK_K; j += 64) {
|
|
41
|
+
uint8_t sc, m;
|
|
42
|
+
scale_min_k4(is + 0, x->scales, &sc, &m);
|
|
43
|
+
float d1 = d * sc, m1 = min * m;
|
|
44
|
+
scale_min_k4(is + 1, x->scales, &sc, &m);
|
|
45
|
+
float d2 = d * sc, m2 = min * m;
|
|
46
|
+
for (int l = 0; l < 32; l++) *y++ = d1 * (q[l] & 0xF) - m1;
|
|
47
|
+
for (int l = 0; l < 32; l++) *y++ = d2 * (q[l] >> 4) - m2;
|
|
48
|
+
q += 32; is += 2;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static void deq_q5_K(const b_q5_K *x, float *y) {
|
|
53
|
+
const float d = cl_f16_to_f32(x->d), min = cl_f16_to_f32(x->dmin);
|
|
54
|
+
const uint8_t *ql = x->qs, *qh = x->qh;
|
|
55
|
+
int is = 0;
|
|
56
|
+
uint8_t u1 = 1, u2 = 2;
|
|
57
|
+
for (int j = 0; j < QK_K; j += 64) {
|
|
58
|
+
uint8_t sc, m;
|
|
59
|
+
scale_min_k4(is + 0, x->scales, &sc, &m);
|
|
60
|
+
float d1 = d * sc, m1 = min * m;
|
|
61
|
+
scale_min_k4(is + 1, x->scales, &sc, &m);
|
|
62
|
+
float d2 = d * sc, m2 = min * m;
|
|
63
|
+
for (int l = 0; l < 32; l++) *y++ = d1 * ((ql[l] & 0xF) + (qh[l] & u1 ? 16 : 0)) - m1;
|
|
64
|
+
for (int l = 0; l < 32; l++) *y++ = d2 * ((ql[l] >> 4) + (qh[l] & u2 ? 16 : 0)) - m2;
|
|
65
|
+
ql += 32; is += 2; u1 <<= 2; u2 <<= 2;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static void deq_q6_K(const b_q6_K *x, float *y) {
|
|
70
|
+
const float d = cl_f16_to_f32(x->d);
|
|
71
|
+
const uint8_t *ql = x->ql, *qh = x->qh;
|
|
72
|
+
const int8_t *sc = x->scales;
|
|
73
|
+
for (int n = 0; n < QK_K; n += 128) {
|
|
74
|
+
for (int l = 0; l < 32; l++) {
|
|
75
|
+
int is = l / 16;
|
|
76
|
+
int q1 = (int8_t)((ql[l] & 0xF) | (((qh[l] >> 0) & 3) << 4)) - 32;
|
|
77
|
+
int q2 = (int8_t)((ql[l + 32] & 0xF) | (((qh[l] >> 2) & 3) << 4)) - 32;
|
|
78
|
+
int q3 = (int8_t)((ql[l] >> 4) | (((qh[l] >> 4) & 3) << 4)) - 32;
|
|
79
|
+
int q4 = (int8_t)((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4)) - 32;
|
|
80
|
+
y[l] = d * sc[is + 0] * q1;
|
|
81
|
+
y[l + 32] = d * sc[is + 2] * q2;
|
|
82
|
+
y[l + 64] = d * sc[is + 4] * q3;
|
|
83
|
+
y[l + 96] = d * sc[is + 6] * q4;
|
|
84
|
+
}
|
|
85
|
+
y += 128; ql += 64; qh += 32; sc += 8;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
void cl_dequant_row(uint32_t type, const void *src, float *dst, int n) {
|
|
90
|
+
const uint8_t *p = src;
|
|
91
|
+
switch (type) {
|
|
92
|
+
case CL_T_F32: memcpy(dst, src, (size_t)n * 4); return;
|
|
93
|
+
case CL_T_F16: for (int i = 0; i < n; i++) { uint16_t h; memcpy(&h, p + 2 * i, 2); dst[i] = cl_f16_to_f32(h); } return;
|
|
94
|
+
case CL_T_BF16: for (int i = 0; i < n; i++) { uint16_t h; memcpy(&h, p + 2 * i, 2); dst[i] = bf16(h); } return;
|
|
95
|
+
case CL_T_Q4_0: for (int b = 0; b < n / 32; b++) {
|
|
96
|
+
const b_q4_0 *x = (const b_q4_0 *)(const void *)(p + (size_t)b * 18); float d = cl_f16_to_f32(x->d);
|
|
97
|
+
for (int j = 0; j < 16; j++) { dst[b * 32 + j] = ((x->qs[j] & 0xF) - 8) * d; dst[b * 32 + j + 16] = ((x->qs[j] >> 4) - 8) * d; }
|
|
98
|
+
} return;
|
|
99
|
+
case CL_T_Q5_0: for (int b = 0; b < n / 32; b++) {
|
|
100
|
+
const b_q5_0 *x = (const b_q5_0 *)(const void *)(p + (size_t)b * 22); float d = cl_f16_to_f32(x->d);
|
|
101
|
+
uint32_t qh; memcpy(&qh, x->qh, 4);
|
|
102
|
+
for (int j = 0; j < 16; j++) {
|
|
103
|
+
int x0 = ((x->qs[j] & 0xF) | (((qh >> j) & 1) << 4)) - 16, x1 = ((x->qs[j] >> 4) | (((qh >> (j + 16)) & 1) << 4)) - 16;
|
|
104
|
+
dst[b * 32 + j] = x0 * d; dst[b * 32 + j + 16] = x1 * d;
|
|
105
|
+
}
|
|
106
|
+
} return;
|
|
107
|
+
case CL_T_Q8_0: for (int b = 0; b < n / 32; b++) {
|
|
108
|
+
const b_q8_0 *x = (const b_q8_0 *)(const void *)(p + (size_t)b * 34); float d = cl_f16_to_f32(x->d);
|
|
109
|
+
for (int j = 0; j < 32; j++) dst[b * 32 + j] = x->qs[j] * d;
|
|
110
|
+
} return;
|
|
111
|
+
case CL_T_Q4_K: for (int b = 0; b < n / QK_K; b++) deq_q4_K((const b_q4_K *)(const void *)(p + (size_t)b * 144), dst + b * QK_K); return;
|
|
112
|
+
case CL_T_Q5_K: for (int b = 0; b < n / QK_K; b++) deq_q5_K((const b_q5_K *)(const void *)(p + (size_t)b * 176), dst + b * QK_K); return;
|
|
113
|
+
case CL_T_Q6_K: for (int b = 0; b < n / QK_K; b++) deq_q6_K((const b_q6_K *)(const void *)(p + (size_t)b * 210), dst + b * QK_K); return;
|
|
114
|
+
default: memset(dst, 0, (size_t)n * 4);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* ---- integer dot products: K-quant weights against 8-bit activations (the maths of ggml's vec_dot for these types) ---- */
|
|
119
|
+
#if !defined(CL_FORCE_SCALAR) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
|
|
120
|
+
#include <arm_neon.h>
|
|
121
|
+
#define CL_Q_NEON 1
|
|
122
|
+
#elif !defined(CL_FORCE_SCALAR) && defined(__AVX2__)
|
|
123
|
+
#include <immintrin.h>
|
|
124
|
+
#define CL_Q_AVX2 1
|
|
125
|
+
#endif
|
|
126
|
+
const char *cl_quant_path(void) {
|
|
127
|
+
#ifdef CL_Q_NEON
|
|
128
|
+
return "neon+dotprod";
|
|
129
|
+
#elif defined(CL_Q_AVX2)
|
|
130
|
+
return "avx2";
|
|
131
|
+
#else
|
|
132
|
+
return "scalar";
|
|
133
|
+
#endif
|
|
134
|
+
}
|
|
135
|
+
int cl_type_integer_dot(uint32_t t) { return t == CL_T_Q4_K || t == CL_T_Q6_K; }
|
|
136
|
+
|
|
137
|
+
int cl_xq_init(cl_xq *q, int n) { q->n = n; q->b = n % 256 == 0 ? malloc((size_t)(n / 256) * sizeof(cl_q8k)) : NULL; return q->b != NULL; }
|
|
138
|
+
void cl_xq_free(cl_xq *q) { free(q->b); q->b = NULL; q->n = 0; }
|
|
139
|
+
void cl_xq_quantize(cl_xq *q, const float *x) {
|
|
140
|
+
for (int i = 0; i < q->n / 256; i++) {
|
|
141
|
+
const float *xs = x + i * 256;
|
|
142
|
+
float amax = 0;
|
|
143
|
+
for (int j = 0; j < 256; j++) { float a = fabsf(xs[j]); if (a > amax) amax = a; }
|
|
144
|
+
cl_q8k *b = &q->b[i];
|
|
145
|
+
float d = amax / 127.0f, id = d != 0.0f ? 1.0f / d : 0.0f;
|
|
146
|
+
b->d = d;
|
|
147
|
+
for (int j = 0; j < 256; j++) b->qs[j] = (int8_t)roundf(xs[j] * id);
|
|
148
|
+
for (int g = 0; g < 16; g++) { int s = 0; for (int j = 0; j < 16; j++) s += b->qs[g * 16 + j]; b->bsums[g] = (int16_t)s; }
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#ifdef CL_Q_NEON
|
|
153
|
+
static inline int32_t dot16(int8x16_t a, int8x16_t b) { return vaddvq_s32(vdotq_s32(vdupq_n_s32(0), a, b)); }
|
|
154
|
+
#elif !defined(CL_Q_AVX2)
|
|
155
|
+
static inline int32_t dot16_s(const int8_t *a, const int8_t *b) { int32_t s = 0; for (int i = 0; i < 16; i++) s += a[i] * b[i]; return s; }
|
|
156
|
+
#endif
|
|
157
|
+
|
|
158
|
+
static float dot_q4_K(int nb, const b_q4_K *x, const cl_q8k *y) {
|
|
159
|
+
float sum = 0;
|
|
160
|
+
for (int i = 0; i < nb; i++) {
|
|
161
|
+
uint8_t sc[8], mn[8];
|
|
162
|
+
for (int j = 0; j < 8; j++) scale_min_k4(j, x[i].scales, &sc[j], &mn[j]);
|
|
163
|
+
const uint8_t *q4 = x[i].qs;
|
|
164
|
+
const int8_t *q8 = y[i].qs;
|
|
165
|
+
int32_t sumi = 0;
|
|
166
|
+
#ifdef CL_Q_NEON
|
|
167
|
+
const uint8x16_t m4 = vdupq_n_u8(0x0f);
|
|
168
|
+
for (int g = 0; g < 4; g++) {
|
|
169
|
+
uint8x16_t v0 = vld1q_u8(q4 + 32 * g), v1 = vld1q_u8(q4 + 32 * g + 16);
|
|
170
|
+
int8x16_t lo0 = vreinterpretq_s8_u8(vandq_u8(v0, m4)), lo1 = vreinterpretq_s8_u8(vandq_u8(v1, m4));
|
|
171
|
+
int8x16_t hi0 = vreinterpretq_s8_u8(vshrq_n_u8(v0, 4)), hi1 = vreinterpretq_s8_u8(vshrq_n_u8(v1, 4));
|
|
172
|
+
int32x4_t a = vdotq_s32(vdotq_s32(vdupq_n_s32(0), lo0, vld1q_s8(q8 + 64 * g)), lo1, vld1q_s8(q8 + 64 * g + 16));
|
|
173
|
+
int32x4_t b = vdotq_s32(vdotq_s32(vdupq_n_s32(0), hi0, vld1q_s8(q8 + 64 * g + 32)), hi1, vld1q_s8(q8 + 64 * g + 48));
|
|
174
|
+
sumi += sc[2 * g] * vaddvq_s32(a) + sc[2 * g + 1] * vaddvq_s32(b);
|
|
175
|
+
}
|
|
176
|
+
#elif defined(CL_Q_AVX2)
|
|
177
|
+
{ /* nibbles are unsigned 0..15 and the activations signed, which is exactly what maddubs wants; each pair-sum fits int16 (at most 15*127*2) */
|
|
178
|
+
const __m256i m4 = _mm256_set1_epi8(0x0f);
|
|
179
|
+
__m256i acc = _mm256_setzero_si256();
|
|
180
|
+
for (int g = 0; g < 4; g++) {
|
|
181
|
+
__m256i v = _mm256_loadu_si256((const __m256i *)(const void *)(q4 + 32 * g));
|
|
182
|
+
__m256i lo = _mm256_and_si256(v, m4), hi = _mm256_and_si256(_mm256_srli_epi16(v, 4), m4);
|
|
183
|
+
__m256i a0 = _mm256_loadu_si256((const __m256i *)(const void *)(q8 + 64 * g)), a1 = _mm256_loadu_si256((const __m256i *)(const void *)(q8 + 64 * g + 32));
|
|
184
|
+
acc = _mm256_add_epi32(acc, _mm256_madd_epi16(_mm256_maddubs_epi16(lo, a0), _mm256_set1_epi16((short)sc[2 * g])));
|
|
185
|
+
acc = _mm256_add_epi32(acc, _mm256_madd_epi16(_mm256_maddubs_epi16(hi, a1), _mm256_set1_epi16((short)sc[2 * g + 1])));
|
|
186
|
+
}
|
|
187
|
+
__m128i t = _mm_add_epi32(_mm256_castsi256_si128(acc), _mm256_extracti128_si256(acc, 1));
|
|
188
|
+
t = _mm_add_epi32(t, _mm_shuffle_epi32(t, _MM_SHUFFLE(1, 0, 3, 2)));
|
|
189
|
+
t = _mm_add_epi32(t, _mm_shuffle_epi32(t, _MM_SHUFFLE(2, 3, 0, 1)));
|
|
190
|
+
sumi = _mm_cvtsi128_si32(t);
|
|
191
|
+
}
|
|
192
|
+
#else
|
|
193
|
+
for (int g = 0; g < 4; g++) {
|
|
194
|
+
int32_t a = 0, b = 0;
|
|
195
|
+
for (int l = 0; l < 32; l++) { a += (q4[32 * g + l] & 0xF) * q8[64 * g + l]; b += (q4[32 * g + l] >> 4) * q8[64 * g + 32 + l]; }
|
|
196
|
+
sumi += sc[2 * g] * a + sc[2 * g + 1] * b;
|
|
197
|
+
}
|
|
198
|
+
#endif
|
|
199
|
+
int32_t summs = 0;
|
|
200
|
+
for (int j = 0; j < 8; j++) summs += mn[j] * (y[i].bsums[2 * j] + y[i].bsums[2 * j + 1]);
|
|
201
|
+
sum += y[i].d * (cl_f16_to_f32(x[i].d) * (float)sumi - cl_f16_to_f32(x[i].dmin) * (float)summs);
|
|
202
|
+
}
|
|
203
|
+
return sum;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static float dot_q6_K(int nb, const b_q6_K *x, const cl_q8k *y) {
|
|
207
|
+
float sum = 0;
|
|
208
|
+
for (int i = 0; i < nb; i++) {
|
|
209
|
+
int8_t q[256];
|
|
210
|
+
const uint8_t *ql = x[i].ql, *qh = x[i].qh;
|
|
211
|
+
for (int n = 0; n < 256; n += 128) {
|
|
212
|
+
for (int l = 0; l < 32; l++) {
|
|
213
|
+
q[n + l] = (int8_t)(((ql[l] & 0xF) | (((qh[l] >> 0) & 3) << 4)) - 32);
|
|
214
|
+
q[n + l + 32] = (int8_t)(((ql[l + 32] & 0xF) | (((qh[l] >> 2) & 3) << 4)) - 32);
|
|
215
|
+
q[n + l + 64] = (int8_t)(((ql[l] >> 4) | (((qh[l] >> 4) & 3) << 4)) - 32);
|
|
216
|
+
q[n + l + 96] = (int8_t)(((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4)) - 32);
|
|
217
|
+
}
|
|
218
|
+
ql += 64; qh += 32;
|
|
219
|
+
}
|
|
220
|
+
int32_t sumi = 0;
|
|
221
|
+
#if defined(CL_Q_AVX2)
|
|
222
|
+
{ /* |q| <= 32 and |a| <= 127, so abs(q) x sign-moved a pair-sums to at most 8128 and fits int16 */
|
|
223
|
+
__m256i acc = _mm256_setzero_si256();
|
|
224
|
+
for (int j = 0; j < 16; j += 2) {
|
|
225
|
+
__m256i qv = _mm256_loadu_si256((const __m256i *)(const void *)(q + 16 * j)), av = _mm256_loadu_si256((const __m256i *)(const void *)(y[i].qs + 16 * j));
|
|
226
|
+
__m256i p16 = _mm256_maddubs_epi16(_mm256_sign_epi8(qv, qv), _mm256_sign_epi8(av, qv));
|
|
227
|
+
__m256i scv = _mm256_setr_epi16((short)x[i].scales[j], (short)x[i].scales[j], (short)x[i].scales[j], (short)x[i].scales[j], (short)x[i].scales[j], (short)x[i].scales[j], (short)x[i].scales[j], (short)x[i].scales[j],
|
|
228
|
+
(short)x[i].scales[j + 1], (short)x[i].scales[j + 1], (short)x[i].scales[j + 1], (short)x[i].scales[j + 1], (short)x[i].scales[j + 1], (short)x[i].scales[j + 1], (short)x[i].scales[j + 1], (short)x[i].scales[j + 1]);
|
|
229
|
+
acc = _mm256_add_epi32(acc, _mm256_madd_epi16(p16, scv));
|
|
230
|
+
}
|
|
231
|
+
__m128i t = _mm_add_epi32(_mm256_castsi256_si128(acc), _mm256_extracti128_si256(acc, 1));
|
|
232
|
+
t = _mm_add_epi32(t, _mm_shuffle_epi32(t, _MM_SHUFFLE(1, 0, 3, 2)));
|
|
233
|
+
t = _mm_add_epi32(t, _mm_shuffle_epi32(t, _MM_SHUFFLE(2, 3, 0, 1)));
|
|
234
|
+
sumi = _mm_cvtsi128_si32(t);
|
|
235
|
+
}
|
|
236
|
+
#else
|
|
237
|
+
for (int j = 0; j < 16; j++) {
|
|
238
|
+
#ifdef CL_Q_NEON
|
|
239
|
+
sumi += x[i].scales[j] * dot16(vld1q_s8(q + 16 * j), vld1q_s8(y[i].qs + 16 * j));
|
|
240
|
+
#else
|
|
241
|
+
sumi += x[i].scales[j] * dot16_s(q + 16 * j, y[i].qs + 16 * j);
|
|
242
|
+
#endif
|
|
243
|
+
}
|
|
244
|
+
#endif
|
|
245
|
+
sum += cl_f16_to_f32(x[i].d) * y[i].d * (float)sumi;
|
|
246
|
+
}
|
|
247
|
+
return sum;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* ---- thread pool: persistent workers claim chunks of a batch; the caller works too ---- */
|
|
251
|
+
#include <stdatomic.h>
|
|
252
|
+
#define CHUNK 16
|
|
253
|
+
static int g_threads = 0;
|
|
254
|
+
void cl_set_threads(int n) { g_threads = n < 1 ? 1 : n > 16 ? 16 : n; }
|
|
255
|
+
int cl_get_threads(void) {
|
|
256
|
+
if (!g_threads) { long c = sysconf(_SC_NPROCESSORS_ONLN); cl_set_threads(c > 0 ? (int)c : 1); }
|
|
257
|
+
return g_threads;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
typedef struct { const cl_mv *jobs; int n; int *first; int total; } batch_t; /* first[j] = index of job j's first chunk */
|
|
261
|
+
static pthread_mutex_t pool_mu = PTHREAD_MUTEX_INITIALIZER;
|
|
262
|
+
static pthread_cond_t pool_cv = PTHREAD_COND_INITIALIZER, pool_done = PTHREAD_COND_INITIALIZER;
|
|
263
|
+
static _Atomic(const batch_t *) cur_batch;
|
|
264
|
+
static atomic_int next_chunk, pending_workers, generation;
|
|
265
|
+
static int pool_started, pool_size;
|
|
266
|
+
#define SPIN 20000 /* a worker polls this many times for the next batch before it sleeps; a batch arrives within microseconds of the last */
|
|
267
|
+
|
|
268
|
+
/* CLUTOR_FLOAT_MATH=1 skips the 8-bit activations and multiplies dequantized weights in float: slower, and the reference the integer kernels are measured against. */
|
|
269
|
+
static int float_math(void) {
|
|
270
|
+
static int v = -1;
|
|
271
|
+
if (v < 0) { const char *e = getenv("CLUTOR_FLOAT_MATH"); v = e && *e == '1'; }
|
|
272
|
+
return v;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static void do_rows(const cl_mv *j, int r0, int r1) {
|
|
276
|
+
uint32_t t = j->type;
|
|
277
|
+
const cl_xq *xq = float_math() ? NULL : j->xq;
|
|
278
|
+
uint64_t rb = cl_row_bytes(t, (uint64_t)j->cols);
|
|
279
|
+
const uint8_t *w = j->w;
|
|
280
|
+
if (t == CL_T_Q4_K && xq) for (int r = r0; r < r1; r++) j->out[r] = dot_q4_K(j->cols / 256, (const b_q4_K *)(const void *)(w + (size_t)r * rb), xq->b);
|
|
281
|
+
else if (t == CL_T_Q6_K && xq) for (int r = r0; r < r1; r++) j->out[r] = dot_q6_K(j->cols / 256, (const b_q6_K *)(const void *)(w + (size_t)r * rb), xq->b);
|
|
282
|
+
else { /* any other type: dequantize a block at a time and use the float vector */
|
|
283
|
+
uint32_t bl = cl_type_block(t), bb = cl_type_bytes(t);
|
|
284
|
+
float buf[256];
|
|
285
|
+
for (int r = r0; r < r1; r++) {
|
|
286
|
+
const uint8_t *row = w + (size_t)r * rb;
|
|
287
|
+
float acc = 0;
|
|
288
|
+
if (t == CL_T_F32) { const float *wf = (const float *)(const void *)row; for (int i = 0; i < j->cols; i++) acc += wf[i] * j->xf[i]; }
|
|
289
|
+
else for (int c = 0; c < j->cols; c += (int)bl) { cl_dequant_row(t, row + (size_t)(c / (int)bl) * bb, buf, (int)bl); for (uint32_t i = 0; i < bl; i++) acc += buf[i] * j->xf[c + (int)i]; }
|
|
290
|
+
j->out[r] = acc;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
static void run_chunks(const batch_t *b) {
|
|
296
|
+
for (;;) {
|
|
297
|
+
int c = atomic_fetch_add(&next_chunk, 1);
|
|
298
|
+
if (c >= b->total) return;
|
|
299
|
+
int j = 0;
|
|
300
|
+
while (j + 1 < b->n && b->first[j + 1] <= c) j++;
|
|
301
|
+
int r0 = (c - b->first[j]) * CHUNK, r1 = r0 + CHUNK;
|
|
302
|
+
if (r1 > b->jobs[j].rows) r1 = b->jobs[j].rows;
|
|
303
|
+
do_rows(&b->jobs[j], r0, r1);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static void *pool_worker(void *arg) {
|
|
308
|
+
(void)arg;
|
|
309
|
+
int seen = 0; /* the pool is started before the first batch, so every worker begins at generation 0 */
|
|
310
|
+
for (;;) {
|
|
311
|
+
int g = atomic_load(&generation);
|
|
312
|
+
for (int i = 0; i < SPIN && g == seen; i++) g = atomic_load(&generation);
|
|
313
|
+
if (g == seen) {
|
|
314
|
+
pthread_mutex_lock(&pool_mu);
|
|
315
|
+
while ((g = atomic_load(&generation)) == seen) pthread_cond_wait(&pool_cv, &pool_mu);
|
|
316
|
+
pthread_mutex_unlock(&pool_mu);
|
|
317
|
+
}
|
|
318
|
+
seen = g;
|
|
319
|
+
run_chunks(atomic_load(&cur_batch));
|
|
320
|
+
if (atomic_fetch_sub(&pending_workers, 1) == 1) { pthread_mutex_lock(&pool_mu); pthread_cond_signal(&pool_done); pthread_mutex_unlock(&pool_mu); }
|
|
321
|
+
}
|
|
322
|
+
return NULL;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
void cl_matvec_batch(const cl_mv *jobs, int n) {
|
|
326
|
+
int first[64], total = 0;
|
|
327
|
+
if (n > 64) n = 64;
|
|
328
|
+
for (int j = 0; j < n; j++) { first[j] = total; total += (jobs[j].rows + CHUNK - 1) / CHUNK; }
|
|
329
|
+
batch_t b = {jobs, n, first, total};
|
|
330
|
+
int nt = cl_get_threads();
|
|
331
|
+
if (nt == 1 || total < 4) { atomic_store(&next_chunk, 0); run_chunks(&b); return; }
|
|
332
|
+
if (!pool_started) {
|
|
333
|
+
pool_started = 1; pool_size = nt - 1;
|
|
334
|
+
for (int i = 0; i < pool_size; i++) { pthread_t t; if (pthread_create(&t, NULL, pool_worker, NULL) == 0) pthread_detach(t); else { pool_size = i; break; } }
|
|
335
|
+
}
|
|
336
|
+
atomic_store(&next_chunk, 0);
|
|
337
|
+
atomic_store(&cur_batch, &b);
|
|
338
|
+
atomic_store(&pending_workers, pool_size);
|
|
339
|
+
pthread_mutex_lock(&pool_mu);
|
|
340
|
+
atomic_fetch_add(&generation, 1);
|
|
341
|
+
pthread_cond_broadcast(&pool_cv);
|
|
342
|
+
pthread_mutex_unlock(&pool_mu);
|
|
343
|
+
run_chunks(&b);
|
|
344
|
+
for (int i = 0; i < SPIN && atomic_load(&pending_workers) > 0; i++) { }
|
|
345
|
+
if (atomic_load(&pending_workers) > 0) {
|
|
346
|
+
pthread_mutex_lock(&pool_mu);
|
|
347
|
+
while (atomic_load(&pending_workers) > 0) pthread_cond_wait(&pool_done, &pool_mu);
|
|
348
|
+
pthread_mutex_unlock(&pool_mu);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
void cl_matvec(uint32_t type, const void *w, int rows, int cols, const float *x, float *out) {
|
|
353
|
+
cl_xq q = {0};
|
|
354
|
+
int use_q = cl_type_integer_dot(type) && cl_xq_init(&q, cols);
|
|
355
|
+
if (use_q) cl_xq_quantize(&q, x);
|
|
356
|
+
cl_mv j = {type, w, rows, cols, x, use_q ? &q : NULL, out};
|
|
357
|
+
if (cl_type_integer_dot(type) && !use_q) { /* odd width: cannot happen for real K-quant tensors */ return; }
|
|
358
|
+
cl_matvec_batch(&j, 1);
|
|
359
|
+
cl_xq_free(&q);
|
|
360
|
+
}
|
package/c/src/quant.h
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#ifndef CL_QUANT_H
|
|
2
|
+
#define CL_QUANT_H
|
|
3
|
+
#include <stddef.h>
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
|
|
6
|
+
/* ggml tensor type ids this engine can compute with (see gguf.c for the full table). */
|
|
7
|
+
enum { CL_T_F32 = 0, CL_T_F16 = 1, CL_T_Q4_0 = 2, CL_T_Q5_0 = 6, CL_T_Q8_0 = 8, CL_T_Q4_K = 12, CL_T_Q5_K = 13, CL_T_Q6_K = 14, CL_T_BF16 = 30 };
|
|
8
|
+
|
|
9
|
+
/* 1 when cl_dequant_row / cl_matvec understand this type. */
|
|
10
|
+
int cl_type_supported(uint32_t type);
|
|
11
|
+
/* Elements per block and bytes per block (F32: 1 and 4). 0 for unsupported types. */
|
|
12
|
+
uint32_t cl_type_block(uint32_t type);
|
|
13
|
+
uint32_t cl_type_bytes(uint32_t type);
|
|
14
|
+
/* Bytes of one row of n elements (n must be a multiple of the block). */
|
|
15
|
+
uint64_t cl_row_bytes(uint32_t type, uint64_t n);
|
|
16
|
+
|
|
17
|
+
/* Dequantizes n elements (a multiple of the block) from one row into floats. */
|
|
18
|
+
void cl_dequant_row(uint32_t type, const void *src, float *dst, int n);
|
|
19
|
+
|
|
20
|
+
/* An activation vector quantized to 8-bit blocks of 256 (ggml's Q8_K layout: a scale, 256 int8, and the sum of each 16), the partner of
|
|
21
|
+
* the K-quant weight types in the integer dot products. n must be a multiple of 256. */
|
|
22
|
+
typedef struct { float d; int8_t qs[256]; int16_t bsums[16]; } cl_q8k;
|
|
23
|
+
typedef struct { cl_q8k *b; int n; } cl_xq;
|
|
24
|
+
int cl_xq_init(cl_xq *q, int n);
|
|
25
|
+
void cl_xq_free(cl_xq *q);
|
|
26
|
+
void cl_xq_quantize(cl_xq *q, const float *x);
|
|
27
|
+
|
|
28
|
+
/* One matrix-vector product: out[r] = dot(row r of W, x). K-quant weights use `xq` and integer arithmetic; other types use the float `xf`. */
|
|
29
|
+
typedef struct { uint32_t type; const void *w; int rows, cols; const float *xf; const cl_xq *xq; float *out; } cl_mv;
|
|
30
|
+
/* Runs several products at once, their rows split into chunks over the thread pool. */
|
|
31
|
+
void cl_matvec_batch(const cl_mv *jobs, int n);
|
|
32
|
+
/* 1 when the weights of this type are multiplied with integer arithmetic (and so need `xq`). */
|
|
33
|
+
int cl_type_integer_dot(uint32_t type);
|
|
34
|
+
/* Name of the integer dot path compiled in: "neon+dotprod" or "scalar". */
|
|
35
|
+
const char *cl_quant_path(void);
|
|
36
|
+
|
|
37
|
+
/* out[r] = dot(row r of W, x) for rows x cols weights of `type`, using a pool of threads. Dequantizes each block to float on the fly. */
|
|
38
|
+
void cl_matvec(uint32_t type, const void *w, int rows, int cols, const float *x, float *out);
|
|
39
|
+
|
|
40
|
+
/* Threads used by cl_matvec (default: the number of CPUs, at most 16). */
|
|
41
|
+
void cl_set_threads(int n);
|
|
42
|
+
int cl_get_threads(void);
|
|
43
|
+
#endif
|
package/c/src/stream.c
CHANGED
|
@@ -299,7 +299,8 @@ int cl_stream_run(const cl_model *m, const char *path, const cl_stream_opts *o,
|
|
|
299
299
|
const uint8_t *dbuf = resident ? e->dense_res[l] : e->ring[s & 1];
|
|
300
300
|
if (!resident) { uint64_t t0 = now_ns(); int waited = 0; while (e->ring_step[s & 1] != s && !e->failed) { waited = 1; pthread_cond_wait(&e->cv_done, &e->mu); } if (waited) { lw += now_ns() - t0; } }
|
|
301
301
|
if (ly->n_exp) {
|
|
302
|
-
|
|
302
|
+
if (o->trace) memcpy(logits, o->trace + ((size_t)(tok % o->trace_tokens) * L + (size_t)l) * E, (size_t)E * sizeof(float));
|
|
303
|
+
else router_logits(&rt, tok, l, 0, rmode, logits);
|
|
303
304
|
resctx rc = {e, l, E};
|
|
304
305
|
cl_route_result rr;
|
|
305
306
|
cl_route(logits, E, K, &o->smart, resident_cb, &rc, &rr);
|
|
@@ -323,8 +324,10 @@ int cl_stream_run(const cl_model *m, const char *path, const cl_stream_opts *o,
|
|
|
323
324
|
e->t.dropped_mass += rr.dropped_mass;
|
|
324
325
|
if (rr.max_delta > e->t.max_delta) e->t.max_delta = rr.max_delta;
|
|
325
326
|
/* while this layer's bytes arrive and are used, start reading the next layer's predicted experts */
|
|
326
|
-
|
|
327
|
-
|
|
327
|
+
/* With a recorded trace the predictor is causal: it guesses layer l+1 from what that layer chose for the PREVIOUS token (nothing is known for the first). */
|
|
328
|
+
if (o->prefetch && l + 1 < L && m->layers[l + 1].n_exp && stage_slots && (!o->trace || tok > 0)) {
|
|
329
|
+
if (o->trace) memcpy(logits, o->trace + ((size_t)((tok - 1) % o->trace_tokens) * L + (size_t)(l + 1)) * E, (size_t)E * sizeof(float));
|
|
330
|
+
else router_logits(&rt, tok, l + 1, 1, rmode, logits);
|
|
328
331
|
cl_route_result pr;
|
|
329
332
|
cl_route(logits, E, K, NULL, NULL, NULL, &pr);
|
|
330
333
|
for (int i = 0; i < K; i++) {
|
package/c/src/stream.h
CHANGED
|
@@ -25,6 +25,8 @@ typedef struct {
|
|
|
25
25
|
int compute_mode; /* CL_COMPUTE_KERNEL: a Q4_0 x Q8_0 dot-product kernel over every loaded byte; CL_COMPUTE_CHECKSUM: a cheap hash, so the run is I/O only */
|
|
26
26
|
int compute_us; /* extra per-layer busy time, added to either mode */
|
|
27
27
|
int shift_at, shift_len; /* scenario: measured tokens [shift_at, shift_at+shift_len) route uniformly at random (scan noise); every other measured token uses one fixed tight topic. shift_len 0 = off */
|
|
28
|
+
const float *trace; /* a recorded run of the REAL router (from `generate --trace`): trace_tokens x layers x experts raw scores; replayed cyclically. NULL = synthetic routing */
|
|
29
|
+
int trace_tokens;
|
|
28
30
|
int window_tokens; /* record hits and peak RSS every N measured tokens; 0 = off (10 when a scenario is set) */
|
|
29
31
|
} cl_stream_opts;
|
|
30
32
|
|