HelloSun commited on
Commit
e8b5a42
·
verified ·
1 Parent(s): 841c778

experiment E1: fp16 prefill GEMM (pp512 40.0 -> 57.0 t/s)

Browse files
Files changed (1) hide show
  1. patches/0002-nvfp4-f16-prefill.patch +168 -0
patches/0002-nvfp4-f16-prefill.patch ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- a/ggml/src/ggml-cpu/ggml-cpu.c
2
+ +++ b/ggml/src/ggml-cpu/ggml-cpu.c
3
+ @@ -77,6 +77,56 @@
4
+ #define UNUSED GGML_UNUSED
5
+ #define SWAP(x, y, T) do { T SWAP = x; (x) = y; (y) = SWAP; } while (0)
6
+
7
+ +#if GGML_USE_LLAMAFILE && defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512VL__) && defined(__AVX512VNNI__)
8
+ +#include <pthread.h>
9
+ +// Lazily dequantized fp16 shadow of the NVFP4 weights, used for prompt processing only. The dequantization is
10
+ +// exact (see ggml_nvfp4_to_fp16_row), so logits are unchanged; decode keeps using the packed 4-bit weights.
11
+ +#define GGML_NVFP4_SHADOW_MAX 512
12
+ +static pthread_mutex_t g_nvfp4_shadow_mu = PTHREAD_MUTEX_INITIALIZER;
13
+ +static struct {
14
+ + const void * src;
15
+ + void * data;
16
+ +} g_nvfp4_shadow[GGML_NVFP4_SHADOW_MAX];
17
+ +static int g_nvfp4_shadow_n = 0;
18
+ +
19
+ +static void * ggml_nvfp4_shadow_get(const void * src, int64_t K, int64_t M, size_t bx) {
20
+ + pthread_mutex_lock(&g_nvfp4_shadow_mu);
21
+ + void * data = NULL;
22
+ + for (int i = 0; i < g_nvfp4_shadow_n; ++i) {
23
+ + if (g_nvfp4_shadow[i].src == src) { data = g_nvfp4_shadow[i].data; break; }
24
+ + }
25
+ + if (data == NULL && g_nvfp4_shadow_n < GGML_NVFP4_SHADOW_MAX) {
26
+ + data = aligned_alloc(64, (size_t) K * M * sizeof(ggml_fp16_t));
27
+ + if (data != NULL) {
28
+ + for (int64_t r = 0; r < M; ++r) {
29
+ + ggml_nvfp4_to_fp16_row((const char *) src + r * bx,
30
+ + (ggml_fp16_t *) data + r * K, K);
31
+ + }
32
+ + g_nvfp4_shadow[g_nvfp4_shadow_n].src = src;
33
+ + g_nvfp4_shadow[g_nvfp4_shadow_n].data = data;
34
+ + g_nvfp4_shadow_n++;
35
+ + }
36
+ + }
37
+ + pthread_mutex_unlock(&g_nvfp4_shadow_mu);
38
+ + return data;
39
+ +}
40
+ +
41
+ +// Scratch buffer holding the fp16 activations for the fp16 GEMM path. Reused between ops; the graph executes
42
+ +// one matmul at a time, so a single buffer is enough.
43
+ +static void * g_nvfp4_act = NULL;
44
+ +static size_t g_nvfp4_act_sz = 0;
45
+ +
46
+ +static void * ggml_nvfp4_act_get(size_t need) {
47
+ + if (need > g_nvfp4_act_sz) {
48
+ + free(g_nvfp4_act);
49
+ + need = (need + 63) & ~(size_t) 63;
50
+ + g_nvfp4_act = aligned_alloc(64, need);
51
+ + g_nvfp4_act_sz = g_nvfp4_act ? need : 0;
52
+ + }
53
+ + return g_nvfp4_act;
54
+ +}
55
+ +#endif
56
+ +
57
+ // precomputed f32 table for f16 (256 KB) (simd-mappings.h)
58
+ float ggml_table_f32_f16[1 << 16];
59
+
60
+ @@ -1325,6 +1375,49 @@
61
+ UseGgmlGemm1:;
62
+ #endif
63
+
64
+ +#if GGML_USE_LLAMAFILE && defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512VL__) && defined(__AVX512VNNI__)
65
+ + // Prompt processing of the NVFP4 projections through an exact fp16 shadow + fp16 GEMM. This replaces the
66
+ + // per-column 4-bit dot products, which re-read and re-decode the weights for every pair of tokens.
67
+ + if (!params->use_ref && src0->type == GGML_TYPE_NVFP4 && src1->type == GGML_TYPE_F32 &&
68
+ + ggml_is_contiguous(src1) &&
69
+ + ne02 == 1 && ne03 == 1 && ne12 == 1 && ne13 == 1 &&
70
+ + ne11 >= 8 && ne00 % QK_NVFP4 == 0 &&
71
+ + dst->type == GGML_TYPE_F32 && nb0 == (int64_t) sizeof(float) &&
72
+ + nb1 == (int64_t) ne0 * (int64_t) sizeof(float) &&
73
+ + nb11 == (int64_t) ne00 * (int64_t) sizeof(float) &&
74
+ + (params->nth == 1 || params->threadpool != NULL)) {
75
+ + void * A = NULL;
76
+ + void * act16 = NULL;
77
+ + if (ith == 0) {
78
+ + A = ggml_nvfp4_shadow_get(src0->data, ne00, ne01, nb01);
79
+ + act16 = ggml_nvfp4_act_get((size_t) ne00 * ne11 * sizeof(ggml_fp16_t));
80
+ + }
81
+ + if (params->nth > 1) {
82
+ + ggml_barrier(params->threadpool);
83
+ + }
84
+ + if (ith != 0) {
85
+ + A = ggml_nvfp4_shadow_get(src0->data, ne00, ne01, nb01);
86
+ + act16 = g_nvfp4_act;
87
+ + }
88
+ + if (A != NULL && act16 != NULL) {
89
+ + const int64_t c0 = (ne11 * ith) / nth;
90
+ + const int64_t c1 = (ne11 * (ith + 1)) / nth;
91
+ + for (int64_t c = c0; c < c1; ++c) {
92
+ + ggml_fp32_to_fp16_row((const float *) ((const char *) src1->data + c * nb11),
93
+ + (ggml_fp16_t *) act16 + c * ne00, ne00);
94
+ + }
95
+ + if (params->nth > 1) {
96
+ + ggml_barrier(params->threadpool);
97
+ + }
98
+ + if (llamafile_sgemm(params, ne01, ne11, ne00, A, ne00,
99
+ + act16, ne00, dst->data, ne0,
100
+ + GGML_TYPE_F16, GGML_TYPE_F16, GGML_TYPE_F32)) {
101
+ + return;
102
+ + }
103
+ + }
104
+ + }
105
+ +#endif
106
+ +
107
+ if (src1->type != vec_dot_type) {
108
+ char * wdata = params->wdata;
109
+
110
+ --- a/ggml/src/ggml-cpu/arch/x86/quants.c
111
+ +++ b/ggml/src/ggml-cpu/arch/x86/quants.c
112
+ @@ -1142,6 +1142,46 @@
113
+ }
114
+ #endif
115
+
116
+ +// Dequantize one NVFP4 row into fp16. The result is exact: E2M1 magnitudes times the UE4M3 sub-block
117
+ +// scales fit in fp16, so prefill can run an ordinary fp16 GEMM instead of the 4-bit dot product.
118
+ +void ggml_nvfp4_to_fp16_row(const void * GGML_RESTRICT vx, ggml_fp16_t * GGML_RESTRICT y, int64_t k) {
119
+ + const block_nvfp4 * GGML_RESTRICT x = (const block_nvfp4 *) vx;
120
+ + const int nb = (int) (k / QK_NVFP4);
121
+ +#if defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512VL__)
122
+ + const __m512 tbl = _mm512_setr_ps(
123
+ + (float) kvalues_fp4[ 0], (float) kvalues_fp4[ 1], (float) kvalues_fp4[ 2], (float) kvalues_fp4[ 3],
124
+ + (float) kvalues_fp4[ 4], (float) kvalues_fp4[ 5], (float) kvalues_fp4[ 6], (float) kvalues_fp4[ 7],
125
+ + (float) kvalues_fp4[ 8], (float) kvalues_fp4[ 9], (float) kvalues_fp4[10], (float) kvalues_fp4[11],
126
+ + (float) kvalues_fp4[12], (float) kvalues_fp4[13], (float) kvalues_fp4[14], (float) kvalues_fp4[15]);
127
+ +
128
+ + for (int ib = 0; ib < nb; ++ib) {
129
+ + const __m512i nib = nvfp4_nibbles_512(&x[ib]);
130
+ + const __m128 sc4 = nvfp4_scale4_128(&x[ib]);
131
+ + for (int s = 0; s < 4; ++s) {
132
+ + const __m128i bytes = _mm512_extracti32x4_epi32(nib, s);
133
+ + const __m512i idx = _mm512_cvtepu8_epi32(bytes);
134
+ + __m512 v = _mm512_permutexvar_ps(idx, tbl);
135
+ + const float sc = _mm_cvtss_f32(_mm_shuffle_ps(sc4, sc4, _MM_SHUFFLE(s, s, s, s)));
136
+ + v = _mm512_mul_ps(v, _mm512_set1_ps(sc));
137
+ + const __m256i h = _mm512_cvtps_ph(v, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC);
138
+ + _mm256_storeu_si256((__m256i *) (y + (int64_t) ib*QK_NVFP4 + s*16), h);
139
+ + }
140
+ + }
141
+ +#else
142
+ + for (int ib = 0; ib < nb; ++ib) {
143
+ + for (int s = 0; s < 4; ++s) {
144
+ + const float d = ggml_ue4m3_to_fp32(x[ib].d[s]);
145
+ + for (int j = 0; j < 8; ++j) {
146
+ + const uint8_t q = x[ib].qs[s*8 + j];
147
+ + const int64_t base = (int64_t) ib*QK_NVFP4 + s*16;
148
+ + y[base + j] = ggml_fp32_to_fp16((float) kvalues_fp4[q & 0x0F] * d);
149
+ + y[base + j + 8] = ggml_fp32_to_fp16((float) kvalues_fp4[q >> 4] * d);
150
+ + }
151
+ + }
152
+ + }
153
+ +#endif
154
+ +}
155
+ +
156
+ void ggml_vec_dot_nvfp4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc) {
157
+ assert(nrc == 1 || nrc == 2);
158
+ assert(n % QK_NVFP4 == 0);
159
+ --- a/ggml/src/ggml-cpu/quants.h
160
+ +++ b/ggml/src/ggml-cpu/quants.h
161
+ @@ -48,6 +48,7 @@
162
+
163
+ void ggml_vec_dot_mxfp4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc);
164
+ void ggml_vec_dot_nvfp4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc);
165
+ +void ggml_nvfp4_to_fp16_row(const void * GGML_RESTRICT vx, ggml_fp16_t * GGML_RESTRICT y, int64_t k);
166
+
167
+ void ggml_vec_dot_q2_K_q8_K(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc);
168
+ void ggml_vec_dot_q3_K_q8_K(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc);