Skip to content

Commit 99009e7

Browse files
ikawrakowKawrakowggerganov
authored
ggml : add SOTA 2,3,4,5,6 bit k-quantizations (#1684)
* Starting to add k-quantization to ggml I think it is better to have quantization separate from ggml. For now just adding the k-quants there, but it would be better to also factor out the existing ggml quantizations. * Adding Q3_K and Q8_K (de)-quantization * Q3_K now working on CUDA and AVX2/scalar CUDA is not ideal - ~50% slower than Q4_0 for single token prediction, about the same in batch mode (perplexity). CPU single token is ~55 ms (on Ryzen 7950X). * Some improvement for Q3_K on CUDA It is now ~22.5 ms/token on my GPU, so ~30% slower than Q4_0. * Some more CUDA optimizations for Q3_K Single token is now 20.5 ms/token (~20% slower than Q4_0). Perplexity is on par with Q4_0. * Adding Q4_K - scalar, AVX2, CUDA Performance is the same or perhaps very slightly better than Q4_0 on the CPU. On the GPU, single token prediction is ~10% better than Q4_0, batch mode (perplexity is about the same). * Adding Q6_K - scalar, AVX2, CUDA Performance is ~40% lower compared to Q4_K on the CPU. This is to be expected, considering that we are memory bound on the CPU and the 6-bit model is ~44% larger than the 4-bit. On the GPU, single token prediction is ~6% lower than Q4_0, batch mode (perplexity) is even closer (but still slower). * Adding Q5_K - scalar, AVX2, CUDA Performance is ~20% lower compared to Q4_K on the CPU. This is to be expected, considering that we are memory bound on the CPU and the 5-bit model is ~22% larger than the 4-bit. On the GPU, single token prediction is about the same as Q4_0 for both, single token and batch prediction. * Per convention, all QX_K quantizations use Q5_K for output.weight * Adding quantization mixes * Quantization mixes: didn't quite get what I wanted in the last commit * Q4_K dot product for ARM_NEON * Q6_K dot product for ARM_NEON * Q5_K dot product for ARM_NEON * Adding Q3_K dot for ARM_NEON It is 22% slower than Q4_K, despite the smaller model size. On x86_64, where we are memory bound, the Q3_K model is quite a bit faster than Q4_K. * A very slightly faster ARM_NEON Q3_K dot * Adding Q2_K - just CUDA for now Token prediction is pretty good - about 15.5 ms on a RTX 4080. Perplexity is about the same as Q4_K. * Adding scalar and AVX2 Q2_K dot * Adding ARM_NEON Q2_K dot About the same performance as Q4_K. * A slightly faster ARM_NEON Q2_K dot Single token prediction is now ~36 ms on M2 Max. The code is much simpler too. * Fixed bug in Q2_K CUDA dot product kernel Stranegly enough, for the few prompts I tried with the 7B model the responses looked perfectly reasonable. Only realized something is not quite right when I tried the larger models and started getting nonse back. In any case, Q2_K single token evaluation time on an RTX 4080 in a Ryzen7950X box iusing CUDA and model fully loaded on the GPU are ~15.5 ms for 7B, ~25.4 ms for 13B, and ~55.8 ms for 30B. The max number of layers that fit in VRAM for The 65B is 32. With that, we get ~330 ms per token, which is not that much faster than just running on the CPU (~470 ms per token). * Don't print zeros/NaNs when no count histogram has been collected * A 10% faster CUDA vector dot kernel for Q3_K Q3_K is now running at ~18.5 ms / token on CUDA, so the gap to Q4_0 is only 10%. It seems memory acccess pattern is more important for performance than the amount of computation the kernel does. * A slightly daster Q4_K AVX2 dot product For perplexity, where we are less memory bound, time per pass drops by ~5%. Barely measurable difference for single token prediction. * A slightly faster ARM_NEON A4_K dot product * Minor * Fix quantization error test We cannot possibly be expecting rmse < 0.002 for 2- and 3-bit quantization variants. * Fix docker build I have been sloppy with vector reinterpret casts on ARM_NEON. It seems clang is very forgiving in that regard. * Added forgotten ggml.o dependence on k_quants.h to the Makefile * Had unintentionally committed the Makefile with -Ofast enabled * ggml : rename k_quants -> ggml-quants-k, use lowercase in code --------- Co-authored-by: Iwan Kawrakow <[email protected]> Co-authored-by: Georgi Gerganov <[email protected]>
1 parent 5220a99 commit 99009e7

File tree

12 files changed

+3148
-29
lines changed

12 files changed

+3148
-29
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,8 @@ endif()
396396
add_library(ggml OBJECT
397397
ggml.c
398398
ggml.h
399+
ggml-quants-k.h
400+
ggml-quants-k.c
399401
${GGML_SOURCES_CUDA}
400402
${GGML_SOURCES_OPENCL}
401403
${GGML_SOURCES_METAL}

Makefile

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ endif
4040
#
4141

4242
# keep standard at C11 and C++11
43-
CFLAGS = -I. -O3 -std=c11 -fPIC
44-
CXXFLAGS = -I. -I./examples -O3 -std=c++11 -fPIC
43+
# -Ofast tends to produce faster code, but may not be available for some compilers.
44+
#OPT = -Ofast
45+
OPT = -O3
46+
CFLAGS = -I. $(OPT) -std=c11 -fPIC
47+
CXXFLAGS = -I. -I./examples $(OPT) -std=c++11 -fPIC
4548
LDFLAGS =
4649

4750
ifdef LLAMA_DEBUG
@@ -228,7 +231,10 @@ $(info )
228231
# Build library
229232
#
230233

231-
ggml.o: ggml.c ggml.h ggml-cuda.h
234+
ggml.o: ggml.c ggml.h ggml-cuda.h ggml-quants-k.h
235+
$(CC) $(CFLAGS) -c $< -o $@
236+
237+
ggml-quants-k.o: ggml-quants-k.c ggml-quants-k.h ggml.h ggml-cuda.h
232238
$(CC) $(CFLAGS) -c $< -o $@
233239

234240
llama.o: llama.cpp ggml.h ggml-cuda.h llama.h llama-util.h
@@ -247,25 +253,25 @@ clean:
247253
# Examples
248254
#
249255

250-
main: examples/main/main.cpp build-info.h ggml.o llama.o common.o $(OBJS)
256+
main: examples/main/main.cpp build-info.h ggml.o ggml-quants-k.o llama.o common.o $(OBJS)
251257
$(CXX) $(CXXFLAGS) $(filter-out %.h,$^) -o $@ $(LDFLAGS)
252258
@echo
253259
@echo '==== Run ./main -h for help. ===='
254260
@echo
255261

256-
quantize: examples/quantize/quantize.cpp build-info.h ggml.o llama.o $(OBJS)
262+
quantize: examples/quantize/quantize.cpp build-info.h ggml.o llama.o ggml-quants-k.o $(OBJS)
257263
$(CXX) $(CXXFLAGS) $(filter-out %.h,$^) -o $@ $(LDFLAGS)
258264

259-
quantize-stats: examples/quantize-stats/quantize-stats.cpp build-info.h ggml.o llama.o $(OBJS)
265+
quantize-stats: examples/quantize-stats/quantize-stats.cpp build-info.h ggml.o llama.o ggml-quants-k.o $(OBJS)
260266
$(CXX) $(CXXFLAGS) $(filter-out %.h,$^) -o $@ $(LDFLAGS)
261267

262-
perplexity: examples/perplexity/perplexity.cpp build-info.h ggml.o llama.o common.o $(OBJS)
268+
perplexity: examples/perplexity/perplexity.cpp build-info.h ggml.o llama.o common.o ggml-quants-k.o $(OBJS)
263269
$(CXX) $(CXXFLAGS) $(filter-out %.h,$^) -o $@ $(LDFLAGS)
264270

265-
embedding: examples/embedding/embedding.cpp build-info.h ggml.o llama.o common.o $(OBJS)
271+
embedding: examples/embedding/embedding.cpp build-info.h ggml.o llama.o common.o ggml-quants-k.o $(OBJS)
266272
$(CXX) $(CXXFLAGS) $(filter-out %.h,$^) -o $@ $(LDFLAGS)
267273

268-
save-load-state: examples/save-load-state/save-load-state.cpp build-info.h ggml.o llama.o common.o $(OBJS)
274+
save-load-state: examples/save-load-state/save-load-state.cpp build-info.h ggml.o llama.o common.o ggml-quants-k.o $(OBJS)
269275
$(CXX) $(CXXFLAGS) $(filter-out %.h,$^) -o $@ $(LDFLAGS)
270276

271277
server: examples/server/server.cpp examples/server/httplib.h examples/server/json.hpp build-info.h ggml.o llama.o common.o $(OBJS)
@@ -287,7 +293,7 @@ benchmark-matmult: examples/benchmark/benchmark-matmult.cpp build-info.h ggml.o
287293
$(CXX) $(CXXFLAGS) $(filter-out %.h,$^) -o $@ $(LDFLAGS)
288294
./$@
289295

290-
vdot: pocs/vdot/vdot.cpp ggml.o $(OBJS)
296+
vdot: pocs/vdot/vdot.cpp ggml.o ggml-quants-k.o $(OBJS)
291297
$(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS)
292298

293299
.PHONY: tests clean

examples/quantize-stats/quantize-stats.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,9 @@ int main(int argc, char ** argv) {
282282
break;
283283
}
284284
int j;
285-
for (j = 0; j < GGML_TYPE_COUNT && strcmp(argv[i], ggml_type_name((ggml_type) j)) != 0; j++) {
286-
// find match
285+
for (j = 0; j < GGML_TYPE_COUNT; ++j) {
286+
const auto * name = ggml_type_name((ggml_type) j);
287+
if (name && strcmp(argv[i], name) == 0) break;
287288
}
288289
if (j < GGML_TYPE_COUNT) {
289290
params.include_types.push_back((ggml_type) j);

examples/quantize/quantize.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,23 @@
77
#include <string>
88

99
static const std::map<std::string, llama_ftype> LLAMA_FTYPE_MAP = {
10-
{"q4_0", LLAMA_FTYPE_MOSTLY_Q4_0},
11-
{"q4_1", LLAMA_FTYPE_MOSTLY_Q4_1},
12-
{"q5_0", LLAMA_FTYPE_MOSTLY_Q5_0},
13-
{"q5_1", LLAMA_FTYPE_MOSTLY_Q5_1},
14-
{"q8_0", LLAMA_FTYPE_MOSTLY_Q8_0},
10+
{"q4_0", LLAMA_FTYPE_MOSTLY_Q4_0},
11+
{"q4_1", LLAMA_FTYPE_MOSTLY_Q4_1},
12+
{"q5_0", LLAMA_FTYPE_MOSTLY_Q5_0},
13+
{"q5_1", LLAMA_FTYPE_MOSTLY_Q5_1},
14+
{"q8_0", LLAMA_FTYPE_MOSTLY_Q8_0},
15+
{"q2_K", LLAMA_FTYPE_MOSTLY_Q2_K},
16+
{"q3_K", LLAMA_FTYPE_MOSTLY_Q3_K_M},
17+
{"q3_K_S", LLAMA_FTYPE_MOSTLY_Q3_K_S},
18+
{"q3_K_M", LLAMA_FTYPE_MOSTLY_Q3_K_M},
19+
{"q3_K_L", LLAMA_FTYPE_MOSTLY_Q3_K_L},
20+
{"q4_K", LLAMA_FTYPE_MOSTLY_Q4_K_M},
21+
{"q4_K_S", LLAMA_FTYPE_MOSTLY_Q4_K_S},
22+
{"q4_K_M", LLAMA_FTYPE_MOSTLY_Q4_K_M},
23+
{"q5_K", LLAMA_FTYPE_MOSTLY_Q5_K_M},
24+
{"q5_K_S", LLAMA_FTYPE_MOSTLY_Q5_K_S},
25+
{"q5_K_M", LLAMA_FTYPE_MOSTLY_Q5_K_M},
26+
{"q6_K", LLAMA_FTYPE_MOSTLY_Q6_K},
1527
};
1628

1729
bool try_parse_ftype(const std::string & ftype_str, llama_ftype & ftype, std::string & ftype_str_out) {

0 commit comments

Comments
 (0)