Skip to content

Commit 4e6455e

Browse files
authored
Merge pull request antirez#14 from jart/update
Add BF16 support and fix warnings
2 parents 3e5c0a4 + ede59bb commit 4e6455e

File tree

6 files changed

+201
-27
lines changed

6 files changed

+201
-27
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
gguf-tools

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
all: gguf-tools
22

3-
gguf-tools: gguf-tools.c gguflib.c gguflib.h sds.c sds.h sdsalloc.h fp16.h
3+
gguf-tools: gguf-tools.c gguflib.c gguflib.h sds.c sds.h sdsalloc.h fp16.h bf16.h
44
$(CC) gguf-tools.c gguflib.c sds.c fp16.c \
5-
-march=native -flto -ffast-math \
5+
-march=native -ffast-math \
66
-g -ggdb -Wall -W -pedantic -O3 -o gguf-tools
77

88
clean:

bf16.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#ifndef BF16_h
2+
#define BF16_h
3+
#include <stdint.h>
4+
5+
/**
6+
* Converts brain16 to float32.
7+
*
8+
* The bfloat16 floating point format has the following structure:
9+
*
10+
* ┌sign
11+
* │
12+
* │ ┌exponent
13+
* │ │
14+
* │ │ ┌mantissa
15+
* │ │ │
16+
* │┌──┴───┐┌─┴───┐
17+
* 0b0000000000000000 brain16
18+
*
19+
* Since bf16 has the same number of exponent bits as a 32bit float,
20+
* encoding and decoding numbers becomes relatively straightforward.
21+
*
22+
* ┌sign
23+
* │
24+
* │ ┌exponent
25+
* │ │
26+
* │ │ ┌mantissa
27+
* │ │ │
28+
* │┌──┴───┐┌─┴───────────────────┐
29+
* 0b00000000000000000000000000000000 IEEE binary32
30+
*
31+
* For comparison, the standard fp16 format has fewer exponent bits.
32+
*
33+
* ┌sign
34+
* │
35+
* │ ┌exponent
36+
* │ │
37+
* │ │ ┌mantissa
38+
* │ │ │
39+
* │┌─┴─┐┌─┴──────┐
40+
* 0b0000000000000000 IEEE binary16
41+
*
42+
* @see IEEE 754-2008
43+
*/
44+
static inline float from_brain(uint16_t h) {
45+
union {
46+
float f;
47+
uint32_t i;
48+
} u;
49+
u.i = (uint32_t)h << 16;
50+
return u.f;
51+
}
52+
53+
/**
54+
* Converts float32 to brain16.
55+
*
56+
* This function is binary identical to AMD Zen4 VCVTNEPS2BF16.
57+
* Subnormals shall be flushed to zero, and NANs will be quiet.
58+
* This code should vectorize nicely if using modern compilers.
59+
*/
60+
static inline uint16_t to_brain(float s) {
61+
uint16_t h;
62+
union {
63+
float f;
64+
uint32_t i;
65+
} u;
66+
u.f = s;
67+
if ((u.i & 0x7fffffff) > 0x7f800000) { /* nan */
68+
h = (u.i >> 16) | 64; /* force to quiet */
69+
return h;
70+
}
71+
if (!(u.i & 0x7f800000)) { /* subnormal */
72+
h = (u.i & 0x80000000) >> 16; /* flush to zero */
73+
return h;
74+
}
75+
return (u.i + (0x7fff + ((u.i >> 16) & 1))) >> 16;
76+
}
77+
78+
#endif

gguf-tools.c

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <assert.h>
66
#include <errno.h>
77
#include <math.h>
8+
#include <inttypes.h>
89

910
#include "gguflib.h"
1011
#include "sds.h"
@@ -143,7 +144,7 @@ int strmatch(const char *pattern, int patternLen,
143144
void gguf_tools_show(const char *filename) {
144145
gguf_ctx *ctx = gguf_open(filename);
145146
if (ctx == NULL) {
146-
perror("Opening GGUF file");
147+
perror(filename);
147148
exit(1);
148149
}
149150

@@ -166,16 +167,16 @@ void gguf_tools_show(const char *filename) {
166167
gguf_tensor tensor;
167168
uint64_t params = 0;
168169
while (gguf_get_tensor(ctx,&tensor)) {
169-
printf("%s tensor %.*s @%llu, %llu weights, dims ",
170+
printf("%s tensor %.*s @%" PRIu64 ", %" PRIu64 " weights, dims ",
170171
gguf_get_tensor_type_name(tensor.type),
171172
(int)tensor.namelen,
172173
tensor.name,
173174
tensor.offset,
174175
tensor.num_weights);
175176
for (uint32_t j = 0; j < tensor.ndim; j++) {
176-
printf("%s%llu",(j == 0) ? "[" : ",", tensor.dim[j]);
177+
printf("%s%" PRIu64 "",(j == 0) ? "[" : ",", tensor.dim[j]);
177178
}
178-
printf("], %llu bytes\n", tensor.bsize);
179+
printf("], %" PRIu64 " bytes\n", tensor.bsize);
179180

180181
params += tensor.num_weights;
181182
}
@@ -192,13 +193,13 @@ void gguf_tools_show(const char *filename) {
192193
void gguf_tools_split_mixtral(int *experts_id, const char *mixtral_filename, const char *output_filename) {
193194
gguf_ctx *mixtral = gguf_open(mixtral_filename);
194195
if (mixtral == NULL) {
195-
perror("Opening Mixtral file");
196+
perror(mixtral_filename);
196197
exit(1);
197198
}
198199

199200
gguf_ctx *output = gguf_create(output_filename, GGUF_NONE);
200201
if (output == NULL) {
201-
perror("Opening the output file");
202+
perror(output_filename);
202203
exit(1);
203204
}
204205

@@ -312,7 +313,7 @@ void gguf_tools_split_mixtral(int *experts_id, const char *mixtral_filename, con
312313
}
313314
tensor_off += tensors[j].orig_info.bsize;
314315
}
315-
printf("Output file: after writing tensors info, file size is: %llu\n", output->size);
316+
printf("Output file: after writing tensors info, file size is: %" PRIu64 "\n", output->size);
316317

317318
/* Finally, append the tensors weights. */
318319
for (uint32_t j = 0; j < num_tensors; j++) {
@@ -333,7 +334,7 @@ void gguf_tools_split_mixtral(int *experts_id, const char *mixtral_filename, con
333334
void gguf_tools_inspect_weights(const char *filename, const char *tname, uint64_t count) {
334335
gguf_ctx *ctx = gguf_open(filename);
335336
if (ctx == NULL) {
336-
perror("Opening GGUF file");
337+
perror(filename);
337338
exit(1);
338339
}
339340

@@ -424,8 +425,8 @@ int tensors_avg_diff(gguf_tensor *t1, gguf_tensor *t2, double *diff) {
424425
float *weights1 = gguf_tensor_to_float(t1);
425426
float *weights2 = gguf_tensor_to_float(t2);
426427
if (weights1 == NULL || weights2 == NULL) {
427-
if (weights1) free(weights1);
428-
if (weights2) free(weights2);
428+
free(weights1);
429+
free(weights2);
429430
return 0;
430431
}
431432

@@ -444,7 +445,7 @@ int tensors_avg_diff(gguf_tensor *t1, gguf_tensor *t2, double *diff) {
444445
double avg_diff = tot_diff / t1->num_weights;
445446

446447
/* Multiply by 75 to normalize the difference of a
447-
* random varialbe between -N and +N to 0 - 100% */
448+
* random variable between -N and +N to 0 - 100% */
448449
*diff = avg_diff / avg_mag * 75;
449450

450451
free(weights1);
@@ -454,9 +455,14 @@ int tensors_avg_diff(gguf_tensor *t1, gguf_tensor *t2, double *diff) {
454455

455456
void gguf_tools_compare(const char *file1, const char *file2) {
456457
gguf_ctx *ctx1 = gguf_open(file1);
458+
if (ctx1 == NULL) {
459+
perror(file1);
460+
exit(1);
461+
}
462+
457463
gguf_ctx *ctx2 = gguf_open(file2);
458-
if (ctx1 == NULL || ctx2 == NULL) {
459-
perror("Opening GGUF files");
464+
if (ctx2 == NULL) {
465+
perror(file2);
460466
exit(1);
461467
}
462468

gguflib.c

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
#include <unistd.h>
99
#include <string.h>
1010
#include <assert.h>
11+
#include <inttypes.h>
1112

1213
#include "gguflib.h"
1314
#include "fp16.h"
15+
#include "bf16.h"
1416

1517
/* ============================ Low level functions ========================= */
1618

@@ -43,9 +45,21 @@ struct gguf_tensor_type_features {
4345
{"q5_k", 256, 176},
4446
{"q6_k", 256, 210},
4547
{"q8_k", 256, 292},
48+
{"iq2_xxs", 256, 66},
49+
{"iq2_xs", 256, 74},
50+
{"iq3_xxs", 256, 98},
51+
{"iq1_s", 256, 110},
52+
{"iq4_nl", 256, 50},
53+
{"iq3_s", 256, 110},
54+
{"iq2_s", 256, 82},
55+
{"iq4_xs", 256, 136},
4656
{"i8", 1, 1},
4757
{"i16", 1, 2},
4858
{"i32", 1, 4},
59+
{"i64", 1, 8},
60+
{"f64", 1, 8},
61+
{"iq1_m", 256, 56},
62+
{"bf16", 1, 2},
4963
};
5064

5165
/* Return the value type name given the type ID. */
@@ -101,8 +115,8 @@ gguf_ctx *gguf_open(const char *filename) {
101115
if (fd == -1) return NULL;
102116

103117
/* Mapping successful. We can create our context object. */
104-
gguf_ctx *ctx = malloc(sizeof(*ctx));
105-
memset(ctx,0,sizeof(*ctx));
118+
gguf_ctx *ctx = calloc(1, sizeof(*ctx));
119+
if (!ctx) return NULL;
106120
ctx->fd = fd;
107121
ctx->alignment = 32; // Default alignment of GGUF files.
108122
ctx->data_off = 0; // Set later.
@@ -363,8 +377,8 @@ void gguf_print_value_callback(void *privdata, uint32_t type, union gguf_value *
363377
struct gguf_print_options *po = privdata;
364378
if (po && po->max_array_items && in_array > po->max_array_items) {
365379
if (in_array-1 == po->max_array_items)
366-
printf("... %llu more items of %llu", array_len-in_array+1,
367-
array_len);
380+
printf("... %" PRIu64 " more items of %" PRIu64 "",
381+
array_len-in_array+1, array_len);
368382
return;
369383
}
370384

@@ -396,9 +410,9 @@ void gguf_print_value_callback(void *privdata, uint32_t type, union gguf_value *
396410
case GGUF_VALUE_TYPE_STRING:
397411
printf("%.*s", (int)val->string.len, val->string.string); break;
398412
case GGUF_VALUE_TYPE_UINT64:
399-
printf("%llu", val->uint64); break;
413+
printf("%" PRIu64 "", val->uint64); break;
400414
case GGUF_VALUE_TYPE_INT64:
401-
printf("%lld", val->int64); break;
415+
printf("%" PRId64 "", val->int64); break;
402416
case GGUF_VALUE_TYPE_FLOAT64:
403417
printf("%lf", val->float64); break;
404418
default:
@@ -516,6 +530,12 @@ void gguf_store_f16_callback(void *dst, uint64_t idx, float f) {
516530
f16[idx] = to_half(f);
517531
}
518532

533+
/* Callback used to store BF16 when dequantizing. */
534+
void gguf_store_bf16_callback(void *dst, uint64_t idx, float f) {
535+
uint16_t *f16 = dst;
536+
f16[idx] = to_brain(f);
537+
}
538+
519539
/* Q8_0 blocks dequantization to floats.
520540
* 'dst' is supposed to have enough space for 'count' weights. */
521541
void gguf_q8_0_to_float(void *weights_data, void *dst, uint64_t count, store_float_callback store_callback) {
@@ -755,7 +775,7 @@ void gguf_q2_k_to_float(void *weights_data, void *dst, uint64_t count, store_flo
755775
float scale_of_scales = from_half(*((uint16_t*)(block+16+64)));
756776
float scale_of_mins = from_half(*((uint16_t*)(block+16+64+2)));
757777

758-
float scale, min;
778+
float scale = 0, min = 0;
759779
int bn = 0; // Block number
760780
for (uint64_t cluster = 0; cluster < 2; cluster++) {
761781
for (uint64_t j = 0; j < 128; j++) {
@@ -863,7 +883,8 @@ void gguf_q4_1_to_float(void *weights_data, void *dst, uint64_t count, store_flo
863883

864884
/* FP16 blocks dequantization to floats.
865885
* 'y' is supposed to have enough space for 'count' weights. */
866-
void gguf_f16_to_float(void *weights_data, float *dst, uint64_t count, store_float_callback store_callback) {
886+
static void gguf_f16_to_float(void *weights_data, void *dst, uint64_t count,
887+
store_float_callback store_callback) {
867888
float *f = dst;
868889
uint64_t i = 0; // i-th weight to dequantize.
869890
uint16_t *w16 = weights_data;
@@ -877,6 +898,23 @@ void gguf_f16_to_float(void *weights_data, float *dst, uint64_t count, store_flo
877898
}
878899
}
879900

901+
/* BF16 blocks dequantization to floats.
902+
* 'y' is supposed to have enough space for 'count' weights. */
903+
static void gguf_bf16_to_float(void *weights_data, void *dst, uint64_t count,
904+
store_float_callback store_callback) {
905+
float *f = dst;
906+
uint64_t i = 0; // i-th weight to dequantize.
907+
uint16_t *w16 = weights_data;
908+
while(i < count) {
909+
float weight = from_brain(w16[i]);
910+
if (store_callback)
911+
store_callback(dst,i,weight);
912+
else
913+
f[i] = weight;
914+
i++;
915+
}
916+
}
917+
880918
/* Convert the specified tensor (quantized or not) into an array of
881919
* floats. The array is allocated with malloc(). If the tensor is already
882920
* in FP32 floats format, it is just memcpy()-ed to the destination array.
@@ -885,10 +923,13 @@ void gguf_f16_to_float(void *weights_data, float *dst, uint64_t count, store_flo
885923
* NULL is returned as well, but errno is set to EINVAL. */
886924
float *gguf_tensor_to_float(gguf_tensor *tensor) {
887925
float *f = malloc(tensor->num_weights*sizeof(float));
926+
if (!f) return NULL;
888927
if (tensor->type == GGUF_TYPE_F32) {
889928
memcpy(f, tensor->weights_data, tensor->num_weights*sizeof(float));
890929
} else if (tensor->type == GGUF_TYPE_F16) {
891930
gguf_f16_to_float(tensor->weights_data, f, tensor->num_weights, NULL);
931+
} else if (tensor->type == GGUF_TYPE_BF16) {
932+
gguf_bf16_to_float(tensor->weights_data, f, tensor->num_weights, NULL);
892933
} else if (tensor->type == GGUF_TYPE_Q8_0) {
893934
gguf_q8_0_to_float(tensor->weights_data, f, tensor->num_weights, NULL);
894935
} else if (tensor->type == GGUF_TYPE_Q4_K) {
@@ -913,12 +954,15 @@ float *gguf_tensor_to_float(gguf_tensor *tensor) {
913954
* an array of int16_t values. */
914955
int16_t *gguf_tensor_to_f16(gguf_tensor *tensor) {
915956
int16_t *f16 = malloc(tensor->num_weights*sizeof(int16_t));
957+
if (!f16) return NULL;
916958
if (tensor->type == GGUF_TYPE_F32) {
917959
float *f = (float*)tensor->weights_data;
918960
for (uint64_t j = 0; j < tensor->num_weights; j++)
919961
f16[j] = to_half(f[j]);
920962
} else if (tensor->type == GGUF_TYPE_F16) {
921963
memcpy(f16, tensor->weights_data, tensor->num_weights*sizeof(int16_t));
964+
} else if (tensor->type == GGUF_TYPE_BF16) {
965+
gguf_bf16_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_f16_callback);
922966
} else if (tensor->type == GGUF_TYPE_Q8_0) {
923967
gguf_q8_0_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_f16_callback);
924968
} else if (tensor->type == GGUF_TYPE_Q4_K) {
@@ -938,3 +982,36 @@ int16_t *gguf_tensor_to_f16(gguf_tensor *tensor) {
938982
}
939983
return f16;
940984
}
985+
986+
/* Same as gguf_tensor_to_float() but the result will be an bf16 tensor, that is
987+
* an array of int16_t values. */
988+
int16_t *gguf_tensor_to_bf16(gguf_tensor *tensor) {
989+
int16_t *f16 = malloc(tensor->num_weights*sizeof(int16_t));
990+
if (!f16) return NULL;
991+
if (tensor->type == GGUF_TYPE_F32) {
992+
float *f = (float*)tensor->weights_data;
993+
for (uint64_t j = 0; j < tensor->num_weights; j++)
994+
f16[j] = to_half(f[j]);
995+
} else if (tensor->type == GGUF_TYPE_F16) {
996+
gguf_f16_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_bf16_callback);
997+
} else if (tensor->type == GGUF_TYPE_BF16) {
998+
memcpy(f16, tensor->weights_data, tensor->num_weights*sizeof(int16_t));
999+
} else if (tensor->type == GGUF_TYPE_Q8_0) {
1000+
gguf_q8_0_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_bf16_callback);
1001+
} else if (tensor->type == GGUF_TYPE_Q4_K) {
1002+
gguf_q4_k_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_bf16_callback);
1003+
} else if (tensor->type == GGUF_TYPE_Q6_K) {
1004+
gguf_q6_k_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_bf16_callback);
1005+
} else if (tensor->type == GGUF_TYPE_Q2_K) {
1006+
gguf_q2_k_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_bf16_callback);
1007+
} else if (tensor->type == GGUF_TYPE_Q4_0) {
1008+
gguf_q4_0_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_bf16_callback);
1009+
} else if (tensor->type == GGUF_TYPE_Q4_1) {
1010+
gguf_q4_1_to_float(tensor->weights_data, f16, tensor->num_weights, gguf_store_bf16_callback);
1011+
} else {
1012+
free(f16);
1013+
errno = EINVAL;
1014+
return NULL;
1015+
}
1016+
return f16;
1017+
}

0 commit comments

Comments
 (0)