From 7733f0c76081b2a69b5f8b192db2db7c43629d58 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 25 Mar 2024 01:39:56 -0400 Subject: [PATCH 1/5] ggml : support AVX512VNNI (#6280) This change causes some quants (e.g. Q4_0, Q8_0) to go faster on some architectures (e.g. AMD Zen 4). --- ggml-quants.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ggml-quants.c b/ggml-quants.c index 2eaca0593..f26798acc 100644 --- a/ggml-quants.c +++ b/ggml-quants.c @@ -132,7 +132,7 @@ static inline __m256 sum_i16_pairs_float(const __m256i x) { } static inline __m256 mul_sum_us8_pairs_float(const __m256i ax, const __m256i sy) { -#if __AVXVNNI__ +#if defined(__AVXVNNI__) || defined(__AVX512VNNI__) const __m256i zero = _mm256_setzero_si256(); const __m256i summed_pairs = _mm256_dpbusd_epi32(zero, ax, sy); return _mm256_cvtepi32_ps(summed_pairs); From 64e7b47c6986221f2ff5c57c89dfc018bb0e9e6d Mon Sep 17 00:00:00 2001 From: Minsoo Cheong <54794500+mscheong01@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:38:22 +0900 Subject: [PATCH 2/5] examples : add "retrieval" (#6193) * add `retrieval` example * add README * minor fixes * cast filepos on print * remove use of variable sized array * store similarities in separate vector * print error on insufficient batch size * fix error message printing * assign n_batch value to n_ubatch * fix param definitions * define retrieval-only parameters in retrieval.cpp * fix `--context-file` option to be provided multiple times for multiple files * use vector for `query_emb` * add usage description in README * fix merge conflict * fix usage printing * remove seed setting * fix lint * increase file read buffer size * retrieval : minor --------- Co-authored-by: Georgi Gerganov --- .gitignore | 1 + Makefile | 6 +- common/common.cpp | 2 +- common/common.h | 2 + common/log.h | 1 + examples/CMakeLists.txt | 1 + examples/retrieval/CMakeLists.txt | 5 + examples/retrieval/README.md | 69 ++++++ examples/retrieval/retrieval.cpp | 350 ++++++++++++++++++++++++++++++ retrieval | Bin 1637080 -> 0 bytes 10 files changed, 435 insertions(+), 2 deletions(-) create mode 100644 examples/retrieval/CMakeLists.txt create mode 100644 examples/retrieval/README.md create mode 100644 examples/retrieval/retrieval.cpp delete mode 100755 retrieval diff --git a/.gitignore b/.gitignore index 072945180..9fb5b80c3 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ models-mnt /batched-bench /export-lora /finetune +/retrieval /speculative /parallel /train-text-from-scratch diff --git a/Makefile b/Makefile index 4f260cc3d..130fde838 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ BUILD_TARGETS = \ main quantize quantize-stats perplexity imatrix embedding vdot q8dot train-text-from-scratch convert-llama2c-to-ggml \ simple batched batched-bench save-load-state server gguf gguf-split llama-bench libllava.a llava-cli baby-llama beam-search \ - speculative infill tokenize benchmark-matmult parallel finetune export-lora lookahead lookup passkey gritlm tests/test-c.o + retrieval speculative infill tokenize benchmark-matmult parallel finetune export-lora lookahead lookup passkey gritlm tests/test-c.o # Binaries only useful for tests TEST_TARGETS = \ @@ -804,6 +804,10 @@ export-lora: examples/export-lora/export-lora.cpp ggml.o common/common.h $(OBJS) $(CXX) $(CXXFLAGS) -c $< -o $(call GET_OBJ_FILE, $<) $(CXX) $(CXXFLAGS) $(filter-out %.h $<,$^) $(call GET_OBJ_FILE, $<) -o $@ $(LDFLAGS) +retrieval: examples/retrieval/retrieval.cpp ggml.o llama.o $(COMMON_DEPS) $(OBJS) + $(CXX) $(CXXFLAGS) -c $< -o $(call GET_OBJ_FILE, $<) + $(CXX) $(CXXFLAGS) $(filter-out %.h $<,$^) $(call GET_OBJ_FILE, $<) -o $@ $(LDFLAGS) + speculative: examples/speculative/speculative.cpp ggml.o llama.o $(COMMON_DEPS) grammar-parser.o $(OBJS) $(CXX) $(CXXFLAGS) -c $< -o $(call GET_OBJ_FILE, $<) $(CXX) $(CXXFLAGS) $(filter-out %.h $<,$^) $(call GET_OBJ_FILE, $<) -o $@ $(LDFLAGS) diff --git a/common/common.cpp b/common/common.cpp index fb80d4bf7..9dec08430 100644 --- a/common/common.cpp +++ b/common/common.cpp @@ -157,7 +157,7 @@ bool gpt_params_parse(int argc, char ** argv, gpt_params & params) { return result; } -static bool gpt_params_find_arg(int argc, char ** argv, const std::string & arg, gpt_params & params, int & i, bool & invalid_param) { +bool gpt_params_find_arg(int argc, char ** argv, const std::string & arg, gpt_params & params, int & i, bool & invalid_param) { llama_sampling_params& sparams = params.sparams; if (arg == "-s" || arg == "--seed") { diff --git a/common/common.h b/common/common.h index a223eceaa..99ee90bc3 100644 --- a/common/common.h +++ b/common/common.h @@ -171,6 +171,8 @@ bool gpt_params_parse(int argc, char ** argv, gpt_params & params); void gpt_print_usage(int argc, char ** argv, const gpt_params & params); +bool gpt_params_find_arg(int argc, char ** argv, const std::string & arg, gpt_params & params, int & i, bool & invalid_param); + std::string get_system_info(const gpt_params & params); std::string gpt_random_prompt(std::mt19937 & rng); diff --git a/common/log.h b/common/log.h index eb111e784..48d21e43c 100644 --- a/common/log.h +++ b/common/log.h @@ -566,6 +566,7 @@ inline void log_print_usage() printf(" --log-new Create a separate new log file on start. " "Each log file will have unique name: \"..log\"\n"); printf(" --log-append Don't truncate the old log file.\n"); + printf("\n"); } #define log_dump_cmdline(argc, argv) log_dump_cmdline_impl(argc, argv) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b59cc65bf..76496bf06 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -34,6 +34,7 @@ else() add_subdirectory(perplexity) add_subdirectory(quantize) add_subdirectory(quantize-stats) + add_subdirectory(retrieval) add_subdirectory(save-load-state) add_subdirectory(simple) add_subdirectory(passkey) diff --git a/examples/retrieval/CMakeLists.txt b/examples/retrieval/CMakeLists.txt new file mode 100644 index 000000000..eaabae08d --- /dev/null +++ b/examples/retrieval/CMakeLists.txt @@ -0,0 +1,5 @@ +set(TARGET retrieval) +add_executable(${TARGET} retrieval.cpp) +install(TARGETS ${TARGET} RUNTIME) +target_link_libraries(${TARGET} PRIVATE common llama ${CMAKE_THREAD_LIBS_INIT}) +target_compile_features(${TARGET} PRIVATE cxx_std_11) diff --git a/examples/retrieval/README.md b/examples/retrieval/README.md new file mode 100644 index 000000000..2b2595c46 --- /dev/null +++ b/examples/retrieval/README.md @@ -0,0 +1,69 @@ +# llama.cpp/examples/retrieval + +Demonstration of simple retrieval technique based on cosine similarity + +More info: +https://github.com/ggerganov/llama.cpp/pull/6193 + +### How to use + +`retieval.cpp` has parameters of its own: +- `--context-file`: file to be embedded - state this option multiple times to embed multiple files +- `--chunk-size`: minimum size of each text chunk to be embedded +- `--chunk-separator`: STRING to divide chunks by. newline by default + +`retrieval` example can be tested as follows: + +```bash +make -j && ./retrieval --model ./models/bge-base-en-v1.5-f16.gguf --top-k 3 --context-file README.md --context-file License --chunk-size 100 --chunk-separator . +``` + +This chunks and embeds all given files and starts a loop requesting query inputs: + +``` +Enter query: +``` + +On each query input, top k chunks are shown along with file name, chunk position within file and original text: + +``` +Enter query: describe the mit license +batch_decode: n_tokens = 6, n_seq = 1 +Top 3 similar chunks: +filename: README.md +filepos: 119 +similarity: 0.762334 +textdata: +png) + +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) + +[Roadmap](https://github. +-------------------- +filename: License +filepos: 0 +similarity: 0.725146 +textdata: +MIT License + +Copyright (c) 2023 Georgi Gerganov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +-------------------- +filename: README.md +filepos: 9178 +similarity: 0.621722 +textdata: +com/cztomsik/ava) (MIT) +- [ptsochantaris/emeltal](https://github.com/ptsochantaris/emeltal) +- [pythops/tenere](https://github. +-------------------- +``` diff --git a/examples/retrieval/retrieval.cpp b/examples/retrieval/retrieval.cpp new file mode 100644 index 000000000..5ba71e76a --- /dev/null +++ b/examples/retrieval/retrieval.cpp @@ -0,0 +1,350 @@ +#include "common.h" +#include "llama.h" + +#include +#include + +struct retrieval_params { + std::vector context_files; // context files to embed + int32_t chunk_size = 64; // chunk size for context embedding + std::string chunk_separator = "\n"; // chunk separator for context embedding +}; + +static void retrieval_params_print_usage(int argc, char ** argv, gpt_params & gpt_params, retrieval_params & params) { + gpt_print_usage(argc, argv, gpt_params); + printf("retrieval options:\n"); + printf(" --context-file FNAME file containing context to embed.\n"); + printf(" specify multiple files by providing --context-file option multiple times.\n"); + printf(" --chunk-size N minimum length of embedded text chunk (default:%d)\n", params.chunk_size); + printf(" --chunk-separator STRING\n"); + printf(" string to separate chunks (default: \"\\n\")\n"); + printf("\n"); +} + +static void retrieval_params_parse(int argc, char ** argv, gpt_params & gpt_params, retrieval_params & retrieval_params) { + int i = 1; + std::string arg; + while (i < argc) { + arg = argv[i]; + bool invalid_gpt_param = false; + if(gpt_params_find_arg(argc, argv, argv[i], gpt_params, i, invalid_gpt_param)) { + if (invalid_gpt_param) { + fprintf(stderr, "error: invalid argument: %s\n", arg.c_str()); + retrieval_params_print_usage(argc, argv, gpt_params, retrieval_params); + exit(1); + } + // option was parsed by gpt_params_find_arg + } else if (arg == "--context-file") { + if (++i >= argc) { + fprintf(stderr, "error: missing argument for --context-file\n"); + retrieval_params_print_usage(argc, argv, gpt_params, retrieval_params); + exit(1); + } + std::ifstream file(argv[i]); + if (!file) { + fprintf(stderr, "error: failed to open file '%s'\n", argv[i]); + retrieval_params_print_usage(argc, argv, gpt_params, retrieval_params); + exit(1); + } + // store the external file name in params + retrieval_params.context_files.push_back(argv[i]); + } else if (arg == "--chunk-size") { + if (++i >= argc) { + fprintf(stderr, "error: missing argument for --chunk-size\n"); + retrieval_params_print_usage(argc, argv, gpt_params, retrieval_params); + exit(1); + } + retrieval_params.chunk_size = std::stoi(argv[i]); + } else if (arg == "--chunk-separator") { + if (++i >= argc) { + fprintf(stderr, "error: missing argument for --chunk-separator\n"); + retrieval_params_print_usage(argc, argv, gpt_params, retrieval_params); + exit(1); + } + retrieval_params.chunk_separator = argv[i]; + } else { + // unknown argument + fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); + retrieval_params_print_usage(argc, argv, gpt_params, retrieval_params); + exit(1); + } + i++; + } +} + +struct chunk { + // filename + std::string filename; + // original file position + size_t filepos; + // original text data + std::string textdata = ""; + // tokenized text data + std::vector tokens; + // embedding + std::vector embedding; +}; + +// chunk file data to chunks of size >= chunk_size +// chunk_separator is the separator between chunks +static std::vector chunk_file(const std::string & filename, int chunk_size, const std::string & chunk_separator) { + std::vector chunks; + std::ifstream f(filename.c_str()); + + if (!f.is_open()) { + fprintf(stderr, "Error: could not open file %s\n", filename.c_str()); + return chunks; + } + + chunk current_chunk; + char buffer[1024]; + int64_t filepos = 0; + std::string current = ""; + while (f.read(buffer, 1024)) { + current += std::string(buffer, f.gcount()); + size_t pos; + while ((pos = current.find(chunk_separator)) != std::string::npos) { + current_chunk.textdata += current.substr(0, pos + chunk_separator.size()); + if ((int) current_chunk.textdata.size() > chunk_size) { + // save chunk + current_chunk.filepos = filepos; + current_chunk.filename = filename; + chunks.push_back(current_chunk); + // update filepos + filepos += (int) current_chunk.textdata.size(); + // reset current_chunk + current_chunk = chunk(); + } + current = current.substr(pos + chunk_separator.size()); + } + + } + // add leftover data to last chunk + if (current_chunk.textdata.size() > 0) { + if (chunks.empty()) { + current_chunk.filepos = filepos; + current_chunk.filename = filename; + chunks.push_back(current_chunk); + } else { + chunks.back().textdata += current_chunk.textdata; + } + } + f.close(); + return chunks; +} + +static void batch_add_seq(llama_batch & batch, const std::vector & tokens, int seq_id) { + for (size_t i = 0; i < tokens.size(); i++) { + llama_batch_add(batch, tokens[i], i, { seq_id }, i == tokens.size() - 1); + } +} + +static void batch_decode(llama_context * ctx, llama_batch & batch, float * output, int n_seq, int n_embd) { + // clear previous kv_cache values (irrelevant for embeddings) + llama_kv_cache_clear(ctx); + + // run model + fprintf(stderr, "%s: n_tokens = %d, n_seq = %d\n", __func__, batch.n_tokens, n_seq); + if (llama_decode(ctx, batch) < 0) { + fprintf(stderr, "%s : failed to decode\n", __func__); + } + + for (int i = 0; i < batch.n_tokens; i++) { + if (!batch.logits[i]) { + continue; + } + + // try to get sequence embeddings - supported only when pooling_type is not NONE + const float * embd = llama_get_embeddings_seq(ctx, batch.seq_id[i][0]); + if (embd == NULL) { + embd = llama_get_embeddings_ith(ctx, i); + if (embd == NULL) { + fprintf(stderr, "%s: failed to get embeddings for token %d\n", __func__, i); + continue; + } + } + + float * out = output + batch.seq_id[i][0] * n_embd; + llama_embd_normalize(embd, out, n_embd); + } +} + +int main(int argc, char ** argv) { + gpt_params params; + retrieval_params retrieval_params; + + retrieval_params_parse(argc, argv, params, retrieval_params); + + // For BERT models, batch size must be equal to ubatch size + params.n_ubatch = params.n_batch; + + if (retrieval_params.chunk_size <= 0) { + fprintf(stderr, "chunk_size must be positive\n"); + return 1; + } + if (retrieval_params.context_files.empty()) { + fprintf(stderr, "context_files must be specified\n"); + return 1; + } + params.embedding = true; + + print_build_info(); + + printf("processing files:\n"); + for (auto & context_file : retrieval_params.context_files) { + printf("%s\n", context_file.c_str()); + } + + std::vector chunks; + for (auto & context_file : retrieval_params.context_files) { + std::vector file_chunk = chunk_file(context_file, retrieval_params.chunk_size, retrieval_params.chunk_separator); + chunks.insert(chunks.end(), file_chunk.begin(), file_chunk.end()); + } + printf("Number of chunks: %ld\n", chunks.size()); + + llama_backend_init(); + llama_numa_init(params.numa); + + llama_model * model; + llama_context * ctx; + + // load the model + std::tie(model, ctx) = llama_init_from_gpt_params(params); + if (model == NULL) { + fprintf(stderr, "%s: error: unable to load model\n", __func__); + return 1; + } + + const int n_ctx_train = llama_n_ctx_train(model); + const int n_ctx = llama_n_ctx(ctx); + + if (n_ctx > n_ctx_train) { + fprintf(stderr, "%s: warning: model was trained on only %d context tokens (%d specified)\n", + __func__, n_ctx_train, n_ctx); + } + + // print system information + { + fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", get_system_info(params).c_str()); + } + + // max batch size + const uint64_t n_batch = params.n_batch; + GGML_ASSERT(params.n_batch >= params.n_ctx); + + // tokenize the prompts and trim + for (auto & chunk : chunks) { + auto inp = ::llama_tokenize(ctx, chunk.textdata, true, false); + if (inp.size() > n_batch) { + fprintf(stderr, "%s: error: chunk size (%lld) exceeds batch size (%lld), increase batch size and re-run\n", + __func__, (long long int) inp.size(), (long long int) n_batch); + return 1; + } + // add eos if not present + if (inp.empty() || inp.back() != llama_token_eos(model)) { + inp.push_back(llama_token_eos(model)); + } + chunk.tokens = inp; + } + + // tokenization stats + if (params.verbose_prompt) { + for (int i = 0; i < (int) chunks.size(); i++) { + fprintf(stderr, "%s: prompt %d: '%s'\n", __func__, i, chunks[i].textdata.c_str()); + fprintf(stderr, "%s: number of tokens in prompt = %zu\n", __func__, chunks[i].tokens.size()); + for (int j = 0; j < (int) chunks[i].tokens.size(); j++) { + fprintf(stderr, "%6d -> '%s'\n", chunks[i].tokens[j], llama_token_to_piece(ctx, chunks[i].tokens[j]).c_str()); + } + fprintf(stderr, "\n\n"); + } + } + + // initialize batch + const int n_chunks = chunks.size(); + struct llama_batch batch = llama_batch_init(n_batch, 0, 1); + + // allocate output + const int n_embd = llama_n_embd(model); + std::vector embeddings(n_chunks * n_embd, 0); + float * emb = embeddings.data(); + + // break into batches + int p = 0; // number of prompts processed already + int s = 0; // number of prompts in current batch + for (int k = 0; k < n_chunks; k++) { + // clamp to n_batch tokens + auto & inp = chunks[k].tokens; + + const uint64_t n_toks = inp.size(); + + // encode if at capacity + if (batch.n_tokens + n_toks > n_batch) { + float * out = emb + p * n_embd; + batch_decode(ctx, batch, out, s, n_embd); + llama_batch_clear(batch); + p += s; + s = 0; + } + + // add to batch + batch_add_seq(batch, inp, s); + s += 1; + } + + // final batch + float * out = emb + p * n_embd; + batch_decode(ctx, batch, out, s, n_embd); + + // save embeddings to chunks + for (int i = 0; i < n_chunks; i++) { + chunks[i].embedding = std::vector(emb + i * n_embd, emb + (i + 1) * n_embd); + // clear tokens as they are no longer needed + chunks[i].tokens.clear(); + } + + // start loop, receive query and return top k similar chunks based on cosine similarity + std::string query; + while (true) { + printf("Enter query: "); + std::getline(std::cin, query); + std::vector query_tokens = llama_tokenize(ctx, query, true); + + struct llama_batch query_batch = llama_batch_init(n_batch, 0, 1); + batch_add_seq(query_batch, query_tokens, 0); + + std::vector query_emb(n_embd, 0); + batch_decode(ctx, query_batch, query_emb.data(), 1, n_embd); + + llama_batch_clear(query_batch); + + // compute cosine similarities + { + std::vector> similarities; + for (int i = 0; i < n_chunks; i++) { + float sim = llama_embd_similarity_cos(chunks[i].embedding.data(), query_emb.data(), n_embd); + similarities.push_back(std::make_pair(i, sim)); + } + + // sort similarities + std::sort(similarities.begin(), similarities.end(), [](const std::pair & a, const std::pair & b) { + return a.second > b.second; + }); + + printf("Top %d similar chunks:\n", params.sparams.top_k); + for (int i = 0; i < std::min(params.sparams.top_k, (int) chunks.size()); i++) { + printf("filename: %s\n", chunks[similarities[i].first].filename.c_str()); + printf("filepos: %lld\n", (long long int) chunks[similarities[i].first].filepos); + printf("similarity: %f\n", similarities[i].second); + printf("textdata:\n%s\n", chunks[similarities[i].first].textdata.c_str()); + printf("--------------------\n"); + } + } + } + + // clean up + llama_print_timings(ctx); + llama_free(ctx); + llama_free_model(model); + llama_backend_free(); +} diff --git a/retrieval b/retrieval deleted file mode 100755 index dd31789f89c3a41fb84b88c08444eb8e4a9dbfbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1637080 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%0A%a^@}ZGOA6iHOiWW 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
    mQGIOb(Z}Y$ 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

    )#ZC5&`;h?Tn)c{Z^RW|R_uFKcrG<) z>2~}`HG1uj1r!CZt%#4FHU^9Zx@y0j%5la)*2>lVQ%%?Z0>|F{zx3Xhd2cDWRZPjE z{G+vF0K8BgYek*u+BHM{C$zo}A8JjWiEl|7{>n+auq~TuTf){HKSRa$Nnx$GG|-I}7L!D}%zEThQelWZl?`eCuQM`TiJV zVO2eO_NBxqr`YO<4g8+S56f?TAkujWnwJpuVR`bxg|$|r^1j4@p2B{-0y`q1cS3hB zVmBhT%Qn7k9)6x1*gsbwzq0d9vgfd1!?G70mh3n7f%o?Vzidk9G9&U}6MR`H{8<=$ zni+mA9KJKc^?gp4csZ#*7ANO!+TO)8c|4m-A9o@{9Pgq}O=lAd&zOa5So{F5#I=z+ zuxv!umj%qce4p4)3v6A>;yap<-3g2bcY&*$ZywB8ur9QV&L;A9n=5*3wkyvMI=#6=z;hnBy7bYG!n=>#@7Z#pX?7 zvi$+ut77JS(~w}-6X;D>5xZ&^*KXms0NI!LBHTuMV~7#`KQRuVt8X9&X++L)5PPj3 zkh?S@m-`yO){E%y#WwR+>V=wbE9c1YvXoHoG~|hXnWO5c;+w=T&&$64Byy6=$o*w~ z`vLjO8_32RkoSpB^&s);zGm%zm9mhyHrd2DwPO#^p!CrC=v2bItDwK|#(zv!LmUeK zCTq>c`6DD}C33bfZEeU>qP_KRPq<{=k$)+dd631t&tz@y2|x2L{P)JEifc<3X4DFt zWepd-M#aL6-EuDc!Evc0_n$_+CvqUsMTqQ1j)CVyCM4%s_Hj1+r10;8i*`ynMeylsqrq_!ocdSrY>L#8 zt2EF~;L)iVyt%+p_=kT5R|ID+!k2%Gtl%5==;BKyGC|=h4q}5SF_6BbT%>$WJg6xY ziQ|;acwNDZxAQNtKhf;x)O#3NQF7@%9%Q`#?uxy;g8Q#<|20;}soL zFypteXpzPb$g`KRHRvoJzWX?3uN;ep@1BPYdSR@eM>~(aSYkIx42gQizr>*InTLOe z@Q;=BCpa*Bm2t!Ulx#}C|1S-io)&Q}O~=>ciho>MfPTY&C$GpIZ27hI{p?$?f9h>X z9IvFkmY(L19hgCUSDAb1;DG4%_rteGLW7P|uj1dt|L+j%UJ`T1ynj{sRIcB(WmP#j zcHL6XfZo_cKPk6wag@)LIR;OVoNjx{3omSA?RXBpqWacQ=SS#aQX+yPjo9L)-q1UL zPKfgM>4Kc^-&lSse{AXNkwc(4Cux(){w#=+9~$X6j9hBSnWym6h^X4S{AB*;l$d8u z=Kqc3B#t*x4*t0y|6ox;d@VU!j*`>*8IEsoe467Kj?Z!oIh>k*v`<#eM>i-n&+x8?D3ZrR?52cA7rA}u zh0e*-)s)v#u&2vjAb!oE@NOnAajqu+PsZ-&j9D6`J4O8QyqO04%#HY&|8(q?K#a{5 z*N?BZX8+^7^#R}dZMsi%nb{vlG6~w12`%%Ov4_DofmrjtTUn2@jLO`bxBf2v-qs3Ut6#5VbeIU0h`|b)6#tl~LPjNJ{Ki^-RR=bhX8+}faF~KW*$nft&ZuS`AH=at{eHJ*AN6j_- zV&_fAS@-JGZI?t|B65Tn=4~DHvyt^LgR%4Sj3dnKXa?WJuC_!~_LR&Rvb$tPIwfPb zg1CLC2ui1>Tn>iarjGjS^aTs$~ z^pnwy%`o=v@V7Uy*3GSiFDPN2rmz=ZJHq5FW&T^BhoYYoUkeLzMzQ5YzNXPu*@qcM zggb+PLF!YfyS2SGGG_VjZrn2x@6qo^8CfNLAX7iO99nx8UZE5Hh^&(zYx^&=W|wmw zns@=5UTts9d&P!&8vpQ%`ETr=HvjS3Ck&-~3g$mitKjPz%sYNLf57g?p^LJ|nhQNC z<(pMxwM+SKBky?-T2lJq{@crflnI7lWx_H_P&Z|Q&}{JmmNkV~m{K~O@@X%#(E-ku~+&A zz1_FKOp_r17wn@nxV$`n=k6`@pRC;tT;7;JzjkBtfJE7^zA}IK?p-=uN+ixHwEH<= zvl_U_-sjB)H}00bOoT<^Vsj zvvmL`ba871zO#VI#*h9U=UU)9#QyerJleuKveoAQU)eXy{|c=Mj7J{$-^sr21>O;X zTvFaKmvJ!1$2f-0?mKBN?@K=T_wOSHEj-lUzpp>=9jL=s(ebvO{jXn#5>*v5M%fKL zh=G6C_zb~~Yrcm$`&Yi_@sdeelC-M8NhHqNUdCd*9k+})~B{(dy4vEol4xX+ZdVNyt zqKk+pFyE4B2sb1d?(py1ZS*g=E;3uY8i6h(T-yWw6&#m-qKFL;JmLzFZxN5DM=s0SgRyoujqI!9Hp;h)~o~QD?h|0bD3gvm*}wpgnsnqIlnJ?40bQk zRSy#9^sP6KzV^Ubvr13q`WXDtL8k%b$5XyjMoX^ zFY$uc5VzL|Z~ip=_!w*x`cqy(2qXL zda9ona6bNi+AgG>e(l63dKUUzVBkNZY z^;~2SO;i2*X%pv-o5Nh5UG&3$1!U;;C0wuIU;Agfyxc3Uv8Ib%b2xha=4E2v6Pkc6 zjh^SnO`e~y1b>d7aJzlS>Ui&E1(}H1UbPtAKK;xBk7o`Z<7(bza(Q#GXXamL#aMk0 z?T}m&Auc<6buW9BX8z|H{?EZZ*`IqOm6%z?WJ(r(XrCofjlMCldO^}8JKutiVeM?_ z!fn_YiH*@fO4E{Q$0`_m@79X=zZNBQpU<^p$UR#fisa@#b_hRn@u_Yyj?Ac1!_2vh z@!df7FZS9$r_YrJ<=%AFoY8np*^`c)Qj-$pb}ftPs4~WOU^nEJc&;vEOvfiC>$+lM z3b>X@D1{OEa$ze>P*(1X~;lL_~CjE z;3veq3nP|iE#K*pI7II$nl05zErsHG*$z!X|Az`Qts+MpvB$yQo;j*Romh z9r|RjV?6WMn;Pz_Lw}3iwSPPX*Y+yDBA#iQ#Jdx)vBKV1e8XMF?j1M71NI=E=$Y8= zu7)+_JAYE~b<9zGvNnG$zS=zNxs1)9nrNRk2VC1MI2339VJG>@S(i@Y-`%(uzL-AA z9rdT?qxa|Ykv_!hjj&*Th^_^>=f{->I8aA1o~ zAFseicCx;|+)EjGY93?X-)~Rel4bRsF#f_dh&<=dKaJfyc(dj?o?#yx%|5>w+*-|f zi!n5>eJZ~8H=7e$a`3~$#@;dCoFI78o=1+5o6XLaaU2J6T{S!ElfXypBW3}&cI>+j zqw|zp;77P7IuQ%DPh$Uc4|Z(P3tw=k24%45%cmm$46`Vc@1ftfiEoUZjusn6;-#_g zodbQIvk(2V=KDci=xGoon9_|BLK!-yq^A2_)|!yWd+{Of>$sPA=0@rRA}8giL{82h zI_87z-S4v11as})du%l-$9~*P=APKP!DA5fFJ8-ucc#bK{11DK$$ykp74 z?)3%Uc=h$ftG`LSdIje<^yqir)10s2{KlfM#=Z1Z(v-@lhzdw;uB@G42a#Ptq39B-Z^2+T*mJ5=(SH?J?SX+M~1wXphi-LVGY? z@+KxFDw~tgF;q{SuT`Ax+ImJ&S`_>mqTu;I;QR7`1;aq=Vu^`28mvns2U34WQ`H(BE9;YkAF_W1=7aR+_yJv1}IaT@p@Iy&kC z9De|8%U%{ZRu&D{b$$ZB3Nr9?o~tHD)Dhxv-0&8nSHquFbaxLQ6T1Mv@B=#h-lEg* zi}?|5i6%dSEV>?Oe~XDGlUd`ut=8sy#dakcyixklg&-c;bMMqn(_3u8y&`xAM# zjPdBcCBRq`Fg5_VN&>F3IR01XNGJdn+R%Be3z@H#;kVbO7+SJ`h26yMDd@-r7F2-+ z+Le6wgl|;gxz@AhDK$Y3=LURMjnPf(jK)K);QS=~6OQpakl#<=6Y z<^#!DZi23*+Ppam>R9tZWw}k@gkjGZ2FHucRK~i4mxc{0OK2LjE4S&wb$I)MySPSd zZQ%Vi=sEQJ$!$I;*t)}ie>L-V9qaY_p4*$&kG?j3Jw94>_!DQ}Y<=;Wg++^h&mMWF z`6&D2C-L3i)y%W6@!V^p2gX0mbIrI1s?@AJ{uT|^FN(KW1M&7&8kZa>ex-Pc4(nZq~d>v%!% zlhv1to4Bg0!bK$O{!Q|on(%Z&4?pQOL;Lhe=;;gi!aNN9!dBR|%)3v-_fhtV>p`o> z3`~eV#tnRk53oB~a!7ac$0Gjm5`V1lZvI$3yZK}J^Cqi?3h}uVKXe!UihoEKHl${F z0GGki(tf+d{1N{gj2F5V+43G>kiEw{@&VRboM7yON-lqURCHmYk^#}=Hx7LF_8QBR4(~}Zi;lA zx>xWec^4Lu2aEeo4(2*I^)!0&H_6#>f;kFD4m#PF>(F`0PjEU89}RxbkP}LLDg89R zl>9;Zepi$ssnGmW&Ct?E$+RO$vL(mQggjUKgj+5I+FSD z1hvLHJ6$Gabt-xf{^NO*J83+0$7rg!T8z0XISbuNCOQ_|L#M{>pDXd^?N#t9shP<9 z@W7j`zB;LW9Vd|c8qgQisbK;2QY~>(HxV0B@k;WzcjTyzxoTK(9W-8Kyywv$th$+; zx?{t&4(4lBPxe_xnD&cpS=vfu8ePQJP%BuM`>$0E zE#@Hd2Y>R^mgPwj-(=D{Hjy8az3t4ns1G=1=|_IozS`%77!~<4t7!AtJB8IzekT=(751k=2Xp zsE5rn@~rgftcQ=Fw&Y;G#o26fP!nbCeH>4!p`Pd(tLHc9IsSsJ$Rhv4kJO{<5P$V5 zgEGHQ&$Vlhz^{A^Ew^At5q|a5mK?1m5*qG7PG`+@90$%jfov|d)(@@{JQu0cX1UgG z49z*U#@cu)Ro5-q&rC!Y3T{hhUOF-(uJt zxM-N}Co6f0n&EjPc_ykP(yMJ2ON_mWL%}Dy%til-F9*L|7dfX2J?3m|B(1=L@XON~hvYJBL*DV{*$?hl(hy?( zzt6LuWcB9ZXvMBj@I}|w>Bz<^I@eNFg%?uo*E4pNypRTTuaY}OWM1Lzs=E@X!2(CmrkK6c5&gRT6u!00XwnrxHnYAPhu!$l8>2=(^!YI>cxj3z`!1Y$ilA9&VYJfJIq`X^dGXD(YTD(pkAEF6 z!o&x>s{}Z*{KcEwpnVoNX3Hbm9BS)`PsYQlxy4lutQetk@wI{|e*J_h%^6Hw;^8%V`Y{JC@; z%N24jm8gA+_X9td;U2Dsa($=IzAN#af_px}4(ZL^9936CoX)FJyEpzG-1F}L1Gwk7 z5BGcue!dRe6KuR#WOyc}K-Yb57ZSbK7c;{UGoZ63^ z&*dCj;@S9ceoBQBPhr@4H zVYgn+@y7CAou}Up*G^$qxDMWP ztix^^#@yzuf)|087d=GJNki;W_-|&-iM!^0>@4GX$Lk!w%kdVD@8p?3@_SNR3AQ5c zmApQ~h%=NpB$pw=?%GLR2($IYx0Rq4ja)r*$dhxDala35da79QyidHzNqADpf2@(m zNaNnch7j)U*SUgwUx8*GG4^yDuFpB2s)pFDDb#wyR#cmH&34HRILU0?vL2s3lN#Jo zJ9w043JtffhJNi^T(ZWMiyaXBww&|Y@;=y@Lh7&|Z`ndDzSxn(HtBi{m?E|-6@J*Y zDO7X8D-FAFZq_txzOHn|Gg$$T=3oN~w!XLy9$0*C^=lLP<(}Wb-?}0d&pre9C37Ef zlCCTT-(2ddQEP4v<7wT{L$iQG_u*&m8fMh`EmJC5h(|NyPv9D6(9SJ_SLRwO_h9$Y z(4(wQMK`$>9#ZIq_$Ir--#hV1X#OEKLh$;=h?cb*$pa*#!#`ZRao9+0 z9@lmPtM%y8g@3jHfgh`k+e>}9`| zZ}scbn-|@v*RYWqR-#Y85njgt%o1O!%hsZs`ysxW!k?S*Xz^` zFs_kr8S}Nc@ z#kROo?P))RFMT3BR+;E9$uXHYL6JB%J>FDcaVs#BPHf0dbfd!83tav{#?u!W1s?ZI zmW=6A9c+F#9juI3^52SHR&wGNbko6>qJv#V?K=E_Dhp^Y+sGH>#YCE#ostIVC$TzB_ktjJmOcjYd~Ys+0QTeU8H zAlSNa_`>-M5*Owz7_e~mfsr+N9XTNPUviCMeliv&(y!`ggr`Wso#_652m$K)}r*$4e9?v2EH&2bV z=kg8YJkaxryTNCo1O8;oWNj{Z{-DIhv$w@hNAzMHxzwcOSomJig@u`7?1_cfHA&ua z@l!oV?0PZ38OBg|X(;h{zP&1QA^Y?b)=c0maIfCaUVS}44|YF$_4NQf*h=18rS|yV zd$mgm*ZDHhgH`$V>L=jz1jfE)#1op--l_?(d6lzg|J61x|2|#6PIbbJ_5m^Y{pH{~eufqa|k}IQb25vVBNIoc&HO9v0ZJkGD9a53wsq9~~BS z`^n#ZuODPAH3FkNJh?QPn6+6%OWv;x{~v{AGqnuYd7nX&Q+Z69q5Z8_~%wE48}CM z@VT1rf0c8MakPIX_Ypz_@a~&QuS2eq9Id+}%>OJ9`=T~D> zYv(*WxU_MOkz9Aw4w^L3rezZgV1=jHh(7cHbJKu6v;lqSPBqM3L#-Fd?Po&9G=Z;F zV5loA>^tj6W7Zp^r!cqsh{?~mTiKjRy_gT+|IZG@--@vmo2`w*h@p3(XT)wcJvF=~ z_BwQ?D^#;31D7m-!sUT$Kj+b4f0vDEw>$hI-nFL0=l`c!}Bojqp^jh)>rs#goZ1 zMT_$eXD`5iYmI{cR&v^A_AfQXFZyu3py;;2I{J}%<3;fF?f0UKU9j3NI$1k8eZ}wU zfH9_}PPMd5qZUq2`gIQjj*aLw)%cba{OhB^>Am0+)2O?W&HwFFL$xCIyW}A)@?F~t zA6|$q)mcxDD)4|4zIqZgD|DAy(O$*)z$NqWn_NLn3yB+bjSbSw_)nU|Kg)4Z$GaC; zbsk~wAvcLn;9g+VIoPOu_8NK3^kakO;CQ)ytZL!|S<61Eg>SEwXa9=cm@zxkRZrn! z>x*;3tS?HQ4Cj~d>(A;kLkqC)sg?fW`D>-05XF;ngIZCG{`P2J)ia52b-vHF-Hh!| z)Gamk>`bAKqw}!hY5gm4e=YPwOqt|a`?QzpIUcEcK1ra#gM4FAv3IGS)1y@U^;FN7 zajNIsajy?qM;16$^y={5Rmin@)cln=$mP_VleO+ZrU={ICr)rRgal?EZ5tL$H}_251ydq(1&Xn_6hyBk0vpCa;<&vEt>aQZ_Sl}y@qSz z%h9n&=0J(=cx$TU5vVv@AoQY-MRJSom-=0++km3*Mrbe<^Z8=zIS zNqypE4qZ9m!N>TY?{RJOZvX&gj&i=NS#2%HLO|z-+qBw&Uvc|X z6+9{ShUuD>aih!qKypYgW~j=17w?JfxnixvSXi~$)tjkQ0>c|CK`0?)9K z?-xGtR0}p#_`e43IjBb4MGkF!%5N`-ya z`qt^bQp7N112+O+g8a^CfO3uQ;t~QI=BCCsL^pTZ8E9(~$dfv0Xkhewu5*A=<5W9oS zofG`7KwGN=><*<@uset!UTc@)k@y5ZeEpj55I|)ZNM}<7$*6 z*;-{ux|fw(%YG z`y!J`z44aLuC1>zZ`e{=UdO%|O#O{(X$7==bo}echc7iiYl!K18eM-r@@6A&A@eTt z_NB2;ntb~kz`&_d_h_e?UpsQ8&;;4*F4i&-XQ#m{+|oO<;o2WqFVwlXrFRzRwg!Jq zb(2lvX7IIA@G_CLlK1#+y>dO9b#j8YT7V1DD=r5f^cWuQcS1vk z^UP%61$}Od*w2epGcn#_En$XuT~|F9Kee(IOB>27N*W%Bu{Jztvo-u**Ih4WKC*52 zO9KOw(esFH6`rCclrc|&W~*}^Zju@eVhi^B`3cTWKrdiH_vHK%Usm8obWf5WNAz|A zKTh_937*9DGJfRLq6q4Jd0livIJFXbFWZ>P^KGn$-0OT6-i&cbzcP<9j>-5wB_WR| zVT;9IMeK39{!8|D4lprE>N(TLtMDdf;p^@2F7W8t`uwXEU4~Hi?g98}OJ7`T@AArv z1Gpw*4t!6dyoYaAsAsO{V&+`}n-_h4{dC7m$JR4nzW}EQ-y*z@@I12Dh5tD{%;q@* zFVqT8)CO;LG#%auKI~)ovQOaChLE3A;MoO#(>`pL=Eo1{R2h3OyW|AM&%@nJe0c}> zLGrxgW2xf<;kTa$#!kUA>{9J~#FFi@rs!43*Y=9SC#r_uB@L!#ysDL>d>YtxEk6$uB=w>Tqj%>hW6LTc=>)dgxXGoRTw(f#{ zwL{wk&ZUl$pN72&oQ4@L(y%GO4zj#XzmC>hJyXH=ty#BgZQ$AKsj=3RJfYYZ>}?w^ z&V8oR7ysJ6&@JNAp}(Lp!<{f zGLysvQp?4JuF4-9m<0a(deKatxt(Wc(8qN8nnsSFsTzIjIex@OQMfHz+hnGn1jVBw zBX%I?FK@JI!xx+4qLxI)%^^3R_;C~s@#frk1Ka1Ddt$b4#&#MyAtFxC!LcjNoiv&n zxiQF~*c;kLp}RvaPxYObajyA}ZSo)QP5n3cHgq-qdup&RN^O-DKfq6f@no1o-AP<~ zBT0M+L-5r_hDP6TgzIG^!T)bsQ#WC=a-%<5rJ5}4qv;#N--})4^I6PQO~u)pm&Z0> z*CqG)_S@C)oG$3xZRip4Uzy*Yfe(sid+Go>@OHj={3>*aqZVjl*G$FMv57n}(|9*A zLD!t=HBoyGz0g+tPEIiP2JRhBzJkHnH;B=Bu?)R)BDQG3v9kwz=}>`Ne16@a@Itx<+N9?k;+(HBgkJ)p5lA>j>(MIM00L1Xtm{cb(%R|-$R*RxU@HV ziKT05nQzhGz0UZZ#20AKvuxr<-iihs_}jnOS*(PgC| zzdMtOTO?1TGZOo=v2SO}zCm`UMe%%I9ISo%vRV74%A}ngjE)(6-xgx^{Jx5~iKo9T1VwT2O+HJSH%^4;GFUhD_|u@8IqY4Z2%=eauU^0NNV zu|Mu*TqjujIsW!V=S|=*@ZNM}M>p{N5cd_0$nOIELR<0#7hr6JmVT@xLbrh-> z&TYzsr*3{~fWQMW-EoLJn3l3z%(|` z9jW-%`fZBmvD;~~j==0H=!t21tfL<=_C+M?Z6r2d?HnclYa?q9FPEe3xqCZ0 z{J7@*Da32`sapr!{|Y$jhi{Pj?d<3|g(uPTK(Z$7jD6##CpF|kecx_;`w871HkUP$n9~Eml;{?f1>StL z*?gPiW;d}m;@@3L{)nn9<;*hHC6a!TH--(7`}sYIZ^0>O+-b(&rq?&ak1Ga0F5CaT z4`~!QE1$I${^CY>mgkL;EmG_9G;sMax~w;8D)ws0VIXn?K0Z3XE|nY)n{O2rSsgQK`l<`FogHd+*QsUxuYdU2c7 zEwtiSC_360S}biJIouY3i~IL6IKE*IyO>9LUh?#$5-%aS)y7aeQF7ep8ojx#!XJrs zJUchmaY6}i*{OPSv4_x??6VWV^XKSGkFzGFJxZuYzk6df_WO0%@9}BVVP5|0@paeM z6Vxj|fq&19_y=XR{iMESYPkV`0LYqT# zr*9{Rl1dIG19aF3Jr05{2ScBGK+{90+lFkjC*;srXnLXShm1?;XbYvfn)2SBe~}O?l$;|BKBpjnLIEY zpMu{x36GMdUo-3c#^hqr{a~l5_2xau0dB!Jhp6Gcb|id&W(c(ieGtFlKATsp-D~Kb zN{DW+LCI^3ikz+P?dynX>oW_x9dkvS; z+~xQhuORQUYbv!+h-+{yQaq;B*T#=gn*ZQT57Vf_e%P6-c)ogpS`Ts5T1&_F0o|8< zaUuu)1lr*|b&Do8kMpdT#OfuUguXIfGc?QseJx~torA-*v^llgi>~V&+I_(@Bs@3v7v87EKuR5Gh0%OAOjN$k( z=2?yhhiI+Ovc@Io`W8OkRGOF)UkmOpC0F>y|Ib+YcUlm~&T?Qx{x>DYSN9s*AbBK& zP8fy8l|(z71-jkHB6QEm-cdB`c?F(SmzPEwR{S zf82H7n*BwK$v3uGf6q$Zy{^}y26ZB}IMIC^1%F$El?3xZLqcBhxTbr^14w=%-7iRV zH0Fgj>a}RaX7$yE>G(!sf71K!Z+9hMqsiW=LYH2HZuz&sn2aqI{?T2Z4XRZct)u4Xv|EZ?5H5CI4&_LY+kkTrEzjip%!YU@46ZkJ+J1%_5SybyKud~@A_fx54-P& zdY{qsd3f9i{hkkfeOA(^+!KA@5Z`rgpZ@Fp?;CvKy7Vd659xg->3#O1&qL$l^?MHc z`ds6?C;GmAzU$sT{nz{7cg=o|VR_`x`-Q7pBDo}oT*5OVWsYS30NM=vs$gOH)OaK4rEZJbX)p8kmQKXLwN z&L2d!_Hcfl^Dj9s#J7`u!P=`0iKzqQUo-w`oI?Ftg?h9KHEI=V)GE}lRj6UBP}^3a zuB}47T7^2c3UzE1>e?zPV`(?Vo2g}MGBp38AGIRyVK0`!Beny3PIwC!drtgSl0NR5 zIR@G;a?8ui$yxB@5%Qvb0gZBh2rdP0iT${JnALMT_U9Y1Srk6?_;%BWU8BVAbl@=j zCG@0y@NHV^oI2rUTWZNUpJ$O;l5urMALLp3g^wWT7yjkYj0$uHJr)})J7^!#nrU^k zO4?FdA#Db29PL_K6is-BuO5zp4~T^yh=VWa4}b6jc#3#x!i7}SAg_y_Kx9496YRb2 zQoh2gC%BJi{*7mUOdt2s*FEqQcT-QyTfb3iT$=8NCxOmD&*n=mL;czvTzk-aO>zOt zHFHnq51Lo(I}Z=X_RhH*`l889`0k(hs*9cAU{7GoXUdCPvk7EIk{wDn~ zy>_YKeeqM3KE-aS_s8?-Fx+{}ljP^CHO>HrLXpk!C%6}yA^jlZO25BJAEC|ST>Q2r zFQ7h;&^?9PgHDIBvH_n#$pI@dqg(G?py}@kd0dnCNG;S7;I$eVpq3geqZ6rn3J)ZH zGg1>(c&DxRj@F7;PvOVIrT^Kn)JrA*G4Jr!M(tg9(DxqU%PYwR`;nnH{CK!t!;E-Z zUB{TM!_D2DCZ6*@Z$fqudeF|EP(;Rnx6TG$gttDy{uBO8L-whuX9=2gC3^bFz` zB>E0nt3voCnb%&#Y{t@LuFr%h9@j9%vkjSF=H)YAjofqTw`fkcO_SPKXF{x=$Iy>< zvabanIN=fWdbjEDL(EGed4NTClR~~1XO{2@{AK`a`uax2!5cJo9x+0@sHw|ZoQ5AW zvCkzZnB)gO7n!YzPRGp}pMQT6@3h#(r$urRi@u|Cs_(t1bzq<$>dijM9<|NdmJ&Z;qGSmi@K%J zj^*Tq5IuL1;2nHv)03lt^Se8RANKDT(H~Kl27Qaz`YhxM_R5Z;-w~U78otgJ>^K|Q zA6ER>GsFFLyu9^S@_|{=L%E?NCHOB}*;7Ttv(1%yzh?1)rgju@E{VXo5&-9t|L*BU z-yX9h@kvh6^G02Yzg76iZ}VSGi2{xV|FZW|!@Rgz@OK*ig1KBz=lIwVo7Mpi9zt&2 zq2UMClDDwspe@|-2J7+x=Mn!N?vPsR2RWA9M+1-trXkA@hnKdp_wqT;Mfbf9dyVJ> zWWS6j7uajd!^xK);rJ9?=YyPowldstTnV;Fos#2H%kWl**b5+ zMcG>R%{#gtlp0ZtQ*;Ky4N>-7_J`yI*v-7jyF}+T9>2wF@g+MBZ!Z1^*r9ng$}Txn ztGK@v{|3RM(G!9kKW8j|d@w# zR;Z7c7lLjra@odTL6-zprzT|^A&2cASmGNO|R0_Q$Wta*>X zG0&1~`C_ii$htgqowLz(DxK9+l4zN<0$M4$&t>R7u_Ng=Y_UN_eBHI}BcAu?5G|a4 z=^W+FBNszJ?$8M4=m_&t{}470cpaIe9FDuskzZCl$UF^Wo~~z}{BtBaLpAD0@dolJ z8qbDI4LWO_dgXbOe4jFJ;mlivTPf;3Z{e5C+ivDm_DlD9ThF}JCML!Yx|JB|_XfrP z%r}2F=1+9R7w0dk?g3&@A0!5K^WN$yf1rIvi{4*7Wxca{$~&~|G`rp>3=k&%QETTDb0q%liDw=ls75mr8E;`Td@7N#61A2$%DK%Xz@%Jm7NPcY#ai zk1w60>btKDm+reR!lj+#?+=$l;|<>t9!u}&K3|u^|USfQ?4;BSy ze$ovV3!?D9>9>1h0WkP9Z9VNBT2}w$DYx_=IAuIAH~|@7 zOUKljedRHwbKO6t7V^eiZA>Qcij2!o?*iYgOj2eBj#qqdb^3#?MaEmo*sSu;EhEOe;dzS4==b= zjdHhfuIF9nw@T$1ey0HU!-y*(hFjOkNAliNYQha&#N?D>pO0*|rk+A3#dZ)k7kSsN zMp=rn#m@`>Ahw0;#pF<*r$$)FM_v3LIfqO5-G3wTnCu-pzeCwGQ`s+d>=&`U4;h1P z;%19OS$$)?x_V%IBr(aSeEAaTm%487q44PR<1W2HnRy!dz?)AzRIiybo486F`3aN8 zSzkQ5(3>x8zyzf!3LCKGgA$(3NW7@zWXuDOQ-R|=a`xu{+q12S@sh9Kzdp_zF0N16 z^_Qm$HujPwm!C@m#N2`42+9}&;AJNXyD&N__`3BB6aK2$m)ZcHMH~jA-=gs*059dk#_DB9c zYTk=dD?)f)WGX#>`}wYIB3t>_#2I_(nl$&h@|swyd;jmRiPT8HRBpJO-#B^| zenZwIiZzL1O`=$nDAvTk9!adnv8${{6l*Y?HiPyUEs8Z5PMbk{j26Wj45!VYJw}US z4TjTZ(DeD|*FXRH-S}TW9x_D+;GwW9;2~lhy3+|8*ZwIU!mp(}ov0zdQg=F`axD-K zwc)3!(+PfcI>B!s9%{ocCJ+x{d-^ZYiRaKK2p$r@;H%=H(0?5s`g+j>Lz*>l2 z@(g+pgi^OWT17yGXs?clJ}>Ey#wyYe=Qt2wWmEnG3!2UqI;B_ z021?`fKRvhw7t(BGtj5#bdbGUg2`#}6Lbde(mo4`YLPrnUY$^w{WyNdt>|tu(GQ8< zphTfg9UYAsrM zzoRhP(R4?&!(ohTDe>uZ;^|NHIbK~(Ox-u=Z1T|O%toJ6iauu<`kcR)d-XZ;Ujq7^ zR`6&XajJhqpR+i)zwV>*qaN}0=E2qlrGu3P{yJ&@$@i{d?XOmQ$u9#;?n%5ewUErL zIri4Hc4E@a)Kqp+&m@s`6MgzH{yR;aa_Ux_mR<|*2hS$?d=jV`UI@;Jf>+!OuQ+^N zLdB`^QQq3&@O^siaQI279gg3BbPBv(0=%69ZnzE~lI4SJ+H>%qt5U6vmhDzOKjbF( zxl{0XC&LfMz7NmlMn~i2@xqG_kQcH<4KJS5XglwRvwHBs@VmVJ0J4wK4Xsgtwd;vP zjwFt_jpq-VRr@J)iIV?kIQk>02PpB{0uKe~8$Sllf;pZ*POo>c`5ecGQe>R1_{&wI zZ6`^kNkt2qI?)+>=yzSw!lnV#zFk)|rpbUDW8fHg-XVS) z{qP~8Zi0IkdqQL~skgTceYn_01TO>2)LT~UJAq#t>zm2>)4*LWaA!l$D}0T>GT-jb zM|Xd<+E~7I_~7C?P@|o77|t4WuhE{${uBRNjruM728B5W(0?2CWUkcrpI$OX_Xd_a zbDO|lDtOF*K9Cypd+@sgckDGFZv-nNrDn<>(cMX&tDStC3U1y@E~ynXvGtS%k<02D zd~j)^*H<~;kM7S-6MvvG_P+&KD+W$VfD^rrH)|;|_JW(-(AUq{ce>pMo_;xNxdQ!P zQc_>XUXJ&1Jb+`V0i9>a_(d&K{3wLzEi zD0z%?uTOWmkFiT002yCBJeiD7=0V0d8W{iMHP*(MZyBrU1@r^hhmyOEJA-kHF5yb+ zA#wSq8G9g}w&5Ql*JWQ1vX1W@Xr0hWKBUxE#vP_v~;0rKg z8m^4o>#NI`gbdWe`t1XLO3dJKWSzYO@RbF2a)8qW;6vrwd)MN33H%fS7gg9W^O1K( zG0z?J;{*=F!9{P7qu?NNYs8pv$6jcV+-F0+IYIwU`VYhJ=%Bf@ai#g-+MURH`N%dW zc~=MTT7~?2gnhOv9ltQ%b%J-jfgf8JvQZM(h63A1pmS|$9EW+2%j6jPC)bnY82l*5 zyO5DGcy|rIx$+)s?r$!3+ncc0AI>8N%$8W#kcktwy%PhjFqK^0mw8zicch0LcZjf#LtJl zxFZr-YPliCf^L0!X!u`ZCoSn07pjI@sQZpTd}S=Q?MD2(it7^57nPXfvcbhS(fZRQ zcDo%LXLuYwTaApcNQWLq?#SX6LBtTc~Oi(hExI?3DE% zY}QQpCpyO}9{)#=68}M#vVi8TZ}Q=*TFCOYc}*}K1||6#N3FkO^+Ez<`f@c zp|z#Vzv_?urLKbH302@rs*QK+IYF(J#!89nH4)GH4`}jU%kwYpJ-PEA&aaRELs1`m zMd??l*O{r#ifLN6Mc&&jFD5=up^RZCzO?Ju#xZv6sV;1|a{c3w z=9?AlhX!J~y!o#NPRaam;FJQIf-kB8U({6WA8p_kdFCwrIKe-K__Eg;%q^0WAe(iO zSUQPuE90B=+*jBXS$EyGv=d(y=(#QzOB~<1Wbu!VsZ)&Bud9te*ZqWk#CKO4`{F0` z(YUc3*WgnlK2plF%2(%jzJ~WZdCt{~91OhsE!Nn@yC)mX_R*40lQ|v2a|-%Q@VGaR zRlpo{9-)@hu*up$nS;LUU5>@~&NUW%j2tF1_zF*_)nmL&7!7kp2Utf;Yb?SY3(+uHN{QDr0eNYIj zo6IrxzQFocAFRv%m-prKzHM2+zYd3&#xH)t|AqX&^TJsD|Hszd$@TW+Ntyyr_?`JGJk3ADNkY3`d%{@xJZ%AOHfn2u(3TQ}97}Fch3-0k?&LZF&SE*^C5$fP0^)~0M{16-(^D! zOy>P-bzD6VKV)*HNN!H?-N^-BRIa7N@1}y|#gC^1-X%X-nOVns#5XyK0>Um<_Nf7<=Q6t*hnAG`{+r2k}~sJXxBmdd7tqL|8{z0lp_)vB|6bE z@aQ4>eV=}%)&>7J9yEWjHp775a)L7RAn*8qcl2CJ9UpRTs$AR6I}Y&EE;VXbwJ*ojThW@6~1#_XX~ z{IJKs7o@UhW!+LYFJCMEIvGZ@y9oV|#MmsrCwdMsMkeYJr?V#ui7|4e8#U)9Rl|3r zMe3l%u}{uJKW>74SR39PcZ=Bq{>C3Dk@>greckDiyt{xo zSY8t2SYeQuA(MTl!R%hfoVoP#e!d#TX1D0*1oun7I-X9KXUz8fX)%s=?zQ@H^yo`* zbSboZ`R(5C*~fRK%6Bo&%V@=iuTfuZJv8@qXzqq7m(dE}x0$dHQWLxH*Ndir$8H0k z-3ngI0l#I#t4)SioAeNPPuH*f?We}dcW7_Y*3zD)bxQuM}kH}p%Gy4skBX%Ca2gvTa%=qefqvL+?LarL*R?)i+ z1ST_yktqNtWSaJ@tx^nba{0_00Nz|55&F!zuCgG4{>d$YA)6`uZX3lWAPByeh7Qb zI+A)1;D@p#iz9`5mw{WpUbL(B2QM0<cTo^= zvTDUh^d=*Fc8)S(+xZMR>vMSc(X2%u>V~T?kfRWuGec3_dCd26WD|V&Z}i8|TG63S zW{>o-C?g*kVACx4q0C8>_!zTeuB_cCoA!pR-Na1oAm4J3xPvN@LD0|5N7k83e`2G1 zmHMOwqB%MH)3#sBh<;BR=zLwgxV$^f%tQevpXpjzxZD#&BV zclqZik@YR~%ZI>~iLr@2vkf>hC8IMU{s`SboH?CmmLM~Q?~fHZym?*rQ`NuDdCCnw z>MlRKl7Fx3TLlM*d{RscVjUP$h0u{w__+@FXt}l$pSmjci_}bBg`9E{d@t9XK59z)AgbQBmT^}aA=d*{4`)_DXow;gSMLXy>c{`ha3u&-D`m3 zF8r^*Pc0US+Y-JA{o9%Rsl=z=TiWG_%Hwl=jC5PE0V8l69@k|0XoJRbstG)ZyT+)X)@8~%4N$Ry=CWY^fttW4I z=n_j@`z*C_64&9`icRcc=P0$Y*ob@;b$z_rFFoFBON=i7W@P`(MP`i@nKd;vzVOSk z87b!4M^$n$39nrmlNcY%eP-^HXZmuC&*=69WYSPk(W z`c}oZ7F%~%8PD0(#!AU6xtx4blAFQ2`B!V(|5O!QV#J?rgCjNp+0sqywZwLbFF7%W zGrH1=qvsow`1YFeo=r*SbdHC{Cs_x@i@kBcqyh2uVD~H@!il@#H8ruGX(pdM) zU(R^0al@lFnGck(HG8HvB}&YYJ!MGgm;v$aw=J7dG|l_}@UJu1n3vergf2|DW>WBS z-4FOV@?O=G`&4L%JHqOD7};SHzPRGMD*osu^!X2cS#mg)Uu9Afb)9-Om2%ucJZ8u3 z)Zbz3VazkNEbBD<$lLgx%r&W%b_{(*=Yt{I*$u>-6%%)MDp)&Hpm?guZzz7e?OSfq zBwpENgg+%#Gn1A?J3;)I_|iJFY+3{K{v^0TZ1+*<-Nn!L6g-H;nu=^_miv(ZI+(vI z_IL-rcTV1Y0=@4^e08cn>C*kyItClayK5$wQCOWjJxFt>U!$FjR6Hlf_S8-!8@0CN zC~DzcZ9Ptr#PDQ?5gxtV5B+kxN;yW(k zxZHPK&hZ-G@fwbwmE#z@|cl@&Ac?Vz6^@<5U)PC-2{7}!K zr~dS1{J3+ho{yh|cU}0}cIOM=#~#X!bJ!zd!>CP4jBkaO;`iU#+Z=6gHmAl*4WH$& zSH_x_sJ#S!*Okv~N*XP{w>KsALzfMFB>`VacU>1>HYO>)#+($tjNc@Fl^{dH;TMPt z%bM9FF=19U0qil^b_vH0yZ0`{@>T{)!B|F+QO6>QazI>bJ z!iLh0y{4KxB>pw~_(|kp#sod8kQ~(bW)t5p`4TPY3i`OM1Bh8 zhmf&k?VUKxi@g#7Q z)bM?^Xu;ywiXLAq_5GfvtuI=%cs;*AVLje4UmHK6pC#cP=Kj;7l*I*RYWjvnBxG{F ztT<)yI_@!;2E;FeIgx9-xhKgqD84jENs#r@pcUw47E6qZ#3O8DPBM(K?l$Z-k{^4k zF>1TSol}D>ejnre?A{rgz@de;5Lr>yLFQ5BR*%tPE=6{fx!h2G$95$WS%$e}ezxm# z3EW8y2$w#e=HmVQFYwp^jL07EP4maMq%kidkJU1tGS?Q5P!6h~yE-^6HdT_#TpEp(}30xu99C2Pk#r=<$L>W1e- zRkI}rU3i+#4@MOi)=YgWar2$$C67QlJfXx?h4B9pV(*UQqq#_a(a)U+=D#{MUZ*3T z4B(#c-C&%yUF3S%N8SIIebqVE>goRe2kEc#!HLk$%l{LY$>smak5RXVb_4Bu@tj## z;5oCtAJ6H5=NyMTv>e*3^X=2nqrrEMwV*pNTHJQdh3_n$ri@CNC46G<&QY)Cq)HrG zExhK42d#~1!f(z}b_~(^&8VhS_|3#}>yFl3Wk*V{%rUiD(c4!XBR}$BaGOQ(-1JwQ zHu7+$mIB}D+z_p$B=ehtEkXW=?+kjHRBVt_mM0Hfc9|9p!3{QT#W9CznGzkvVT3IFMa#$CpLPP{t*xfTAi zfd2ga=UMC;zafrlFS&TS^Pycmd_3qZFAsVL&%Fsw5I(dJJ`~;)KGdP}q0rm;dtd)~ z>{;OM2;)i*;6--|4jO!s7j4T`cYG@^n&IO`pS=<fTHsNO;8E)Wc+_z8e>%_m?6pHA4l+V)PVj(3_;tYtieJjx zTW-{ZPwn7(1M%#-k86Y{GRBGjLfg_sKTELKk8x*Mdozryl50WX&AYiBI=rn4;G%^U2lX3 zjnafKb&ZJBoN@4?$r0L#NCmrHxOS3tY|mvcyld7{7>{$Xt&u&n{j1|S+S|wh=fE}o zdpbv08UM6SS_p4R`j@I{rF*yAM;&*jN@|eG4X-W4`M&z zErp-ZkKt`4SAl*^O;L&a*N>l7&|_1F5x&$p5gqn}!jHbOo&Uc-KWc;z)%npG--#a` zwo3TQ8Q;QJ#$AygP12v6_U+F>dvn<<2apwA;O{H(qvV|4exk3fag-%$l+b)+$?aw} z*5b-i8ynbjFZVT#68e1p-f5b-Us}BI0phRV++g)cyri?<8~aK*o*(<}E49dk%m1=L zmyw0X7v8fb>-Hw4A3Qz$!YbxL##tL0ZCCdl315cHs%6dGo}ns_8qtvoT@8NL`jrwk zAim~rNkhxA?XCM;^3crHrIDGY>Im}pMb0j|=_}#=A2Wm#FB#{~f&Y`(8{x<27-HE6 zee9B>`&R6IrMx5mXV$Nl(RbqC5{D+OUKW`|KZXx$B9sr4A`An=<_%DluTAjBlJiG$ zvx;B!7WylJAFg9=8n7J*FD^XsWSQ%GmBz0IWA}a)_ys;)@S`G+xg3h;i@{Sg*UO5J zSMD?9RKaap3~S(K&cxqF) zPs+Y4QDfbS*fQKNpaWt3oP$;N>a^{-s>#b2vxaiq-qxkpx{GUbe~*51Isbd{JBa(e@EyH{ z_uWy4>;UiE6v*?!?~*gs%kRSLHp$!zzbkwD_d0JIs`Ivfns6ykOI?wgTj6Q%xrC=h zR-P_&<`MWy^e;Ni5n3bssPkU*+}}cj@&cam)2O?+_V-8dVWquG`+Vwe(Xp%e3Mm!h zOZ6E#N$k0v&}yrmKPDBqSnL>ckUbLB0?O(=jEs;UkNEORehGJ!!_9%xwpUS!{!vC)vzgc|Uj*d_&%!*Qpy{?hkRr5_f ztL`Z@D2-n)%4BYB#00z=2{xh zCa9sscAgd7x}WD$d44j_E7(nxrpSumkCe(3o^=}GVUm@KzZ_I5+xQjRuH@c2#@zpb zxsdg6mNbuXDxIUe>v0R;mxWCzlboksKWOn8{`fic5Bhpt{=dJELZ$PEa_?V!_x^PS zJ`P4BKH5F7|Ak-&B)8=(GyR7$*B1ON4HZWjpV*jMk#Sm+)ry{~Qu$ek;@Q$y*($g$ zgzxTpM(O+$@UQ2;r2hnPRyA=Gl6Rv2!UdhnvFq%$V1o+jXYX}p$zh4p z+futS*JQxUrj%iaKego7Vuv>?U%ePTe8t+wdnj9%f3xIpVT`h^^dCzO*YMq@usHj3 zyxRqTo%S;Y9)}#w*m&Fn$g!rzw&dokXA00!7OP4NIVxL<*>ff2OxVMo&;B~=u;{LG zTuWYji0|kygtwT~@Rk$HltU5pd$3G7B>9|c$no?lIT3fMO2Zy=Mni+D9wLwRp;Jl9 zp<%$lIrweyJFNyT+Mmzu3|*C=k3;;*|HOCh1$UUjqV3u`MSBbUu^oJ=FxOWayA9fK z$^F#8kU9c+d~2QHS?1I~hksfp^J;RJle_IheLkaGMlzoo{9hXLSri4XQlnc!k$ZAg zvwL#cinZ;`DX{D|F{dMjFJF5*b6O~K8mVl{V@_|?=d?HV=1p}O`g}&aTd|XQ=QH}k ze1@SfmYQeZeLj2tH|I0j?Vr!kuwM4P*DBh6aOum;=babk^U`sPkMkw>y1a84&Ns4m z`*w91wJu~K|D3i1N0YIe_L?)NQ|xn(O&Mvg!Z*PN{B)HVv@YbOLhOpzzUCietrfL& zhC*NL`MO@rRqLy&CcYrDui6TfilgAHx!^SF;~jeZdFyuRYcDXN+ixO~^~m4Y+6z99 zeJe4;N=WI94vt&Pe>V>JxY;=j8e^irNPHej%f>mAIsZz0rgPZS+3fAf?D0wL^@-T} zCur<_9Y?lecl;B$M)vZ*6)BavuOZh2|Ax+3gYZ#u-paW0$dxDK z+9PA(8#aR@1RtvYd-%S6Toe5C+vVh)nze)R1mbh04o$R{z}U;c$wGUbpQ*ntyyK;R zOuD1(9~tDjn9ZCn?-6b9!@iU8zQMZ$XA7M*fg_e1$ZyE{?wze1a`EgOo;5Am=P-SJ z%VATn$sWyfWwXJvJhz;jDyDpBE4astGrct9?5sUHO_poV^X>A@Vq3=-)laImfI<@*=ga*bn!Jh8m`RzDSK0V z*E6x9@BBvLSn;eFVAbHS-B$d@ewfnvmVEakjDPPpS!=2die?IfZ{?ex<38w&yBRr5 za(%<69Li`@&Kv`0S(MNg!C&PZr-HxI!C!O1Un`i8+u%pc*tD|Cl!^%Qnw|nLS@A)X z95BuJ8t%r|5S*p^UbdqvXvIcVqK3Jv6VVAXFF8N6cBTWz@^0B{U97kNI}G5|?%#pN zdgh%2yx;MoZ~2Z=e8c`Xz9SERuq%DX0lq_Epp7-YT5eL-z2ZQj5}m>NC1Dd0STqOY z`=^kL5_psthkL*aeW4$M8~$U;O?GUn^OKOpelgFgElySrb(N@E7qqmL8a;W)%GSP} zZ^e9-wdS1c5&CN_mo<*DJHaD!jQv_tZN7D7FYKpgy!hQpKA%0D%i5;0wrQ;Gt*ouZ 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Ζ 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

    =X%2M^feFET=79pe5C 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�r=Y|0~Z5w!E-%43Wv#g+=VsDkLOK^(~ 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

    WJFifi2S4TJpk$d8X32w&O|9as+a-xGSQ<- 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 Date: Mon, 25 Mar 2024 15:52:41 +0800 Subject: [PATCH 3/5] [SYCL] fix SYCL backend build on windows is break by LOG() error (#6290) * fix LOG() error for SYCL, enhance erro check by CI * rollback to bash * add newline at end of file --- common/log.h | 4 ++-- examples/sycl/win-build-sycl.bat | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/common/log.h b/common/log.h index 48d21e43c..e4edcac7d 100644 --- a/common/log.h +++ b/common/log.h @@ -234,7 +234,7 @@ inline std::string log_filename_generator_impl(LogTriState multilog, const std:: // INTERNAL, DO NOT USE // USE LOG() INSTEAD // -#ifndef _MSC_VER +#if !defined(_MSC_VER) or defined(__INTEL_LLVM_COMPILER) #define LOG_IMPL(str, ...) \ do { \ if (LOG_TARGET != nullptr) \ @@ -257,7 +257,7 @@ inline std::string log_filename_generator_impl(LogTriState multilog, const std:: // INTERNAL, DO NOT USE // USE LOG_TEE() INSTEAD // -#ifndef _MSC_VER +#if !defined(_MSC_VER) or defined(__INTEL_LLVM_COMPILER) #define LOG_TEE_IMPL(str, ...) \ do { \ if (LOG_TARGET != nullptr) \ diff --git a/examples/sycl/win-build-sycl.bat b/examples/sycl/win-build-sycl.bat index f9d43f8ed..1b0dc41ba 100644 --- a/examples/sycl/win-build-sycl.bat +++ b/examples/sycl/win-build-sycl.bat @@ -3,9 +3,13 @@ :: Copyright (C) 2024 Intel Corporation :: SPDX-License-Identifier: MIT -mkdir -p build + +IF not exist build (mkdir build) cd build +if %errorlevel% neq 0 goto ERROR + @call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" intel64 --force +if %errorlevel% neq 0 goto ERROR :: for FP16 :: faster for long-prompt inference @@ -13,11 +17,18 @@ cd build :: for FP32 cmake -G "MinGW Makefiles" .. -DLLAMA_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icx -DCMAKE_BUILD_TYPE=Release - - +if %errorlevel% neq 0 goto ERROR :: build example/main only :: make main :: build all binary make -j +if %errorlevel% neq 0 goto ERROR + cd .. +exit /B 0 + +:ERROR +echo comomand error: %errorlevel% +exit /B %errorlevel% + From ad3a0505e3b6cd777259ee35e61d428357ffc565 Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Mon, 25 Mar 2024 09:42:17 +0100 Subject: [PATCH 4/5] Server: clean up OAI params parsing function (#6284) * server: clean up oai parsing function * fix response_format * fix empty response_format * minor fixes * add TODO for logprobs * update docs --- examples/server/README.md | 2 +- examples/server/server.cpp | 13 ++++-- examples/server/utils.hpp | 86 +++++++++++++++++++++++--------------- 3 files changed, 63 insertions(+), 38 deletions(-) diff --git a/examples/server/README.md b/examples/server/README.md index dfea2b905..49121a460 100644 --- a/examples/server/README.md +++ b/examples/server/README.md @@ -360,7 +360,7 @@ Notice that each `probs` is an array of length `n_probs`. - `default_generation_settings` - the default generation settings for the `/completion` endpoint, has the same fields as the `generation_settings` response object from the `/completion` endpoint. - `total_slots` - the total number of slots for process requests (defined by `--parallel` option) -- **POST** `/v1/chat/completions`: OpenAI-compatible Chat Completions API. Given a ChatML-formatted json description in `messages`, it returns the predicted completion. Both synchronous and streaming mode are supported, so scripted and interactive applications work fine. While no strong claims of compatibility with OpenAI API spec is being made, in our experience it suffices to support many apps. Only ChatML-tuned models, such as Dolphin, OpenOrca, OpenHermes, OpenChat-3.5, etc can be used with this endpoint. +- **POST** `/v1/chat/completions`: OpenAI-compatible Chat Completions API. Given a ChatML-formatted json description in `messages`, it returns the predicted completion. Both synchronous and streaming mode are supported, so scripted and interactive applications work fine. While no strong claims of compatibility with OpenAI API spec is being made, in our experience it suffices to support many apps. Only model with [supported chat template](https://github.com/ggerganov/llama.cpp/wiki/Templates-supported-by-llama_chat_apply_template) can be used optimally with this endpoint. By default, ChatML template will be used. *Options:* diff --git a/examples/server/server.cpp b/examples/server/server.cpp index b02c2546e..338e60f28 100644 --- a/examples/server/server.cpp +++ b/examples/server/server.cpp @@ -847,9 +847,16 @@ struct server_context { slot.sparams.penalize_nl = json_value(data, "penalize_nl", default_sparams.penalize_nl); slot.params.n_keep = json_value(data, "n_keep", slot.params.n_keep); slot.params.seed = json_value(data, "seed", default_params.seed); - if (data.contains("json_schema") && !data.contains("grammar")) { + slot.sparams.n_probs = json_value(data, "n_probs", default_sparams.n_probs); + slot.sparams.min_keep = json_value(data, "min_keep", default_sparams.min_keep); + + // process "json_schema" and "grammar" + if (data.contains("json_schema") && data.contains("grammar")) { + send_error(task, "Either \"json_schema\" or \"grammar\" can be specified, but not both", ERROR_TYPE_INVALID_REQUEST); + return false; + } else if (data.contains("json_schema") && !data.contains("grammar")) { try { - auto schema = json_value(data, "json_schema", json::object()); + auto schema = json_value(data, "json_schema", json::object()); slot.sparams.grammar = json_schema_to_grammar(schema); } catch (const std::exception & e) { send_error(task, std::string("\"json_schema\": ") + e.what(), ERROR_TYPE_INVALID_REQUEST); @@ -858,8 +865,6 @@ struct server_context { } else { slot.sparams.grammar = json_value(data, "grammar", default_sparams.grammar); } - slot.sparams.n_probs = json_value(data, "n_probs", default_sparams.n_probs); - slot.sparams.min_keep = json_value(data, "min_keep", default_sparams.min_keep); if (slot.params.cache_prompt && slot.ga_n != 1) { LOG_WARNING("cache_prompt is not supported with group-attention", {}); diff --git a/examples/server/utils.hpp b/examples/server/utils.hpp index 8f20ff614..7d9ab622b 100644 --- a/examples/server/utils.hpp +++ b/examples/server/utils.hpp @@ -352,51 +352,71 @@ static json oaicompat_completion_params_parse( // https://platform.openai.com/docs/api-reference/chat/create llama_sampling_params default_sparams; llama_params["model"] = json_value(body, "model", std::string("unknown")); - llama_params["prompt"] = format_chat(model, chat_template, body["messages"]); - llama_params["cache_prompt"] = json_value(body, "cache_prompt", false); - llama_params["temperature"] = json_value(body, "temperature", 0.0); - llama_params["top_k"] = json_value(body, "top_k", default_sparams.top_k); - llama_params["top_p"] = json_value(body, "top_p", 1.0); - llama_params["n_predict"] = json_value(body, "max_tokens", -1); - llama_params["logit_bias"] = json_value(body, "logit_bias", json::object()); llama_params["frequency_penalty"] = json_value(body, "frequency_penalty", 0.0); + llama_params["logit_bias"] = json_value(body, "logit_bias", json::object()); + llama_params["n_predict"] = json_value(body, "max_tokens", -1); llama_params["presence_penalty"] = json_value(body, "presence_penalty", 0.0); llama_params["seed"] = json_value(body, "seed", LLAMA_DEFAULT_SEED); llama_params["stream"] = json_value(body, "stream", false); - llama_params["mirostat"] = json_value(body, "mirostat", default_sparams.mirostat); - llama_params["mirostat_tau"] = json_value(body, "mirostat_tau", default_sparams.mirostat_tau); - llama_params["mirostat_eta"] = json_value(body, "mirostat_eta", default_sparams.mirostat_eta); - llama_params["penalize_nl"] = json_value(body, "penalize_nl", default_sparams.penalize_nl); - llama_params["typical_p"] = json_value(body, "typical_p", default_sparams.typical_p); - llama_params["repeat_last_n"] = json_value(body, "repeat_last_n", default_sparams.penalty_last_n); - llama_params["ignore_eos"] = json_value(body, "ignore_eos", false); - llama_params["tfs_z"] = json_value(body, "tfs_z", default_sparams.tfs_z); - llama_params["n_keep"] = json_value(body, "n_keep", 0); + llama_params["temperature"] = json_value(body, "temperature", 0.0); + llama_params["top_p"] = json_value(body, "top_p", 1.0); - if (body.contains("grammar")) { - llama_params["grammar"] = json_value(body, "grammar", json::object()); - } + // Apply chat template to the list of messages + llama_params["prompt"] = format_chat(model, chat_template, body["messages"]); - if (body.contains("response_format")) { - auto response_format = json_value(body, "response_format", json::object()); - if (response_format.contains("type")) { - if (response_format["type"] == "json_object") { - llama_params["json_schema"] = json_value(response_format, "schema", json::object()); - } else { - throw std::runtime_error("response_format type not supported: " + response_format["type"].dump()); - } - } - } - - // Handle 'stop' field + // Handle "stop" field if (body.contains("stop") && body["stop"].is_string()) { llama_params["stop"] = json::array({body["stop"].get()}); } else { llama_params["stop"] = json_value(body, "stop", json::array()); } + // Some chat templates don't use EOS token to stop generation + // We must add their end sequences to list of stop words + llama_params["stop"].push_back("<|im_end|>"); // chatml + llama_params["stop"].push_back(""); // gemma - // Ensure there is ChatML-specific end sequence among stop words - llama_params["stop"].push_back("<|im_end|>"); + // Handle "response_format" field + if (body.contains("response_format")) { + json response_format = json_value(body, "response_format", json::object()); + std::string response_type = json_value(response_format, "type", std::string()); + if (response_type == "json_object") { + llama_params["json_schema"] = json_value(response_format, "schema", json::object()); + } else if (!response_type.empty() && response_type != "text") { + throw std::runtime_error("response_format type must be one of \"text\" or \"json_object\", but got: " + response_type); + } + } + + // Handle "n" field + int n_choices = json_value(body, "n", 1); + if (n_choices != 1) { + throw std::runtime_error("Only one completion choice is allowed"); + } + + // Handle "logprobs" field + // TODO: The response format of this option is not yet OAI-compatible, but seems like no one really using it; We may need to fix it in the future + if (body.contains("logprobs")) { + llama_params["n_probs"] = json_value(body, "top_logprobs", 20); + } else if (body.contains("top_logprobs")) { + throw std::runtime_error("top_logprobs requires logprobs to be set to true"); + } + + // Params supported by OAI but unsupported by llama.cpp + static const std::vector unsupported_params { "tools", "tool_choice" }; + for (auto & param : unsupported_params) { + if (body.contains(param)) { + throw std::runtime_error("Unsupported param: " + param); + } + } + + // Copy remaining properties to llama_params + // This allows user to use llama.cpp-specific params like "mirostat", "tfs_z",... via OAI endpoint. + // See "launch_slot_with_task()" for a complete list of params supported by llama.cpp + for (const auto & item : body.items()) { + // Exception: if "n_predict" is present, we overwrite the value specified earlier by "max_tokens" + if (!llama_params.contains(item.key()) || item.key() == "n_predict") { + llama_params[item.key()] = item.value(); + } + } return llama_params; } From ae1f211ce2138448b47ebb148e25c58406845278 Mon Sep 17 00:00:00 2001 From: slaren Date: Mon, 25 Mar 2024 13:50:23 +0100 Subject: [PATCH 5/5] cuda : refactor into multiple files (#6269) --- .clang-tidy | 1 + CMakeLists.txt | 10 +- Makefile | 23 +- ggml-cuda.cu | 9095 +------------------------------------- ggml-cuda/acc.cu | 47 + ggml-cuda/acc.cuh | 5 + ggml-cuda/alibi.cu | 63 + ggml-cuda/alibi.cuh | 5 + ggml-cuda/arange.cu | 34 + ggml-cuda/arange.cuh | 5 + ggml-cuda/argsort.cu | 77 + ggml-cuda/argsort.cuh | 3 + ggml-cuda/binbcast.cu | 236 + ggml-cuda/binbcast.cuh | 6 + ggml-cuda/clamp.cu | 35 + ggml-cuda/clamp.cuh | 5 + ggml-cuda/common.cuh | 550 +++ ggml-cuda/concat.cu | 49 + ggml-cuda/concat.cuh | 5 + ggml-cuda/convert.cu | 783 ++++ ggml-cuda/convert.cuh | 13 + ggml-cuda/cpy.cu | 461 ++ ggml-cuda/cpy.cuh | 7 + ggml-cuda/dequantize.cuh | 103 + ggml-cuda/diagmask.cu | 40 + ggml-cuda/diagmask.cuh | 5 + ggml-cuda/dmmv.cu | 820 ++++ ggml-cuda/dmmv.cuh | 7 + ggml-cuda/getrows.cu | 178 + ggml-cuda/getrows.cuh | 5 + ggml-cuda/im2col.cu | 104 + ggml-cuda/im2col.cuh | 5 + ggml-cuda/mmq.cu | 2265 ++++++++++ ggml-cuda/mmq.cuh | 9 + ggml-cuda/mmvq.cu | 395 ++ ggml-cuda/mmvq.cuh | 7 + ggml-cuda/norm.cu | 215 + ggml-cuda/norm.cuh | 7 + ggml-cuda/pad.cu | 49 + ggml-cuda/pad.cuh | 5 + ggml-cuda/pool2d.cu | 94 + ggml-cuda/pool2d.cuh | 5 + ggml-cuda/quantize.cu | 45 + ggml-cuda/quantize.cuh | 5 + ggml-cuda/rope.cu | 308 ++ ggml-cuda/rope.cuh | 5 + ggml-cuda/scale.cu | 32 + ggml-cuda/scale.cuh | 5 + ggml-cuda/softmax.cu | 201 + ggml-cuda/softmax.cuh | 5 + ggml-cuda/sumrows.cu | 40 + ggml-cuda/sumrows.cuh | 3 + ggml-cuda/tsembd.cu | 47 + ggml-cuda/tsembd.cuh | 5 + ggml-cuda/unary.cu | 240 + ggml-cuda/unary.cuh | 27 + ggml-cuda/upscale.cu | 48 + ggml-cuda/upscale.cuh | 5 + ggml-cuda/vecdotq.cuh | 1284 ++++++ 59 files changed, 9154 insertions(+), 8987 deletions(-) create mode 100644 ggml-cuda/acc.cu create mode 100644 ggml-cuda/acc.cuh create mode 100644 ggml-cuda/alibi.cu create mode 100644 ggml-cuda/alibi.cuh create mode 100644 ggml-cuda/arange.cu create mode 100644 ggml-cuda/arange.cuh create mode 100644 ggml-cuda/argsort.cu create mode 100644 ggml-cuda/argsort.cuh create mode 100644 ggml-cuda/binbcast.cu create mode 100644 ggml-cuda/binbcast.cuh create mode 100644 ggml-cuda/clamp.cu create mode 100644 ggml-cuda/clamp.cuh create mode 100644 ggml-cuda/common.cuh create mode 100644 ggml-cuda/concat.cu create mode 100644 ggml-cuda/concat.cuh create mode 100644 ggml-cuda/convert.cu create mode 100644 ggml-cuda/convert.cuh create mode 100644 ggml-cuda/cpy.cu create mode 100644 ggml-cuda/cpy.cuh create mode 100644 ggml-cuda/dequantize.cuh create mode 100644 ggml-cuda/diagmask.cu create mode 100644 ggml-cuda/diagmask.cuh create mode 100644 ggml-cuda/dmmv.cu create mode 100644 ggml-cuda/dmmv.cuh create mode 100644 ggml-cuda/getrows.cu create mode 100644 ggml-cuda/getrows.cuh create mode 100644 ggml-cuda/im2col.cu create mode 100644 ggml-cuda/im2col.cuh create mode 100644 ggml-cuda/mmq.cu create mode 100644 ggml-cuda/mmq.cuh create mode 100644 ggml-cuda/mmvq.cu create mode 100644 ggml-cuda/mmvq.cuh create mode 100644 ggml-cuda/norm.cu create mode 100644 ggml-cuda/norm.cuh create mode 100644 ggml-cuda/pad.cu create mode 100644 ggml-cuda/pad.cuh create mode 100644 ggml-cuda/pool2d.cu create mode 100644 ggml-cuda/pool2d.cuh create mode 100644 ggml-cuda/quantize.cu create mode 100644 ggml-cuda/quantize.cuh create mode 100644 ggml-cuda/rope.cu create mode 100644 ggml-cuda/rope.cuh create mode 100644 ggml-cuda/scale.cu create mode 100644 ggml-cuda/scale.cuh create mode 100644 ggml-cuda/softmax.cu create mode 100644 ggml-cuda/softmax.cuh create mode 100644 ggml-cuda/sumrows.cu create mode 100644 ggml-cuda/sumrows.cuh create mode 100644 ggml-cuda/tsembd.cu create mode 100644 ggml-cuda/tsembd.cuh create mode 100644 ggml-cuda/unary.cu create mode 100644 ggml-cuda/unary.cuh create mode 100644 ggml-cuda/upscale.cu create mode 100644 ggml-cuda/upscale.cuh create mode 100644 ggml-cuda/vecdotq.cuh diff --git a/.clang-tidy b/.clang-tidy index 3078beacc..952c0cca8 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -12,6 +12,7 @@ Checks: > -readability-implicit-bool-conversion, -readability-magic-numbers, -readability-uppercase-literal-suffix, + -readability-simplify-boolean-expr, clang-analyzer-*, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, performance-*, diff --git a/CMakeLists.txt b/CMakeLists.txt index 3333ee1c9..b25cfd2fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -369,7 +369,9 @@ if (LLAMA_CUBLAS) enable_language(CUDA) set(GGML_HEADERS_CUDA ggml-cuda.h) - set(GGML_SOURCES_CUDA ggml-cuda.cu) + + file(GLOB GGML_SOURCES_CUDA "ggml-cuda/*.cu") + list(APPEND GGML_SOURCES_CUDA "ggml-cuda.cu") add_compile_definitions(GGML_USE_CUBLAS) if (LLAMA_CUDA_FORCE_DMMV) @@ -519,7 +521,9 @@ if (LLAMA_HIPBLAS) message(STATUS "HIP and hipBLAS found") set(GGML_HEADERS_ROCM ggml-cuda.h) - set(GGML_SOURCES_ROCM ggml-cuda.cu) + + file(GLOB GGML_SOURCES_ROCM "ggml-cuda/*.cu") + list(APPEND GGML_SOURCES_ROCM "ggml-cuda.cu") add_compile_definitions(GGML_USE_HIPBLAS GGML_USE_CUBLAS) @@ -543,7 +547,7 @@ if (LLAMA_HIPBLAS) add_compile_definitions(GGML_CUDA_MMV_Y=${LLAMA_CUDA_MMV_Y}) add_compile_definitions(K_QUANTS_PER_ITERATION=${LLAMA_CUDA_KQUANTS_ITER}) - set_source_files_properties(ggml-cuda.cu PROPERTIES LANGUAGE CXX) + set_source_files_properties(${GGML_SOURCES_ROCM} PROPERTIES LANGUAGE CXX) if (LLAMA_STATIC) message(FATAL_ERROR "Static linking not supported for HIP/ROCm") diff --git a/Makefile b/Makefile index 130fde838..08eafb1e7 100644 --- a/Makefile +++ b/Makefile @@ -398,6 +398,7 @@ ifdef LLAMA_CUBLAS MK_CPPFLAGS += -DGGML_USE_CUBLAS -I$(CUDA_PATH)/include -I$(CUDA_PATH)/targets/$(UNAME_M)-linux/include MK_LDFLAGS += -lcuda -lcublas -lculibos -lcudart -lcublasLt -lpthread -ldl -lrt -L$(CUDA_PATH)/lib64 -L/usr/lib64 -L$(CUDA_PATH)/targets/$(UNAME_M)-linux/lib -L/usr/lib/wsl/lib OBJS += ggml-cuda.o + OBJS += $(patsubst %.cu,%.o,$(wildcard ggml-cuda/*.cu)) MK_NVCCFLAGS += -use_fast_math ifdef LLAMA_FATAL_WARNINGS MK_NVCCFLAGS += -Werror all-warnings @@ -458,12 +459,23 @@ endif # LLAMA_CUDA_NO_PEER_COPY ifdef LLAMA_CUDA_CCBIN MK_NVCCFLAGS += -ccbin $(LLAMA_CUDA_CCBIN) endif -ggml-cuda.o: ggml-cuda.cu ggml-cuda.h ggml-common.h + ifdef JETSON_EOL_MODULE_DETECT +define NVCC_COMPILE $(NVCC) -I. -Icommon -D_XOPEN_SOURCE=600 -D_GNU_SOURCE -DNDEBUG -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/opt/cuda/include -I/usr/local/cuda/targets/aarch64-linux/include -std=c++11 -O3 $(NVCCFLAGS) $(CPPFLAGS) -Xcompiler "$(CUDA_CXXFLAGS)" -c $< -o $@ +endef # NVCC_COMPILE else +define NVCC_COMPILE $(NVCC) $(NVCCFLAGS) $(CPPFLAGS) -Xcompiler "$(CUDA_CXXFLAGS)" -c $< -o $@ +endef # NVCC_COMPILE endif # JETSON_EOL_MODULE_DETECT + +ggml-cuda/%.o: ggml-cuda/%.cu ggml-cuda/%.cuh ggml.h ggml-common.h ggml-cuda/common.cuh + $(NVCC_COMPILE) + +ggml-cuda.o: ggml-cuda.cu ggml-cuda.h ggml.h ggml-backend.h ggml-backend-impl.h ggml-common.h $(wildcard ggml-cuda/*.cuh) + $(NVCC_COMPILE) + endif # LLAMA_CUBLAS ifdef LLAMA_CLBLAST @@ -510,7 +522,6 @@ ggml-vulkan.o: ggml-vulkan.cpp ggml-vulkan.h endif # LLAMA_VULKAN ifdef LLAMA_HIPBLAS - ifeq ($(wildcard /opt/rocm),) ROCM_PATH ?= /usr GPU_TARGETS ?= $(shell $(shell which amdgpu-arch)) @@ -539,8 +550,13 @@ ifdef LLAMA_CUDA_NO_PEER_COPY HIPFLAGS += -DGGML_CUDA_NO_PEER_COPY endif # LLAMA_CUDA_NO_PEER_COPY OBJS += ggml-cuda.o -ggml-cuda.o: ggml-cuda.cu ggml-cuda.h + OBJS += $(patsubst %.cu,%.o,$(wildcard ggml-cuda/*.cu)) +ggml-cuda.o: ggml-cuda.cu ggml-cuda.h ggml.h ggml-backend.h ggml-backend-impl.h ggml-common.h $(wildcard ggml-cuda/*.cuh) $(HIPCC) $(CXXFLAGS) $(HIPFLAGS) -x hip -c -o $@ $< + +ggml-cuda/%.o: ggml-cuda/%.cu ggml-cuda/%.cuh ggml.h ggml-common.h ggml-cuda/common.cuh + $(HIPCC) $(CXXFLAGS) $(HIPFLAGS) -x hip -c -o $@ $< + endif # LLAMA_HIPBLAS ifdef LLAMA_METAL @@ -687,6 +703,7 @@ libllama.a: llama.o ggml.o $(OBJS) $(COMMON_DEPS) clean: rm -vrf *.o tests/*.o *.so *.a *.dll benchmark-matmult lookup-create lookup-merge lookup-stats common/build-info.cpp *.dot $(COV_TARGETS) $(BUILD_TARGETS) $(TEST_TARGETS) + rm -vrf ggml-cuda/*.o find examples pocs -type f -name "*.o" -delete # diff --git a/ggml-cuda.cu b/ggml-cuda.cu index adf930478..4f50c9f9f 100644 --- a/ggml-cuda.cu +++ b/ggml-cuda.cu @@ -2,18 +2,36 @@ #include "ggml.h" #include "ggml-backend-impl.h" -#if defined(GGML_USE_HIPBLAS) -#define GGML_COMMON_DECL_HIP -#define GGML_COMMON_IMPL_HIP -#else -#define GGML_COMMON_DECL_CUDA -#define GGML_COMMON_IMPL_CUDA -#endif -#include "ggml-common.h" +#include "ggml-cuda/common.cuh" +#include "ggml-cuda/acc.cuh" +#include "ggml-cuda/alibi.cuh" +#include "ggml-cuda/arange.cuh" +#include "ggml-cuda/argsort.cuh" +#include "ggml-cuda/binbcast.cuh" +#include "ggml-cuda/clamp.cuh" +#include "ggml-cuda/concat.cuh" +#include "ggml-cuda/convert.cuh" +#include "ggml-cuda/cpy.cuh" +#include "ggml-cuda/diagmask.cuh" +#include "ggml-cuda/dmmv.cuh" +#include "ggml-cuda/getrows.cuh" +#include "ggml-cuda/im2col.cuh" +#include "ggml-cuda/mmq.cuh" +#include "ggml-cuda/mmvq.cuh" +#include "ggml-cuda/norm.cuh" +#include "ggml-cuda/pad.cuh" +#include "ggml-cuda/pool2d.cuh" +#include "ggml-cuda/quantize.cuh" +#include "ggml-cuda/rope.cuh" +#include "ggml-cuda/scale.cuh" +#include "ggml-cuda/softmax.cuh" +#include "ggml-cuda/sumrows.cuh" +#include "ggml-cuda/tsembd.cuh" +#include "ggml-cuda/unary.cuh" +#include "ggml-cuda/upscale.cuh" #include #include -#include #include #include #include @@ -28,161 +46,10 @@ #include #include -// stringize macro for converting __CUDA_ARCH_LIST__ (list of integers) to string -#define STRINGIZE_IMPL(...) #__VA_ARGS__ -#define STRINGIZE(...) STRINGIZE_IMPL(__VA_ARGS__) - -#if defined(GGML_USE_HIPBLAS) -#include -#include -#include -#ifdef __HIP_PLATFORM_AMD__ -// for rocblas_initialize() -#include "rocblas/rocblas.h" -#endif // __HIP_PLATFORM_AMD__ -#define CUBLAS_COMPUTE_16F HIPBLAS_R_16F -#define CUBLAS_COMPUTE_32F HIPBLAS_R_32F -#define CUBLAS_COMPUTE_32F_FAST_16F HIPBLAS_R_32F -#define CUBLAS_GEMM_DEFAULT HIPBLAS_GEMM_DEFAULT -#define CUBLAS_GEMM_DEFAULT_TENSOR_OP HIPBLAS_GEMM_DEFAULT -#define CUBLAS_OP_N HIPBLAS_OP_N -#define CUBLAS_OP_T HIPBLAS_OP_T -#define CUBLAS_STATUS_SUCCESS HIPBLAS_STATUS_SUCCESS -#define CUBLAS_TF32_TENSOR_OP_MATH 0 -#define CUDA_R_16F HIPBLAS_R_16F -#define CUDA_R_32F HIPBLAS_R_32F -#define __shfl_xor_sync(mask, var, laneMask, width) __shfl_xor(var, laneMask, width) -#define cublasComputeType_t hipblasDatatype_t //deprecated, new hipblasComputeType_t not in 5.6 -#define cublasCreate hipblasCreate -#define cublasDestroy hipblasDestroy -#define cublasGemmEx hipblasGemmEx -#define cublasGemmBatchedEx hipblasGemmBatchedEx -#define cublasGemmStridedBatchedEx hipblasGemmStridedBatchedEx -#define cublasHandle_t hipblasHandle_t -#define cublasSetMathMode(handle, mode) CUBLAS_STATUS_SUCCESS -#define cublasSetStream hipblasSetStream -#define cublasSgemm hipblasSgemm -#define cublasStatus_t hipblasStatus_t -#define cudaDataType_t hipblasDatatype_t //deprecated, new hipblasDatatype not in 5.6 -#define cudaDeviceCanAccessPeer hipDeviceCanAccessPeer -#define cudaDeviceDisablePeerAccess hipDeviceDisablePeerAccess -#define cudaDeviceEnablePeerAccess hipDeviceEnablePeerAccess -#define cudaDeviceProp hipDeviceProp_t -#define cudaDeviceSynchronize hipDeviceSynchronize -#define cudaError_t hipError_t -#define cudaErrorPeerAccessAlreadyEnabled hipErrorPeerAccessAlreadyEnabled -#define cudaErrorPeerAccessNotEnabled hipErrorPeerAccessNotEnabled -#define cudaEventCreateWithFlags hipEventCreateWithFlags -#define cudaEventDisableTiming hipEventDisableTiming -#define cudaEventRecord hipEventRecord -#define cudaEventSynchronize hipEventSynchronize -#define cudaEvent_t hipEvent_t -#define cudaEventDestroy hipEventDestroy -#define cudaFree hipFree -#define cudaFreeHost hipHostFree -#define cudaGetDevice hipGetDevice -#define cudaGetDeviceCount hipGetDeviceCount -#define cudaGetDeviceProperties hipGetDeviceProperties -#define cudaGetErrorString hipGetErrorString -#define cudaGetLastError hipGetLastError -#define cudaHostRegister hipHostRegister -#define cudaHostRegisterPortable hipHostRegisterPortable -#define cudaHostRegisterReadOnly hipHostRegisterReadOnly -#define cudaHostUnregister hipHostUnregister -#define cudaLaunchHostFunc hipLaunchHostFunc -#ifdef GGML_HIP_UMA -#define cudaMalloc hipMallocManaged -#define cudaMallocHost(ptr, size) hipHostMalloc(ptr, size) -#else -#define cudaMalloc hipMalloc -#define cudaMallocHost(ptr, size) hipHostMalloc(ptr, size, hipHostMallocDefault) -#endif -#define cudaMemcpy hipMemcpy -#define cudaMemcpyAsync hipMemcpyAsync -#define cudaMemcpyPeerAsync hipMemcpyPeerAsync -#define cudaMemcpy2DAsync hipMemcpy2DAsync -#define cudaMemcpyDeviceToDevice hipMemcpyDeviceToDevice -#define cudaMemcpyDeviceToHost hipMemcpyDeviceToHost -#define cudaMemcpyHostToDevice hipMemcpyHostToDevice -#define cudaMemcpyKind hipMemcpyKind -#define cudaMemset hipMemset -#define cudaMemsetAsync hipMemsetAsync -#define cudaMemGetInfo hipMemGetInfo -#define cudaOccupancyMaxPotentialBlockSize hipOccupancyMaxPotentialBlockSize -#define cudaSetDevice hipSetDevice -#define cudaStreamCreateWithFlags hipStreamCreateWithFlags -#define cudaStreamDestroy hipStreamDestroy -#define cudaStreamFireAndForget hipStreamFireAndForget -#define cudaStreamNonBlocking hipStreamNonBlocking -#define cudaStreamPerThread hipStreamPerThread -#define cudaStreamSynchronize hipStreamSynchronize -#define cudaStreamWaitEvent(stream, event, flags) hipStreamWaitEvent(stream, event, flags) -#define cudaStream_t hipStream_t -#define cudaSuccess hipSuccess -#define __trap abort -#define CUBLAS_STATUS_SUCCESS HIPBLAS_STATUS_SUCCESS -#define CUBLAS_STATUS_NOT_INITIALIZED HIPBLAS_STATUS_NOT_INITIALIZED -#define CUBLAS_STATUS_ALLOC_FAILED HIPBLAS_STATUS_ALLOC_FAILED -#define CUBLAS_STATUS_INVALID_VALUE HIPBLAS_STATUS_INVALID_VALUE -#define CUBLAS_STATUS_ARCH_MISMATCH HIPBLAS_STATUS_ARCH_MISMATCH -#define CUBLAS_STATUS_MAPPING_ERROR HIPBLAS_STATUS_MAPPING_ERROR -#define CUBLAS_STATUS_EXECUTION_FAILED HIPBLAS_STATUS_EXECUTION_FAILED -#define CUBLAS_STATUS_INTERNAL_ERROR HIPBLAS_STATUS_INTERNAL_ERROR -#define CUBLAS_STATUS_NOT_SUPPORTED HIPBLAS_STATUS_NOT_SUPPORTED -#else -#include -#include -#include -#include - -#if CUDART_VERSION < 11020 -#define CU_DEVICE_ATTRIBUTE_VIRTUAL_MEMORY_MANAGEMENT_SUPPORTED CU_DEVICE_ATTRIBUTE_VIRTUAL_ADDRESS_MANAGEMENT_SUPPORTED -#define CUBLAS_TF32_TENSOR_OP_MATH CUBLAS_TENSOR_OP_MATH -#define CUBLAS_COMPUTE_16F CUDA_R_16F -#define CUBLAS_COMPUTE_32F CUDA_R_32F -#define cublasComputeType_t cudaDataType_t -#endif // CUDART_VERSION < 11020 - -#endif // defined(GGML_USE_HIPBLAS) - -#define CUDART_HMAX 11070 // CUDA 11.7, min. ver. for which __hmax and __hmax2 are known to work (may be higher than needed) - -#define CC_PASCAL 600 -#define MIN_CC_DP4A 610 // minimum compute capability for __dp4a, an intrinsic for byte-wise dot products -#define CC_VOLTA 700 -#define CC_OFFSET_AMD 1000000 -#define CC_RDNA1 (CC_OFFSET_AMD + 1010) -#define CC_RDNA2 (CC_OFFSET_AMD + 1030) -#define CC_RDNA3 (CC_OFFSET_AMD + 1100) - -// define this if you want to always fallback to MMQ kernels and not use cuBLAS for matrix multiplication -// on modern hardware, using cuBLAS is recommended as it utilizes F16 tensor cores which are very performant -// for large computational tasks. the drawback is that this requires some extra amount of VRAM: -// - 7B quantum model: +100-200 MB -// - 13B quantum model: +200-400 MB -// -//#define GGML_CUDA_FORCE_MMQ - -// TODO: improve this to be correct for more hardware -// for example, currently fails for GeForce GTX 1660 which is TURING arch (> VOLTA) but does not have tensor cores -#if !defined(GGML_CUDA_FORCE_MMQ) -#define CUDA_USE_TENSOR_CORES -#endif - -#define MMVQ_MAX_BATCH_SIZE 8 // max batch size to use MMVQ kernels -#define MMQ_MAX_BATCH_SIZE 32 // max batch size to use MMQ kernels when tensor cores are available - -#define MATRIX_ROW_PADDING 512 // last row of quant. matrices is a multiple of this to avoid out-of-bounds memory accesses - - -#if defined(_MSC_VER) -#pragma warning(disable: 4244 4267) // possible loss of data -#endif - static_assert(sizeof(half) == sizeof(ggml_fp16_t), "wrong fp16 size"); [[noreturn]] -static void ggml_cuda_error(const char * stmt, const char * func, const char * file, const int line, const char * msg) { +void ggml_cuda_error(const char * stmt, const char * func, const char * file, int line, const char * msg) { int id = -1; // in case cudaGetDevice fails cudaGetDevice(&id); @@ -193,65 +60,9 @@ static void ggml_cuda_error(const char * stmt, const char * func, const char * f GGML_ASSERT(!"CUDA error"); } -#define CUDA_CHECK_GEN(err, success, error_fn) \ - do { \ - auto err_ = (err); \ - if (err_ != (success)) { \ - ggml_cuda_error(#err, __func__, __FILE__, __LINE__, error_fn(err_)); \ - } \ - } while (0) - -#define CUDA_CHECK(err) CUDA_CHECK_GEN(err, cudaSuccess, cudaGetErrorString) - -#if CUDART_VERSION >= 12000 - static const char * cublas_get_error_str(const cublasStatus_t err) { - return cublasGetStatusString(err); - } -#else - static const char * cublas_get_error_str(const cublasStatus_t err) { - switch (err) { - case CUBLAS_STATUS_SUCCESS: return "CUBLAS_STATUS_SUCCESS"; - case CUBLAS_STATUS_NOT_INITIALIZED: return "CUBLAS_STATUS_NOT_INITIALIZED"; - case CUBLAS_STATUS_ALLOC_FAILED: return "CUBLAS_STATUS_ALLOC_FAILED"; - case CUBLAS_STATUS_INVALID_VALUE: return "CUBLAS_STATUS_INVALID_VALUE"; - case CUBLAS_STATUS_ARCH_MISMATCH: return "CUBLAS_STATUS_ARCH_MISMATCH"; - case CUBLAS_STATUS_MAPPING_ERROR: return "CUBLAS_STATUS_MAPPING_ERROR"; - case CUBLAS_STATUS_EXECUTION_FAILED: return "CUBLAS_STATUS_EXECUTION_FAILED"; - case CUBLAS_STATUS_INTERNAL_ERROR: return "CUBLAS_STATUS_INTERNAL_ERROR"; - case CUBLAS_STATUS_NOT_SUPPORTED: return "CUBLAS_STATUS_NOT_SUPPORTED"; - default: return "unknown error"; - } - } -#endif // CUDART_VERSION >= 12000 - -#define CUBLAS_CHECK(err) CUDA_CHECK_GEN(err, CUBLAS_STATUS_SUCCESS, cublas_get_error_str) - -#if !defined(GGML_USE_HIPBLAS) -static const char * cu_get_error_str(CUresult err) { - const char * err_str; - cuGetErrorString(err, &err_str); - return err_str; -} -#define CU_CHECK(err) CUDA_CHECK_GEN(err, CUDA_SUCCESS, cu_get_error_str) -#endif - -#if CUDART_VERSION >= 11100 -#define GGML_CUDA_ASSUME(x) __builtin_assume(x) -#else -#define GGML_CUDA_ASSUME(x) -#endif // CUDART_VERSION >= 11100 - - -#define GGML_CUDA_MAX_STREAMS 8 - -struct ggml_tensor_extra_gpu { - void * data_device[GGML_CUDA_MAX_DEVICES]; // 1 pointer for each device for split tensors - cudaEvent_t events[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS]; // events for synchronizing multiple GPUs -}; - // this is faster on Windows // probably because the Windows CUDA libraries forget to make this check before invoking the drivers -static void ggml_cuda_set_device(const int device) { +void ggml_cuda_set_device(int device) { int current_device; CUDA_CHECK(cudaGetDevice(¤t_device)); @@ -262,28 +73,12 @@ static void ggml_cuda_set_device(const int device) { CUDA_CHECK(cudaSetDevice(device)); } -static int ggml_cuda_get_device() { +int ggml_cuda_get_device() { int id; CUDA_CHECK(cudaGetDevice(&id)); return id; } -struct ggml_cuda_device_info { - int device_count; - - struct cuda_device_info { - int cc; // compute capability - size_t smpb; // max. shared memory per block - bool vmm; // virtual memory support - size_t vmm_granularity; // granularity of virtual memory - size_t total_vram; - }; - - cuda_device_info devices[GGML_CUDA_MAX_DEVICES] = {}; - - std::array default_tensor_split = {}; -}; - static ggml_cuda_device_info ggml_cuda_init() { #ifdef __HIP_PLATFORM_AMD__ // Workaround for a rocBLAS bug when using multiple graphics cards: @@ -357,7 +152,7 @@ static ggml_cuda_device_info ggml_cuda_init() { return info; } -static const ggml_cuda_device_info & get_cuda_global_info() { +const ggml_cuda_device_info & ggml_cuda_info() { static ggml_cuda_device_info info = ggml_cuda_init(); return info; } @@ -365,13 +160,6 @@ static const ggml_cuda_device_info & get_cuda_global_info() { // #define DEBUG_CUDA_MALLOC // buffer pool for cuda (legacy) -struct ggml_cuda_pool { - virtual ~ggml_cuda_pool() = default; - - virtual void * alloc(size_t size, size_t * actual_size) = 0; - virtual void free(void * ptr, size_t size) = 0; -}; - struct ggml_cuda_pool_leg : public ggml_cuda_pool { static const int MAX_BUFFERS = 256; @@ -481,7 +269,7 @@ struct ggml_cuda_pool_vmm : public ggml_cuda_pool { explicit ggml_cuda_pool_vmm(int device) : device(device), - granularity(get_cuda_global_info().devices[device].vmm_granularity) { + granularity(ggml_cuda_info().devices[device].vmm_granularity) { } ~ggml_cuda_pool_vmm() { @@ -535,7 +323,7 @@ struct ggml_cuda_pool_vmm : public ggml_cuda_pool { pool_size += reserve_size; //printf("cuda pool[%d]: size increased to %llu MB (reserved %llu MB)\n", - // id, (unsigned long long) (pool_size[id]/1024/1024), + // device, (unsigned long long) (pool_size/1024/1024), // (unsigned long long) (reserve_size/1024/1024)); } @@ -565,130 +353,14 @@ struct ggml_cuda_pool_vmm : public ggml_cuda_pool { }; #endif // !defined(GGML_USE_HIPBLAS) -template -struct ggml_cuda_pool_alloc { - ggml_cuda_pool * pool = nullptr; - T * ptr = nullptr; - size_t actual_size = 0; - - ggml_cuda_pool_alloc() = default; - - explicit ggml_cuda_pool_alloc(ggml_cuda_pool & pool) : pool(&pool) { - } - - ggml_cuda_pool_alloc(ggml_cuda_pool & pool, size_t size) : pool(&pool) { - alloc(size); - } - - ~ggml_cuda_pool_alloc() { - if (ptr != nullptr) { - pool->free(ptr, actual_size); - } - } - - // size is in number of elements - T * alloc(size_t size) { - GGML_ASSERT(pool != nullptr); - GGML_ASSERT(ptr == nullptr); - ptr = (T *) pool->alloc(size * sizeof(T), &this->actual_size); - return ptr; - } - - T * alloc(ggml_cuda_pool & pool, size_t size) { - this->pool = &pool; - return alloc(size); - } - - T * get() { - return ptr; - } - - ggml_cuda_pool_alloc(const ggml_cuda_pool_alloc &) = delete; - ggml_cuda_pool_alloc(ggml_cuda_pool_alloc &&) = delete; - ggml_cuda_pool_alloc& operator=(const ggml_cuda_pool_alloc &) = delete; - ggml_cuda_pool_alloc& operator=(ggml_cuda_pool_alloc &&) = delete; -}; - - -// backend interface - -struct ggml_backend_cuda_context { - int device; - std::string name; - cudaEvent_t copy_event = nullptr; - - cudaStream_t streams[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS] = { { nullptr } }; - cublasHandle_t cublas_handles[GGML_CUDA_MAX_DEVICES] = {nullptr}; - - explicit ggml_backend_cuda_context(int device) : - device(device), - name(GGML_CUDA_NAME + std::to_string(device)) { - } - - ~ggml_backend_cuda_context() { - if (copy_event != nullptr) { - CUDA_CHECK(cudaEventDestroy(copy_event)); - } - for (int i = 0; i < GGML_CUDA_MAX_DEVICES; ++i) { - for (int j = 0; j < GGML_CUDA_MAX_STREAMS; ++j) { - if (streams[i][j] != nullptr) { - CUDA_CHECK(cudaStreamDestroy(streams[i][j])); - } - } - if (cublas_handles[i] != nullptr) { - CUBLAS_CHECK(cublasDestroy(cublas_handles[i])); - } - } - } - - cudaStream_t stream(int device, int stream) { - if (streams[device][stream] == nullptr) { - ggml_cuda_set_device(device); - CUDA_CHECK(cudaStreamCreateWithFlags(&streams[device][stream], cudaStreamNonBlocking)); - } - return streams[device][stream]; - } - - cudaStream_t stream() { - return stream(device, 0); - } - - cublasHandle_t cublas_handle(int device) { - if (cublas_handles[device] == nullptr) { - ggml_cuda_set_device(device); - CUBLAS_CHECK(cublasCreate(&cublas_handles[device])); - CUBLAS_CHECK(cublasSetMathMode(cublas_handles[device], CUBLAS_TF32_TENSOR_OP_MATH)); - } - return cublas_handles[device]; - } - - cublasHandle_t cublas_handle() { - return cublas_handle(device); - } - - // pool - std::unique_ptr pools[GGML_CUDA_MAX_DEVICES]; - - static std::unique_ptr new_pool_for_device(int device) { +std::unique_ptr ggml_backend_cuda_context::new_pool_for_device(int device) { #if !defined(GGML_USE_HIPBLAS) - if (get_cuda_global_info().devices[device].vmm) { - return std::unique_ptr(new ggml_cuda_pool_vmm(device)); - } + if (ggml_cuda_info().devices[device].vmm) { + return std::unique_ptr(new ggml_cuda_pool_vmm(device)); + } #endif - return std::unique_ptr(new ggml_cuda_pool_leg(device)); - } - - ggml_cuda_pool & pool(int device) { - if (pools[device] == nullptr) { - pools[device] = new_pool_for_device(device); - } - return *pools[device]; - } - - ggml_cuda_pool & pool() { - return pool(device); - } -}; + return std::unique_ptr(new ggml_cuda_pool_leg(device)); +} // cuda buffer @@ -911,11 +583,11 @@ static int64_t get_row_rounding(ggml_type type, const std::array get_cuda_global_info().devices[id].cc) { - min_compute_capability = get_cuda_global_info().devices[id].cc; + if (min_compute_capability > ggml_cuda_info().devices[id].cc) { + min_compute_capability = ggml_cuda_info().devices[id].cc; } - if (max_compute_capability < get_cuda_global_info().devices[id].cc) { - max_compute_capability = get_cuda_global_info().devices[id].cc; + if (max_compute_capability < ggml_cuda_info().devices[id].cc) { + max_compute_capability = ggml_cuda_info().devices[id].cc; } } } @@ -1275,7 +947,7 @@ GGML_CALL ggml_backend_buffer_type_t ggml_backend_cuda_split_buffer_type(const f bool all_zero = tensor_split == nullptr || std::all_of(tensor_split, tensor_split + GGML_CUDA_MAX_DEVICES, [](float x) { return x == 0.0f; }); if (all_zero) { - tensor_split_arr = get_cuda_global_info().default_tensor_split; + tensor_split_arr = ggml_cuda_info().default_tensor_split; } else { float split_sum = 0.0f; for (int i = 0; i < ggml_backend_cuda_get_device_count(); ++i) { @@ -1374,5161 +1046,20 @@ GGML_CALL ggml_backend_buffer_type_t ggml_backend_cuda_host_buffer_type() { // return buffer->buft->iface.get_name == ggml_backend_cuda_host_buffer_type_name; //} - - /// kernels - -#if defined(GGML_USE_HIPBLAS) -#define __CUDA_ARCH__ 1300 - -#if defined(__gfx1100__) || defined(__gfx1101__) || defined(__gfx1102__) || defined(__gfx1103__) || \ - defined(__gfx1150__) || defined(__gfx1151__) -#define RDNA3 -#endif - -#if defined(__gfx1030__) || defined(__gfx1031__) || defined(__gfx1032__) || defined(__gfx1033__) || \ - defined(__gfx1034__) || defined(__gfx1035__) || defined(__gfx1036__) || defined(__gfx1037__) -#define RDNA2 -#endif - -#ifndef __has_builtin - #define __has_builtin(x) 0 -#endif - -typedef int8_t int8x4_t __attribute__((ext_vector_type(4))); -typedef uint8_t uint8x4_t __attribute__((ext_vector_type(4))); -static __device__ __forceinline__ int __vsubss4(const int a, const int b) { - const int8x4_t va = reinterpret_cast(a); - const int8x4_t vb = reinterpret_cast(b); -#if __has_builtin(__builtin_elementwise_sub_sat) - const int8x4_t c = __builtin_elementwise_sub_sat(va, vb); - return reinterpret_cast(c); -#else - int8x4_t c; - int16_t tmp; -#pragma unroll - for (int i = 0; i < 4; i++) { - tmp = va[i] - vb[i]; - if(tmp > std::numeric_limits::max()) tmp = std::numeric_limits::max(); - if(tmp < std::numeric_limits::min()) tmp = std::numeric_limits::min(); - c[i] = tmp; - } - return reinterpret_cast(c); -#endif // __has_builtin(__builtin_elementwise_sub_sat) -} - -static __device__ __forceinline__ int __vsub4(const int a, const int b) { - return __vsubss4(a, b); -} - -static __device__ __forceinline__ unsigned int __vcmpeq4(unsigned int a, unsigned int b) { - const uint8x4_t& va = reinterpret_cast(a); - const uint8x4_t& vb = reinterpret_cast(b); - unsigned int c; - uint8x4_t& vc = reinterpret_cast(c); -#pragma unroll - for (int i = 0; i < 4; ++i) { - vc[i] = va[i] == vb[i] ? 0xff : 0x00; - } - return c; -} - -static __device__ __forceinline__ int __dp4a(const int a, const int b, int c) { -#if defined(__gfx906__) || defined(__gfx908__) || defined(__gfx90a__) || defined(__gfx1030__) - c = __builtin_amdgcn_sdot4(a, b, c, false); -#elif defined(RDNA3) - c = __builtin_amdgcn_sudot4( true, a, true, b, c, false); -#elif defined(__gfx1010__) || defined(__gfx900__) - int tmp1; - int tmp2; - asm("\n \ - v_mul_i32_i24 %1, sext(%3), sext(%4) dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:BYTE_0 src1_sel:BYTE_0 \n \ - v_mul_i32_i24 %2, sext(%3), sext(%4) dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:BYTE_1 src1_sel:BYTE_1 \n \ - v_add3_u32 %0, %1, %2, %0 \n \ - v_mul_i32_i24 %1, sext(%3), sext(%4) dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:BYTE_2 src1_sel:BYTE_2 \n \ - v_mul_i32_i24 %2, sext(%3), sext(%4) dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:BYTE_3 src1_sel:BYTE_3 \n \ - v_add3_u32 %0, %1, %2, %0 \n \ - " - : "+v"(c), "=&v"(tmp1), "=&v"(tmp2) - : "v"(a), "v"(b) - ); -#else - const int8x4_t va = reinterpret_cast(a); - const int8x4_t vb = reinterpret_cast(b); - c += va[0] * vb[0] + va[1] * vb[1] + va[2] * vb[2] + va[3] * vb[3]; -#endif - return c; -} -#endif // defined(GGML_USE_HIPBLAS) - - -#ifdef GGML_CUDA_F16 -typedef half dfloat; // dequantize float -typedef half2 dfloat2; -#else -typedef float dfloat; // dequantize float -typedef float2 dfloat2; -#endif //GGML_CUDA_F16 - -static __device__ __forceinline__ int get_int_from_int8(const int8_t * x8, const int & i32) { - const uint16_t * x16 = (const uint16_t *) (x8 + sizeof(int) * i32); // assume at least 2 byte alignment - - int x32 = 0; - x32 |= x16[0] << 0; - x32 |= x16[1] << 16; - - return x32; -} - -static __device__ __forceinline__ int get_int_from_uint8(const uint8_t * x8, const int & i32) { - const uint16_t * x16 = (const uint16_t *) (x8 + sizeof(int) * i32); // assume at least 2 byte alignment - - int x32 = 0; - x32 |= x16[0] << 0; - x32 |= x16[1] << 16; - - return x32; -} - -static __device__ __forceinline__ int get_int_from_int8_aligned(const int8_t * x8, const int & i32) { - return *((const int *) (x8 + sizeof(int) * i32)); // assume at least 4 byte alignment -} - -static __device__ __forceinline__ int get_int_from_uint8_aligned(const uint8_t * x8, const int & i32) { - return *((const int *) (x8 + sizeof(int) * i32)); // assume at least 4 byte alignment -} - -template -using to_t_cuda_t = void (*)(const void * __restrict__ x, T * __restrict__ y, int k, cudaStream_t stream); -typedef to_t_cuda_t to_fp32_cuda_t; -typedef to_t_cuda_t to_fp16_cuda_t; - -typedef void (*dequantize_kernel_t)(const void * vx, const int ib, const int iqs, dfloat2 & v); -typedef void (*dot_kernel_k_t)(const void * __restrict__ vx, const int ib, const int iqs, const float * __restrict__ y, float & v); -typedef void (*cpy_kernel_t)(const char * cx, char * cdst); - -typedef void (*ggml_cuda_func_t)(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst); - typedef void (*ggml_cuda_op_mul_mat_t)( ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, const int64_t src1_padded_row_size, cudaStream_t stream); -typedef void (*ggml_cuda_op_flatten_t)( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream); - -typedef float (*vec_dot_q_cuda_t)(const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs); -typedef void (*allocate_tiles_cuda_t)(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc); -typedef void (*load_tiles_cuda_t)( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row); -typedef float (*vec_dot_q_mul_mat_cuda_t)( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ms, const int & i, const int & j, const int & k); - -#define WARP_SIZE 32 - -#define CUDA_GELU_BLOCK_SIZE 256 -#define CUDA_SILU_BLOCK_SIZE 256 -#define CUDA_TANH_BLOCK_SIZE 256 -#define CUDA_RELU_BLOCK_SIZE 256 -#define CUDA_HARDSIGMOID_BLOCK_SIZE 256 -#define CUDA_HARDSWISH_BLOCK_SIZE 256 -#define CUDA_SQR_BLOCK_SIZE 256 -#define CUDA_CPY_BLOCK_SIZE 32 -#define CUDA_SCALE_BLOCK_SIZE 256 -#define CUDA_CLAMP_BLOCK_SIZE 256 -#define CUDA_ROPE_BLOCK_SIZE 256 -#define CUDA_SOFT_MAX_BLOCK_SIZE 1024 -#define CUDA_ALIBI_BLOCK_SIZE 32 -#define CUDA_DIAG_MASK_INF_BLOCK_SIZE 32 -#define CUDA_QUANTIZE_BLOCK_SIZE 256 -#define CUDA_DEQUANTIZE_BLOCK_SIZE 256 -#define CUDA_GET_ROWS_BLOCK_SIZE 256 -#define CUDA_UPSCALE_BLOCK_SIZE 256 -#define CUDA_CONCAT_BLOCK_SIZE 256 -#define CUDA_PAD_BLOCK_SIZE 256 -#define CUDA_ARANGE_BLOCK_SIZE 256 -#define CUDA_TIMESTEP_EMBEDDING_BLOCK_SIZE 256 -#define CUDA_ACC_BLOCK_SIZE 256 -#define CUDA_IM2COL_BLOCK_SIZE 256 -#define CUDA_POOL2D_BLOCK_SIZE 256 - -#define CUDA_Q8_0_NE_ALIGN 2048 - -// dmmv = dequantize_mul_mat_vec -#ifndef GGML_CUDA_DMMV_X -#define GGML_CUDA_DMMV_X 32 -#endif -#ifndef GGML_CUDA_MMV_Y -#define GGML_CUDA_MMV_Y 1 -#endif - -#ifndef K_QUANTS_PER_ITERATION -#define K_QUANTS_PER_ITERATION 2 -#else -static_assert(K_QUANTS_PER_ITERATION == 1 || K_QUANTS_PER_ITERATION == 2, "K_QUANTS_PER_ITERATION must be 1 or 2"); -#endif - #ifndef GGML_CUDA_PEER_MAX_BATCH_SIZE #define GGML_CUDA_PEER_MAX_BATCH_SIZE 128 #endif // GGML_CUDA_PEER_MAX_BATCH_SIZE #define MUL_MAT_SRC1_COL_STRIDE 128 -[[noreturn]] -static __device__ void no_device_code( - const char * file_name, const int line, const char * function_name, const int arch, const char * arch_list) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) - printf("%s:%d: ERROR: HIP kernel %s has no device code compatible with HIP arch %d.\n", - file_name, line, function_name, arch); - GGML_UNUSED(arch_list); -#else - printf("%s:%d: ERROR: CUDA kernel %s has no device code compatible with CUDA arch %d. ggml-cuda.cu was compiled for: %s\n", - file_name, line, function_name, arch, arch_list); -#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) - __trap(); - - GGML_UNUSED(no_device_code); // suppress unused function warning -} - -#ifdef __CUDA_ARCH__ -#define NO_DEVICE_CODE no_device_code(__FILE__, __LINE__, __FUNCTION__, __CUDA_ARCH__, STRINGIZE(__CUDA_ARCH_LIST__)) -#else -//#define NO_DEVICE_CODE GGML_ASSERT(false && "NO_DEVICE_CODE not valid in host code.") -#define NO_DEVICE_CODE -#endif // __CUDA_ARCH__ - -static __device__ __forceinline__ float warp_reduce_sum(float x) { -#pragma unroll - for (int mask = 16; mask > 0; mask >>= 1) { - x += __shfl_xor_sync(0xffffffff, x, mask, 32); - } - return x; -} - -static __device__ __forceinline__ float2 warp_reduce_sum(float2 a) { -#pragma unroll - for (int mask = 16; mask > 0; mask >>= 1) { - a.x += __shfl_xor_sync(0xffffffff, a.x, mask, 32); - a.y += __shfl_xor_sync(0xffffffff, a.y, mask, 32); - } - return a; -} - -#ifdef GGML_CUDA_F16 -static __device__ __forceinline__ half2 warp_reduce_sum(half2 a) { -#if !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) && __CUDA_ARCH__ >= CC_PASCAL -#pragma unroll - for (int mask = 16; mask > 0; mask >>= 1) { - a = __hadd2(a, __shfl_xor_sync(0xffffffff, a, mask, 32)); - } - return a; -#else - GGML_UNUSED(a); - NO_DEVICE_CODE; -#endif // !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) && __CUDA_ARCH__ >= CC_PASCAL -} -#endif // GGML_CUDA_F16 - -static __device__ __forceinline__ float warp_reduce_max(float x) { -#pragma unroll - for (int mask = 16; mask > 0; mask >>= 1) { - x = fmaxf(x, __shfl_xor_sync(0xffffffff, x, mask, 32)); - } - return x; -} - -//static __device__ __forceinline__ half2 warp_reduce_max(half2 x) { -//#if !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) && __CUDA_ARCH__ >= CC_PASCAL && CUDART_VERSION >= CUDART_HMAX -//#pragma unroll -// for (int mask = 16; mask > 0; mask >>= 1) { -// x = __hmax2(x, __shfl_xor_sync(0xffffffff, x, mask, 32)); -// } -// return x; -//#else -// GGML_UNUSED(x); -// NO_DEVICE_CODE; -//#endif // !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) && __CUDA_ARCH__ >= CC_PASCAL && CUDART_VERSION >= CUDART_HMAX -//} - -static __device__ __forceinline__ float op_repeat(const float a, const float b) { - return b; - GGML_UNUSED(a); -} - -static __device__ __forceinline__ float op_add(const float a, const float b) { - return a + b; -} - -static __device__ __forceinline__ float op_mul(const float a, const float b) { - return a * b; -} - -static __device__ __forceinline__ float op_div(const float a, const float b) { - return a / b; -} - -template -static __global__ void k_bin_bcast(const src0_t * src0, const src1_t * src1, dst_t * dst, - int ne0, int ne1, int ne2, int ne3, - int ne10, int ne11, int ne12, int ne13, - /*int s0, */ int s1, int s2, int s3, - /*int s10,*/ int s11, int s12, int s13) { - const int i0s = blockDim.x*blockIdx.x + threadIdx.x; - const int i1 = (blockDim.y*blockIdx.y + threadIdx.y); - const int i2 = (blockDim.z*blockIdx.z + threadIdx.z) / ne3; - const int i3 = (blockDim.z*blockIdx.z + threadIdx.z) % ne3; - - if (i0s >= ne0 || i1 >= ne1 || i2 >= ne2 || i3 >= ne3) { - return; - } - - const int i11 = i1 % ne11; - const int i12 = i2 % ne12; - const int i13 = i3 % ne13; - - const size_t i_src0 = i3*s3 + i2*s2 + i1*s1; - const size_t i_src1 = i13*s13 + i12*s12 + i11*s11; - const size_t i_dst = i_src0; - - const src0_t * src0_row = src0 + i_src0; - const src1_t * src1_row = src1 + i_src1; - dst_t * dst_row = dst + i_dst; - - for (int i0 = i0s; i0 < ne0; i0 += blockDim.x*gridDim.x) { - const int i10 = i0 % ne10; - dst_row[i0] = (dst_t)bin_op(src0 ? (float)src0_row[i0] : 0.0f, (float)src1_row[i10]); - } -} - -template -static __global__ void k_bin_bcast_unravel(const src0_t * src0, const src1_t * src1, dst_t * dst, - int ne0, int ne1, int ne2, int ne3, - int ne10, int ne11, int ne12, int ne13, - /*int s0, */ int s1, int s2, int s3, - /*int s10,*/ int s11, int s12, int s13) { - - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - const int i3 = i/(ne2*ne1*ne0); - const int i2 = (i/(ne1*ne0)) % ne2; - const int i1 = (i/ne0) % ne1; - const int i0 = i % ne0; - - if (i0 >= ne0 || i1 >= ne1 || i2 >= ne2 || i3 >= ne3) { - return; - } - - const int i11 = i1 % ne11; - const int i12 = i2 % ne12; - const int i13 = i3 % ne13; - - const size_t i_src0 = i3*s3 + i2*s2 + i1*s1; - const size_t i_src1 = i13*s13 + i12*s12 + i11*s11; - const size_t i_dst = i_src0; - - const src0_t * src0_row = src0 + i_src0; - const src1_t * src1_row = src1 + i_src1; - dst_t * dst_row = dst + i_dst; - - const int i10 = i0 % ne10; - dst_row[i0] = (dst_t)bin_op(src0 ? (float)src0_row[i0] : 0.0f, (float)src1_row[i10]); -} - -static __global__ void acc_f32(const float * x, const float * y, float * dst, const int ne, - const int ne10, const int ne11, const int ne12, - const int nb1, const int nb2, int offset) { - const int i = blockDim.x * blockIdx.x + threadIdx.x; - if (i >= ne) { - return; - } - int src1_idx = i - offset; - int oz = src1_idx / nb2; - int oy = (src1_idx - (oz * nb2)) / nb1; - int ox = src1_idx % nb1; - if (src1_idx >= 0 && ox < ne10 && oy < ne11 && oz < ne12) { - dst[i] = x[i] + y[ox + oy * ne10 + oz * ne10 * ne11]; - } else { - dst[i] = x[i]; - } -} - -static __global__ void gelu_f32(const float * x, float * dst, const int k) { - const float GELU_COEF_A = 0.044715f; - const float SQRT_2_OVER_PI = 0.79788456080286535587989211986876f; - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - - float xi = x[i]; - dst[i] = 0.5f*xi*(1.0f + tanhf(SQRT_2_OVER_PI*xi*(1.0f + GELU_COEF_A*xi*xi))); -} - -static __global__ void silu_f32(const float * x, float * dst, const int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - dst[i] = x[i] / (1.0f + expf(-x[i])); -} - -static __global__ void gelu_quick_f32(const float * x, float * dst, int k) { - const float GELU_QUICK_COEF = -1.702f; - const int i = blockDim.x*blockIdx.x + threadIdx.x; - if (i >= k) { - return; - } - dst[i] = x[i] * (1.0f / (1.0f + expf(GELU_QUICK_COEF * x[i]))); -} - -static __global__ void tanh_f32(const float * x, float * dst, int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - if (i >= k) { - return; - } - dst[i] = tanhf(x[i]); -} - -static __global__ void relu_f32(const float * x, float * dst, const int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - dst[i] = fmaxf(x[i], 0); -} - -static __global__ void hardsigmoid_f32(const float * x, float * dst, const int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - dst[i] = fminf(1.0f, fmaxf(0.0f, (x[i] + 3.0f) / 6.0f)); -} - -static __global__ void hardswish_f32(const float * x, float * dst, const int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - dst[i] = x[i] * fminf(1.0f, fmaxf(0.0f, (x[i] + 3.0f) / 6.0f)); -} - -static __global__ void leaky_relu_f32(const float * x, float * dst, const int k, const float negative_slope) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - if (i >= k) { - return; - } - dst[i] = fmaxf(x[i], 0) + fminf(x[i], 0.0f) * negative_slope; -} - -static __global__ void sqr_f32(const float * x, float * dst, const int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - dst[i] = x[i] * x[i]; -} - -template -static __global__ void norm_f32(const float * x, float * dst, const int ncols, const float eps) { - const int row = blockIdx.x*blockDim.y + threadIdx.y; - const int tid = threadIdx.x; - - float2 mean_var = make_float2(0.f, 0.f); - - for (int col = tid; col < ncols; col += block_size) { - const float xi = x[row*ncols + col]; - mean_var.x += xi; - mean_var.y += xi * xi; - } - - // sum up partial sums - mean_var = warp_reduce_sum(mean_var); - if (block_size > WARP_SIZE) { - __shared__ float2 s_sum[32]; - int warp_id = threadIdx.x / WARP_SIZE; - int lane_id = threadIdx.x % WARP_SIZE; - if (lane_id == 0) { - s_sum[warp_id] = mean_var; - } - __syncthreads(); - mean_var = s_sum[lane_id]; - mean_var = warp_reduce_sum(mean_var); - } - - const float mean = mean_var.x / ncols; - const float var = mean_var.y / ncols - mean * mean; - const float inv_std = rsqrtf(var + eps); - - for (int col = tid; col < ncols; col += block_size) { - dst[row*ncols + col] = (x[row*ncols + col] - mean) * inv_std; - } -} - -static __global__ void concat_f32(const float * x,const float * y, float * dst, const int ne0, const int ne02) { - int nidx = threadIdx.x + blockIdx.x * blockDim.x; - if (nidx >= ne0) { - return; - } - // operation - int offset_dst = - nidx + - blockIdx.y * ne0 + - blockIdx.z * ne0 * gridDim.y; - if (blockIdx.z < ne02) { // src0 - int offset_src = - nidx + - blockIdx.y * ne0 + - blockIdx.z * ne0 * gridDim.y; - dst[offset_dst] = x[offset_src]; - } else { - int offset_src = - nidx + - blockIdx.y * ne0 + - (blockIdx.z - ne02) * ne0 * gridDim.y; - dst[offset_dst] = y[offset_src]; - } -} - -static __global__ void upscale_f32(const float * x, float * dst, const int ne00, const int ne00xne01, const int scale_factor) { - // blockIdx.z: idx of ne02*ne03 - // blockIdx.y: idx of ne01*scale_factor, aka ne1 - // blockIDx.x: idx of ne00*scale_factor / BLOCK_SIZE - // ne00xne01: ne00 * ne01 - int ne0 = ne00 * scale_factor; - int nidx = threadIdx.x + blockIdx.x * blockDim.x; - if (nidx >= ne0) { - return; - } - // operation - int i00 = nidx / scale_factor; - int i01 = blockIdx.y / scale_factor; - int offset_src = - i00 + - i01 * ne00 + - blockIdx.z * ne00xne01; - int offset_dst = - nidx + - blockIdx.y * ne0 + - blockIdx.z * ne0 * gridDim.y; - dst[offset_dst] = x[offset_src]; -} - -static __global__ void pad_f32(const float * x, float * dst, const int ne0, const int ne00, const int ne01, const int ne02, const int ne03) { - // blockIdx.z: idx of ne2*ne3, aka ne02*ne03 - // blockIdx.y: idx of ne1 - // blockIDx.x: idx of ne0 / BLOCK_SIZE - int nidx = threadIdx.x + blockIdx.x * blockDim.x; - if (nidx >= ne0) { - return; - } - - // operation - int offset_dst = - nidx + - blockIdx.y * ne0 + - blockIdx.z * ne0 * gridDim.y; - if (nidx < ne00 && blockIdx.y < ne01 && blockIdx.z < ne02*ne03) { - int offset_src = - nidx + - blockIdx.y * ne00 + - blockIdx.z * ne00 * ne01; - dst[offset_dst] = x[offset_src]; - } else { - dst[offset_dst] = 0.0f; - } -} - -static __global__ void arange_f32(float * dst, const int ne0, const float start, const float step) { - // blockIDx.x: idx of ne0 / BLOCK_SIZE - int nidx = threadIdx.x + blockIdx.x * blockDim.x; - if (nidx >= ne0) { - return; - } - dst[nidx] = start + step * nidx; -} - -static __global__ void timestep_embedding_f32(const float * timesteps, float * dst, const int nb1, const int dim, const int max_period) { - // blockIDx.y: idx of timesteps->ne[0] - // blockIDx.x: idx of ((dim + 1) / 2) / BLOCK_SIZE - int i = blockIdx.y; - int j = threadIdx.x + blockIdx.x * blockDim.x; - float * embed_data = (float *)((char *)dst + i*nb1); - - if (dim % 2 != 0 && j == ((dim + 1) / 2)) { - embed_data[dim] = 0.f; - } - - int half = dim / 2; - if (j >= half) { - return; - } - - float timestep = timesteps[i]; - float freq = (float)expf(-logf(max_period) * j / half); - float arg = timestep * freq; - embed_data[j] = cosf(arg); - embed_data[j + half] = sinf(arg); -} - -template -static __global__ void group_norm_f32(const float * x, float * dst, const int group_size, const int ne_elements, const float eps) { - // blockIdx.x: num_groups idx - // threadIdx.x: block_size idx - int start = blockIdx.x * group_size; - int end = start + group_size; - - start += threadIdx.x; - - if (end >= ne_elements) { - end = ne_elements; - } - - float tmp = 0.0f; // partial sum for thread in warp - - for (int j = start; j < end; j += block_size) { - tmp += x[j]; - } - - tmp = warp_reduce_sum(tmp); - if (block_size > WARP_SIZE) { - __shared__ float s_sum[32]; - int warp_id = threadIdx.x / WARP_SIZE; - int lane_id = threadIdx.x % WARP_SIZE; - if (lane_id == 0) { - s_sum[warp_id] = tmp; - } - __syncthreads(); - tmp = s_sum[lane_id]; - tmp = warp_reduce_sum(tmp); - } - - float mean = tmp / group_size; - tmp = 0.0f; - - for (int j = start; j < end; j += block_size) { - float xi = x[j] - mean; - dst[j] = xi; - tmp += xi * xi; - } - - tmp = warp_reduce_sum(tmp); - if (block_size > WARP_SIZE) { - __shared__ float s_sum[32]; - int warp_id = threadIdx.x / WARP_SIZE; - int lane_id = threadIdx.x % WARP_SIZE; - if (lane_id == 0) { - s_sum[warp_id] = tmp; - } - __syncthreads(); - tmp = s_sum[lane_id]; - tmp = warp_reduce_sum(tmp); - } - - float variance = tmp / group_size; - float scale = rsqrtf(variance + eps); - for (int j = start; j < end; j += block_size) { - dst[j] *= scale; - } -} - -template -static __global__ void rms_norm_f32(const float * x, float * dst, const int ncols, const float eps) { - const int row = blockIdx.x*blockDim.y + threadIdx.y; - const int tid = threadIdx.x; - - float tmp = 0.0f; // partial sum for thread in warp - - for (int col = tid; col < ncols; col += block_size) { - const float xi = x[row*ncols + col]; - tmp += xi * xi; - } - - // sum up partial sums - tmp = warp_reduce_sum(tmp); - if (block_size > WARP_SIZE) { - __shared__ float s_sum[32]; - int warp_id = threadIdx.x / WARP_SIZE; - int lane_id = threadIdx.x % WARP_SIZE; - if (lane_id == 0) { - s_sum[warp_id] = tmp; - } - __syncthreads(); - tmp = s_sum[lane_id]; - tmp = warp_reduce_sum(tmp); - } - - const float mean = tmp / ncols; - const float scale = rsqrtf(mean + eps); - - for (int col = tid; col < ncols; col += block_size) { - dst[row*ncols + col] = scale * x[row*ncols + col]; - } -} - -static __device__ __forceinline__ void dequantize_q4_0(const void * vx, const int ib, const int iqs, dfloat2 & v){ - const block_q4_0 * x = (const block_q4_0 *) vx; - - const dfloat d = x[ib].d; - - const int vui = x[ib].qs[iqs]; - - v.x = vui & 0xF; - v.y = vui >> 4; - -#ifdef GGML_CUDA_F16 - v = __hsub2(v, {8.0f, 8.0f}); - v = __hmul2(v, {d, d}); -#else - v.x = (v.x - 8.0f) * d; - v.y = (v.y - 8.0f) * d; -#endif // GGML_CUDA_F16 -} - -static __device__ __forceinline__ void dequantize_q4_1(const void * vx, const int ib, const int iqs, dfloat2 & v){ - const block_q4_1 * x = (const block_q4_1 *) vx; - - const dfloat d = __low2half(x[ib].dm); - const dfloat m = __high2half(x[ib].dm); - - const int vui = x[ib].qs[iqs]; - - v.x = vui & 0xF; - v.y = vui >> 4; - -#ifdef GGML_CUDA_F16 - v = __hmul2(v, {d, d}); - v = __hadd2(v, {m, m}); -#else - v.x = (v.x * d) + m; - v.y = (v.y * d) + m; -#endif // GGML_CUDA_F16 -} - -static __device__ __forceinline__ void dequantize_q5_0(const void * vx, const int ib, const int iqs, dfloat2 & v){ - const block_q5_0 * x = (const block_q5_0 *) vx; - - const dfloat d = x[ib].d; - - uint32_t qh; - memcpy(&qh, x[ib].qh, sizeof(qh)); - - const int xh_0 = ((qh >> (iqs + 0)) << 4) & 0x10; - const int xh_1 = ((qh >> (iqs + 12)) ) & 0x10; - - v.x = ((x[ib].qs[iqs] & 0xf) | xh_0); - v.y = ((x[ib].qs[iqs] >> 4) | xh_1); - -#ifdef GGML_CUDA_F16 - v = __hsub2(v, {16.0f, 16.0f}); - v = __hmul2(v, {d, d}); -#else - v.x = (v.x - 16.0f) * d; - v.y = (v.y - 16.0f) * d; -#endif // GGML_CUDA_F16 -} - -static __device__ __forceinline__ void dequantize_q5_1(const void * vx, const int ib, const int iqs, dfloat2 & v){ - const block_q5_1 * x = (const block_q5_1 *) vx; - - const dfloat d = __low2half(x[ib].dm); - const dfloat m = __high2half(x[ib].dm); - - uint32_t qh; - memcpy(&qh, x[ib].qh, sizeof(qh)); - - const int xh_0 = ((qh >> (iqs + 0)) << 4) & 0x10; - const int xh_1 = ((qh >> (iqs + 12)) ) & 0x10; - - v.x = ((x[ib].qs[iqs] & 0xf) | xh_0); - v.y = ((x[ib].qs[iqs] >> 4) | xh_1); - -#ifdef GGML_CUDA_F16 - v = __hmul2(v, {d, d}); - v = __hadd2(v, {m, m}); -#else - v.x = (v.x * d) + m; - v.y = (v.y * d) + m; -#endif // GGML_CUDA_F16 -} - -static __device__ __forceinline__ void dequantize_q8_0(const void * vx, const int ib, const int iqs, dfloat2 & v){ - const block_q8_0 * x = (const block_q8_0 *) vx; - - const dfloat d = x[ib].d; - - v.x = x[ib].qs[iqs + 0]; - v.y = x[ib].qs[iqs + 1]; - -#ifdef GGML_CUDA_F16 - v = __hmul2(v, {d, d}); -#else - v.x *= d; - v.y *= d; -#endif // GGML_CUDA_F16 -} - -template -static __global__ void dequantize_block_q4_0(const void * __restrict__ vx, dst_t * __restrict__ yy, int nb32) { - - const int i = blockIdx.x; - - // assume 32 threads - const int tid = threadIdx.x; - const int il = tid/8; - const int ir = tid%8; - const int ib = 8*i + ir; - if (ib >= nb32) { - return; - } - - dst_t * y = yy + 256*i + 32*ir + 4*il; - - const block_q4_0 * x = (const block_q4_0 *)vx + ib; - const float d = __half2float(x->d); - const float dm = -8*d; - - const uint8_t * q = x->qs + 4*il; - - for (int l = 0; l < 4; ++l) { - y[l+ 0] = d * (q[l] & 0xF) + dm; - y[l+16] = d * (q[l] >> 4) + dm; - } -} - -template -static __global__ void dequantize_block_q4_1(const void * __restrict__ vx, dst_t * __restrict__ yy, int nb32) { - - const int i = blockIdx.x; - - // assume 32 threads - const int tid = threadIdx.x; - const int il = tid/8; - const int ir = tid%8; - const int ib = 8*i + ir; - if (ib >= nb32) { - return; - } - - dst_t * y = yy + 256*i + 32*ir + 4*il; - - const block_q4_1 * x = (const block_q4_1 *)vx + ib; - const float2 d = __half22float2(x->dm); - - const uint8_t * q = x->qs + 4*il; - - for (int l = 0; l < 4; ++l) { - y[l+ 0] = d.x * (q[l] & 0xF) + d.y; - y[l+16] = d.x * (q[l] >> 4) + d.y; - } -} - -//================================== k-quants - -template -static __global__ void dequantize_block_q2_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_q2_K * x = (const block_q2_K *) vx; - - const int tid = threadIdx.x; -#if QK_K == 256 - const int n = tid/32; - const int l = tid - 32*n; - const int is = 8*n + l/16; - - const uint8_t q = x[i].qs[32*n + l]; - dst_t * y = yy + i*QK_K + 128*n; - - float dall = __low2half(x[i].dm); - float dmin = __high2half(x[i].dm); - y[l+ 0] = dall * (x[i].scales[is+0] & 0xF) * ((q >> 0) & 3) - dmin * (x[i].scales[is+0] >> 4); - y[l+32] = dall * (x[i].scales[is+2] & 0xF) * ((q >> 2) & 3) - dmin * (x[i].scales[is+2] >> 4); - y[l+64] = dall * (x[i].scales[is+4] & 0xF) * ((q >> 4) & 3) - dmin * (x[i].scales[is+4] >> 4); - y[l+96] = dall * (x[i].scales[is+6] & 0xF) * ((q >> 6) & 3) - dmin * (x[i].scales[is+6] >> 4); -#else - const int is = tid/16; // 0 or 1 - const int il = tid%16; // 0...15 - const uint8_t q = x[i].qs[il] >> (2*is); - dst_t * y = yy + i*QK_K + 16*is + il; - float dall = __low2half(x[i].dm); - float dmin = __high2half(x[i].dm); - y[ 0] = dall * (x[i].scales[is+0] & 0xF) * ((q >> 0) & 3) - dmin * (x[i].scales[is+0] >> 4); - y[32] = dall * (x[i].scales[is+2] & 0xF) * ((q >> 4) & 3) - dmin * (x[i].scales[is+2] >> 4); -#endif - -} - -template -static __global__ void dequantize_block_q3_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_q3_K * x = (const block_q3_K *) vx; - -#if QK_K == 256 - const int r = threadIdx.x/4; - const int tid = r/2; - const int is0 = r%2; - const int l0 = 16*is0 + 4*(threadIdx.x%4); - const int n = tid / 4; - const int j = tid - 4*n; - - uint8_t m = 1 << (4*n + j); - int is = 8*n + 2*j + is0; - int shift = 2*j; - - int8_t us = is < 4 ? (x[i].scales[is-0] & 0xF) | (((x[i].scales[is+8] >> 0) & 3) << 4) : - is < 8 ? (x[i].scales[is-0] & 0xF) | (((x[i].scales[is+4] >> 2) & 3) << 4) : - is < 12 ? (x[i].scales[is-8] >> 4) | (((x[i].scales[is+0] >> 4) & 3) << 4) : - (x[i].scales[is-8] >> 4) | (((x[i].scales[is-4] >> 6) & 3) << 4); - float d_all = x[i].d; - float dl = d_all * (us - 32); - - dst_t * y = yy + i*QK_K + 128*n + 32*j; - const uint8_t * q = x[i].qs + 32*n; - const uint8_t * hm = x[i].hmask; - - for (int l = l0; l < l0+4; ++l) y[l] = dl * ((int8_t)((q[l] >> shift) & 3) - ((hm[l] & m) ? 0 : 4)); -#else - const int tid = threadIdx.x; - const int is = tid/16; // 0 or 1 - const int il = tid%16; // 0...15 - const int im = il/8; // 0...1 - const int in = il%8; // 0...7 - - dst_t * y = yy + i*QK_K + 16*is + il; - - const uint8_t q = x[i].qs[il] >> (2*is); - const uint8_t h = x[i].hmask[in] >> (2*is + im); - const float d = (float)x[i].d; - - if (is == 0) { - y[ 0] = d * ((x[i].scales[0] & 0xF) - 8) * ((int8_t)((q >> 0) & 3) - ((h >> 0) & 1 ? 0 : 4)); - y[32] = d * ((x[i].scales[1] & 0xF) - 8) * ((int8_t)((q >> 4) & 3) - ((h >> 4) & 1 ? 0 : 4)); - } else { - y[ 0] = d * ((x[i].scales[0] >> 4) - 8) * ((int8_t)((q >> 0) & 3) - ((h >> 0) & 1 ? 0 : 4)); - y[32] = d * ((x[i].scales[1] >> 4) - 8) * ((int8_t)((q >> 4) & 3) - ((h >> 4) & 1 ? 0 : 4)); - } -#endif - -} - -#if QK_K == 256 -static inline __device__ void get_scale_min_k4(int j, const uint8_t * q, uint8_t & d, uint8_t & m) { - if (j < 4) { - d = q[j] & 63; m = q[j + 4] & 63; - } else { - d = (q[j+4] & 0xF) | ((q[j-4] >> 6) << 4); - m = (q[j+4] >> 4) | ((q[j-0] >> 6) << 4); - } -} -#endif - -template -static __global__ void dequantize_block_q4_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { - const block_q4_K * x = (const block_q4_K *) vx; - - const int i = blockIdx.x; - -#if QK_K == 256 - // assume 32 threads - const int tid = threadIdx.x; - const int il = tid/8; - const int ir = tid%8; - const int is = 2*il; - const int n = 4; - - dst_t * y = yy + i*QK_K + 64*il + n*ir; - - const float dall = __low2half(x[i].dm); - const float dmin = __high2half(x[i].dm); - - const uint8_t * q = x[i].qs + 32*il + n*ir; - - uint8_t sc, m; - get_scale_min_k4(is + 0, x[i].scales, sc, m); - const float d1 = dall * sc; const float m1 = dmin * m; - get_scale_min_k4(is + 1, x[i].scales, sc, m); - const float d2 = dall * sc; const float m2 = dmin * m; - for (int l = 0; l < n; ++l) { - y[l + 0] = d1 * (q[l] & 0xF) - m1; - y[l +32] = d2 * (q[l] >> 4) - m2; - } -#else - const int tid = threadIdx.x; - const uint8_t * q = x[i].qs; - dst_t * y = yy + i*QK_K; - const float d = (float)x[i].dm[0]; - const float m = (float)x[i].dm[1]; - y[tid+ 0] = d * (x[i].scales[0] & 0xF) * (q[tid] & 0xF) - m * (x[i].scales[0] >> 4); - y[tid+32] = d * (x[i].scales[1] & 0xF) * (q[tid] >> 4) - m * (x[i].scales[1] >> 4); -#endif -} - -template -static __global__ void dequantize_block_q5_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { - const block_q5_K * x = (const block_q5_K *) vx; - - const int i = blockIdx.x; - -#if QK_K == 256 - // assume 64 threads - this is very slightly better than the one below - const int tid = threadIdx.x; - const int il = tid/16; // il is in 0...3 - const int ir = tid%16; // ir is in 0...15 - const int is = 2*il; // is is in 0...6 - - dst_t * y = yy + i*QK_K + 64*il + 2*ir; - - const float dall = __low2half(x[i].dm); - const float dmin = __high2half(x[i].dm); - - const uint8_t * ql = x[i].qs + 32*il + 2*ir; - const uint8_t * qh = x[i].qh + 2*ir; - - uint8_t sc, m; - get_scale_min_k4(is + 0, x[i].scales, sc, m); - const float d1 = dall * sc; const float m1 = dmin * m; - get_scale_min_k4(is + 1, x[i].scales, sc, m); - const float d2 = dall * sc; const float m2 = dmin * m; - - uint8_t hm = 1 << (2*il); - y[ 0] = d1 * ((ql[ 0] & 0xF) + (qh[ 0] & hm ? 16 : 0)) - m1; - y[ 1] = d1 * ((ql[ 1] & 0xF) + (qh[ 1] & hm ? 16 : 0)) - m1; - hm <<= 1; - y[32] = d2 * ((ql[ 0] >> 4) + (qh[ 0] & hm ? 16 : 0)) - m2; - y[33] = d2 * ((ql[ 1] >> 4) + (qh[ 1] & hm ? 16 : 0)) - m2; -#else - const int tid = threadIdx.x; - const uint8_t q = x[i].qs[tid]; - const int im = tid/8; // 0...3 - const int in = tid%8; // 0...7 - const int is = tid/16; // 0 or 1 - const uint8_t h = x[i].qh[in] >> im; - const float d = x[i].d; - dst_t * y = yy + i*QK_K + tid; - y[ 0] = d * x[i].scales[is+0] * ((q & 0xF) - ((h >> 0) & 1 ? 0 : 16)); - y[32] = d * x[i].scales[is+2] * ((q >> 4) - ((h >> 4) & 1 ? 0 : 16)); -#endif -} - -template -static __global__ void dequantize_block_q6_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { - const block_q6_K * x = (const block_q6_K *) vx; - - const int i = blockIdx.x; -#if QK_K == 256 - - // assume 64 threads - this is very slightly better than the one below - const int tid = threadIdx.x; - const int ip = tid/32; // ip is 0 or 1 - const int il = tid - 32*ip; // 0...32 - const int is = 8*ip + il/16; - - dst_t * y = yy + i*QK_K + 128*ip + il; - - const float d = x[i].d; - - const uint8_t * ql = x[i].ql + 64*ip + il; - const uint8_t qh = x[i].qh[32*ip + il]; - const int8_t * sc = x[i].scales + is; - - y[ 0] = d * sc[0] * ((int8_t)((ql[ 0] & 0xF) | (((qh >> 0) & 3) << 4)) - 32); - y[32] = d * sc[2] * ((int8_t)((ql[32] & 0xF) | (((qh >> 2) & 3) << 4)) - 32); - y[64] = d * sc[4] * ((int8_t)((ql[ 0] >> 4) | (((qh >> 4) & 3) << 4)) - 32); - y[96] = d * sc[6] * ((int8_t)((ql[32] >> 4) | (((qh >> 6) & 3) << 4)) - 32); -#else - - // assume 32 threads - const int tid = threadIdx.x; - const int ip = tid/16; // 0 or 1 - const int il = tid - 16*ip; // 0...15 - - dst_t * y = yy + i*QK_K + 16*ip + il; - - const float d = x[i].d; - - const uint8_t ql = x[i].ql[16*ip + il]; - const uint8_t qh = x[i].qh[il] >> (2*ip); - const int8_t * sc = x[i].scales; - - y[ 0] = d * sc[ip+0] * ((int8_t)((ql & 0xF) | (((qh >> 0) & 3) << 4)) - 32); - y[32] = d * sc[ip+2] * ((int8_t)((ql >> 4) | (((qh >> 4) & 3) << 4)) - 32); -#endif -} - -inline bool ggml_cuda_supports_mmq(enum ggml_type type) { - switch (type) { - case GGML_TYPE_Q4_0: - case GGML_TYPE_Q4_1: - case GGML_TYPE_Q5_0: - case GGML_TYPE_Q5_1: - case GGML_TYPE_Q8_0: - case GGML_TYPE_Q2_K: - case GGML_TYPE_Q3_K: - case GGML_TYPE_Q4_K: - case GGML_TYPE_Q5_K: - case GGML_TYPE_Q6_K: - return true; - default: - return false; - } -} - -template -static __global__ void dequantize_block_iq2_xxs(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_iq2_xxs * x = (const block_iq2_xxs *) vx; - - const int tid = threadIdx.x; -#if QK_K == 256 - const int il = tid/8; // 0...3 - const int ib = tid%8; // 0...7 - dst_t * y = yy + i*QK_K + 32*ib + 8*il; - const uint16_t * q2 = x[i].qs + 4*ib; - const uint8_t * aux8 = (const uint8_t *)q2; - const uint8_t * grid = (const uint8_t *)(iq2xxs_grid + aux8[il]); - const uint32_t aux32 = q2[2] | (q2[3] << 16); - const float d = (float)x[i].d * (0.5f + (aux32 >> 28)) * 0.25f; - const uint8_t signs = ksigns_iq2xs[(aux32 >> 7*il) & 127]; - for (int j = 0; j < 8; ++j) y[j] = d * grid[j] * (signs & kmask_iq2xs[j] ? -1.f : 1.f); -#else - assert(false); -#endif - -} - -template -static __global__ void dequantize_block_iq2_xs(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_iq2_xs * x = (const block_iq2_xs *) vx; - - const int tid = threadIdx.x; -#if QK_K == 256 - const int il = tid/8; // 0...3 - const int ib = tid%8; // 0...7 - dst_t * y = yy + i*QK_K + 32*ib + 8*il; - const uint16_t * q2 = x[i].qs + 4*ib; - const uint8_t * grid = (const uint8_t *)(iq2xs_grid + (q2[il] & 511)); - const float d = (float)x[i].d * (0.5f + ((x[i].scales[ib] >> 4*(il/2)) & 0xf)) * 0.25f; - const uint8_t signs = ksigns_iq2xs[q2[il] >> 9]; - for (int j = 0; j < 8; ++j) y[j] = d * grid[j] * (signs & kmask_iq2xs[j] ? -1.f : 1.f); -#else - assert(false); -#endif - -} - -template -static __global__ void dequantize_block_iq2_s(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_iq2_s * x = (const block_iq2_s *) vx; - - const int tid = threadIdx.x; -#if QK_K == 256 - const int il = tid/8; // 0...3 - const int ib = tid%8; // 0...7 - dst_t * y = yy + i*QK_K + 32*ib + 8*il; - const uint8_t * grid = (const uint8_t *)(iq2s_grid + (x[i].qs[4*ib+il] | ((x[i].qh[ib] << (8-2*il)) & 0x300))); - const float d = (float)x[i].d * (0.5f + ((x[i].scales[ib] >> 4*(il/2)) & 0xf)) * 0.25f; - const uint8_t signs = x[i].qs[QK_K/8+4*ib+il]; - for (int j = 0; j < 8; ++j) y[j] = d * grid[j] * (signs & kmask_iq2xs[j] ? -1.f : 1.f); -#else - assert(false); -#endif - -} - -template -static __global__ void dequantize_block_iq3_xxs(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_iq3_xxs * x = (const block_iq3_xxs *) vx; - - const int tid = threadIdx.x; -#if QK_K == 256 - const int il = tid/8; // 0...3 - const int ib = tid%8; // 0...7 - dst_t * y = yy + i*QK_K + 32*ib + 8*il; - const uint8_t * q3 = x[i].qs + 8*ib; - const uint16_t * gas = (const uint16_t *)(x[i].qs + QK_K/4) + 2*ib; - const uint8_t * grid1 = (const uint8_t *)(iq3xxs_grid + q3[2*il+0]); - const uint8_t * grid2 = (const uint8_t *)(iq3xxs_grid + q3[2*il+1]); - const uint32_t aux32 = gas[0] | (gas[1] << 16); - const float d = (float)x[i].d * (0.5f + (aux32 >> 28)) * 0.5f; - const uint8_t signs = ksigns_iq2xs[(aux32 >> 7*il) & 127]; - for (int j = 0; j < 4; ++j) { - y[j+0] = d * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f); - y[j+4] = d * grid2[j] * (signs & kmask_iq2xs[j+4] ? -1.f : 1.f); - } -#else - assert(false); -#endif - -} - -template -static __global__ void dequantize_block_iq3_s(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_iq3_s * x = (const block_iq3_s *) vx; - - const int tid = threadIdx.x; -#if QK_K == 256 - const int il = tid/8; // 0...3 - const int ib = tid%8; // 0...7 - dst_t * y = yy + i*QK_K + 32*ib + 8*il; - const uint8_t * qs = x[i].qs + 8*ib; - const uint8_t * grid1 = (const uint8_t *)(iq3s_grid + (qs[2*il+0] | ((x[i].qh[ib] << (8-2*il)) & 256))); - const uint8_t * grid2 = (const uint8_t *)(iq3s_grid + (qs[2*il+1] | ((x[i].qh[ib] << (7-2*il)) & 256))); - const float d = (float)x[i].d * (1 + 2*((x[i].scales[ib/2] >> 4*(ib%2)) & 0xf)); - const uint8_t signs = x[i].signs[4*ib + il]; - for (int j = 0; j < 4; ++j) { - y[j+0] = d * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f); - y[j+4] = d * grid2[j] * (signs & kmask_iq2xs[j+4] ? -1.f : 1.f); - } -#else - assert(false); -#endif - -} - -template -static __global__ void dequantize_block_iq1_s(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_iq1_s * x = (const block_iq1_s *) vx; - - const int tid = threadIdx.x; -#if QK_K == 256 - const int il = tid/8; // 0...3 - const int ib = tid%8; // 0...7 - dst_t * y = yy + i*QK_K + 32*ib + 8*il; - const float delta = x[i].qh[ib] & 0x8000 ? -1 - IQ1S_DELTA : -1 + IQ1S_DELTA; - const float d = (float)x[i].d * (2*((x[i].qh[ib] >> 12) & 7) + 1); - uint32_t grid32[2]; const int8_t * q = (const int8_t *)grid32; - grid32[0] = iq1s_grid_gpu[x[i].qs[4*ib+il] | (((x[i].qh[ib] >> 3*il) & 7) << 8)]; - grid32[1] = (grid32[0] >> 4) & 0x0f0f0f0f; - grid32[0] &= 0x0f0f0f0f; - for (int j = 0; j < 8; ++j) { - y[j] = d * (q[j] + delta); - } -#else - assert(false); -#endif - -} - -static const __device__ int8_t kvalues_iq4nl[16] = {-127, -104, -83, -65, -49, -35, -22, -10, 1, 13, 25, 38, 53, 69, 89, 113}; - -template -static __global__ void dequantize_block_iq4_nl(const void * __restrict__ vx, dst_t * __restrict__ yy) { - - const int i = blockIdx.x; - const block_iq4_nl * x = (const block_iq4_nl *) vx + i*(QK_K/QK4_NL); - - const int tid = threadIdx.x; - const int il = tid/8; // 0...3 - const int ib = tid%8; // 0...7 - dst_t * y = yy + i*QK_K + 32*ib + 4*il; - const uint8_t * q4 = x[ib].qs + 4*il; - const float d = (float)x[ib].d; - for (int j = 0; j < 4; ++j) { - y[j+ 0] = d * kvalues_iq4nl[q4[j] & 0xf]; - y[j+16] = d * kvalues_iq4nl[q4[j] >> 4]; - } - -} - -#if QK_K != 64 -template -static __global__ void dequantize_block_iq4_xs(const void * __restrict__ vx, dst_t * __restrict__ yy) { - const int i = blockIdx.x; - const block_iq4_xs * x = (const block_iq4_xs *)vx; - - const int tid = threadIdx.x; - const int il = tid/8; // 0...3 - const int ib = tid%8; // 0...7 - dst_t * y = yy + i*QK_K + 32*ib + 4*il; - const uint8_t * q4 = x[i].qs + 16*ib + 4*il; - const float d = (float)x[i].d * ((((x[i].scales_l[ib/2] >> 4*(ib%2)) & 0xf) | (((x[i].scales_h >> 2*ib) & 3) << 4)) - 32); - for (int j = 0; j < 4; ++j) { - y[j+ 0] = d * kvalues_iq4nl[q4[j] & 0xf]; - y[j+16] = d * kvalues_iq4nl[q4[j] >> 4]; - } -} -#endif - -static __global__ void dequantize_mul_mat_vec_q2_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols, int nrows) { - - static_assert(16%K_QUANTS_PER_ITERATION == 0, "16 must be divisible by K_QUANTS_PER_ITERATION"); - - const int row = blockIdx.x*blockDim.y + threadIdx.y; - if (row > nrows) return; - - const int num_blocks_per_row = ncols / QK_K; - const int ib0 = row*num_blocks_per_row; - - const block_q2_K * x = (const block_q2_K *)vx + ib0; - - float tmp = 0; // partial sum for thread in warp - -#if QK_K == 256 - const int tid = threadIdx.x/K_QUANTS_PER_ITERATION; // 0...31 or 0...15 - const int ix = threadIdx.x%K_QUANTS_PER_ITERATION; // 0 or 0,1 - - const int step = 16/K_QUANTS_PER_ITERATION; - - const int im = tid/step; // 0 or 1. 0 computes 0..., 1 computes 128... - const int in = tid - step*im; // 0...15 or 0...7 - - const int l0 = K_QUANTS_PER_ITERATION*in; // 0...15 or 0...14 in steps of 2 - const int q_offset = 32*im + l0; - const int s_offset = 8*im; - const int y_offset = 128*im + l0; - - uint32_t aux[4]; - const uint8_t * d = (const uint8_t *)aux; - const uint8_t * m = (const uint8_t *)(aux + 2); - - for (int i = ix; i < num_blocks_per_row; i += K_QUANTS_PER_ITERATION) { - - const float * y = yy + i * QK_K + y_offset; - const uint8_t * q = x[i].qs + q_offset; - - const float dall = __low2half(x[i].dm); - const float dmin = __high2half(x[i].dm); - - const uint32_t * a = (const uint32_t *)(x[i].scales + s_offset); - aux[0] = a[0] & 0x0f0f0f0f; - aux[1] = a[1] & 0x0f0f0f0f; - aux[2] = (a[0] >> 4) & 0x0f0f0f0f; - aux[3] = (a[1] >> 4) & 0x0f0f0f0f; - - float sum1 = 0, sum2 = 0; - for (int l = 0; l < K_QUANTS_PER_ITERATION; ++l) { - sum1 += y[l+ 0] * d[0] * ((q[l+ 0] >> 0) & 3) - + y[l+32] * d[2] * ((q[l+ 0] >> 2) & 3) - + y[l+64] * d[4] * ((q[l+ 0] >> 4) & 3) - + y[l+96] * d[6] * ((q[l+ 0] >> 6) & 3) - + y[l+16] * d[1] * ((q[l+16] >> 0) & 3) - + y[l+48] * d[3] * ((q[l+16] >> 2) & 3) - + y[l+80] * d[5] * ((q[l+16] >> 4) & 3) - +y[l+112] * d[7] * ((q[l+16] >> 6) & 3); - sum2 += y[l+ 0] * m[0] + y[l+32] * m[2] + y[l+64] * m[4] + y[ l+96] * m[6] - + y[l+16] * m[1] + y[l+48] * m[3] + y[l+80] * m[5] + y[l+112] * m[7]; - - } - tmp += dall * sum1 - dmin * sum2; - - } -#else - const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...15 or 0...7 - const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); // 0....1 or 0...3 - const int offset = tid * K_QUANTS_PER_ITERATION; - - uint32_t uaux[2]; - const uint8_t * d = (const uint8_t *)uaux; - - for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { - - const float * y = yy + i * QK_K + offset; - const uint8_t * q = x[i].qs + offset; - const uint32_t * s = (const uint32_t *)x[i].scales; - - uaux[0] = s[0] & 0x0f0f0f0f; - uaux[1] = (s[0] >> 4) & 0x0f0f0f0f; - - const float2 dall = __half22float2(x[i].dm); - - float sum1 = 0, sum2 = 0; - for (int l = 0; l < K_QUANTS_PER_ITERATION; ++l) { - const uint8_t ql = q[l]; - sum1 += y[l+ 0] * d[0] * ((ql >> 0) & 3) - + y[l+16] * d[1] * ((ql >> 2) & 3) - + y[l+32] * d[2] * ((ql >> 4) & 3) - + y[l+48] * d[3] * ((ql >> 6) & 3); - sum2 += y[l+0] * d[4] + y[l+16] * d[5] + y[l+32] * d[6] + y[l+48] * d[7]; - } - tmp += dall.x * sum1 - dall.y * sum2; - } -#endif - - // sum up partial sums and write back result - tmp = warp_reduce_sum(tmp); - - if (threadIdx.x == 0) { - dst[row] = tmp; - } -} - -static __global__ void dequantize_mul_mat_vec_q3_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols, int nrows) { - - const int row = blockIdx.x*blockDim.y + threadIdx.y; - if (row > nrows) return; - - const int num_blocks_per_row = ncols / QK_K; - const int ib0 = row*num_blocks_per_row; - - const block_q3_K * x = (const block_q3_K *)vx + ib0; - - float tmp = 0; // partial sum for thread in warp - -#if QK_K == 256 - - const uint16_t kmask1 = 0x0303; - const uint16_t kmask2 = 0x0f0f; - - const int tid = threadIdx.x/K_QUANTS_PER_ITERATION; // 0...31 or 0...16 - const int ix = threadIdx.x%K_QUANTS_PER_ITERATION; // 0 or 0,1 - - const int n = K_QUANTS_PER_ITERATION; // iterations in the inner loop - const int step = 16/K_QUANTS_PER_ITERATION; - const int im = tid/step; // 0 or 1. 0 computes 0..., 1 computes 128... - const int in = tid - step*im; // 0....15 or 0...7 - - const uint8_t m = 1 << (4*im); - - const int l0 = n*in; // 0...15 or 0...14 in steps of 2 - const int q_offset = 32*im + l0; - const int y_offset = 128*im + l0; - - uint16_t utmp[4]; - const int8_t * s = (const int8_t *)utmp; - - const uint16_t s_shift = 4*im; - - for (int i = ix; i < num_blocks_per_row; i += K_QUANTS_PER_ITERATION) { - - const float * y = yy + i * QK_K + y_offset; - const uint8_t * q = x[i].qs + q_offset; - const uint8_t * h = x[i].hmask + l0; - - const uint16_t * a = (const uint16_t *)x[i].scales; - utmp[0] = ((a[0] >> s_shift) & kmask2) | (((a[4] >> (s_shift + 0)) & kmask1) << 4); - utmp[1] = ((a[1] >> s_shift) & kmask2) | (((a[5] >> (s_shift + 0)) & kmask1) << 4); - utmp[2] = ((a[2] >> s_shift) & kmask2) | (((a[4] >> (s_shift + 2)) & kmask1) << 4); - utmp[3] = ((a[3] >> s_shift) & kmask2) | (((a[5] >> (s_shift + 2)) & kmask1) << 4); - - const float d = x[i].d; - - float sum = 0; - for (int l = 0; l < n; ++l) { - sum += y[l+ 0] * (s[0] - 32) * (((q[l] >> 0) & 3) - (h[l] & (m << 0) ? 0 : 4)) - + y[l+32] * (s[2] - 32) * (((q[l] >> 2) & 3) - (h[l] & (m << 1) ? 0 : 4)) - + y[l+64] * (s[4] - 32) * (((q[l] >> 4) & 3) - (h[l] & (m << 2) ? 0 : 4)) - + y[l+96] * (s[6] - 32) * (((q[l] >> 6) & 3) - (h[l] & (m << 3) ? 0 : 4)); - sum += y[l+16] * (s[1] - 32) * (((q[l+16] >> 0) & 3) - (h[l+16] & (m << 0) ? 0 : 4)) - + y[l+48] * (s[3] - 32) * (((q[l+16] >> 2) & 3) - (h[l+16] & (m << 1) ? 0 : 4)) - + y[l+80] * (s[5] - 32) * (((q[l+16] >> 4) & 3) - (h[l+16] & (m << 2) ? 0 : 4)) - + y[l+112] * (s[7] - 32) * (((q[l+16] >> 6) & 3) - (h[l+16] & (m << 3) ? 0 : 4)); - } - tmp += d * sum; - - } -#else - - const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...15 or 0...7 - const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); // 0....1 or 0...3 - const int offset = tid * K_QUANTS_PER_ITERATION; // 0...15 or 0...14 - const int in = offset/8; // 0 or 1 - const int im = offset%8; // 0...7 - - for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { - - const float * y = yy + i * QK_K + offset; - const uint8_t * q = x[i].qs + offset; - const uint8_t * s = x[i].scales; - - const float dall = (float)x[i].d; - - float sum = 0; - for (int l = 0; l < K_QUANTS_PER_ITERATION; ++l) { - const uint8_t hl = x[i].hmask[im+l] >> in; - const uint8_t ql = q[l]; - sum += y[l+ 0] * dall * ((s[0] & 0xF) - 8) * ((int8_t)((ql >> 0) & 3) - ((hl >> 0) & 1 ? 0 : 4)) - + y[l+16] * dall * ((s[0] >> 4) - 8) * ((int8_t)((ql >> 2) & 3) - ((hl >> 2) & 1 ? 0 : 4)) - + y[l+32] * dall * ((s[1] & 0xF) - 8) * ((int8_t)((ql >> 4) & 3) - ((hl >> 4) & 1 ? 0 : 4)) - + y[l+48] * dall * ((s[1] >> 4) - 8) * ((int8_t)((ql >> 6) & 3) - ((hl >> 6) & 1 ? 0 : 4)); - } - tmp += sum; - } -#endif - - // sum up partial sums and write back result - tmp = warp_reduce_sum(tmp); - - if (threadIdx.x == 0) { - dst[row] = tmp; - } -} - -static __global__ void dequantize_mul_mat_vec_q4_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols, int nrows) { - - const int row = blockIdx.x*blockDim.y + threadIdx.y; - if (row > nrows) return; - const int num_blocks_per_row = ncols / QK_K; - const int ib0 = row*num_blocks_per_row; - - const block_q4_K * x = (const block_q4_K *)vx + ib0; - -#if QK_K == 256 - const uint16_t kmask1 = 0x3f3f; - const uint16_t kmask2 = 0x0f0f; - const uint16_t kmask3 = 0xc0c0; - - const int tid = threadIdx.x/K_QUANTS_PER_ITERATION; // 0...31 or 0...16 - const int ix = threadIdx.x%K_QUANTS_PER_ITERATION; // 0 or 0,1 - - const int step = 8/K_QUANTS_PER_ITERATION; // 8 or 4 - - const int il = tid/step; // 0...3 - const int ir = tid - step*il; // 0...7 or 0...3 - const int n = 2 * K_QUANTS_PER_ITERATION; // 2 or 4 - - const int im = il/2; // 0 or 1. 0 computes 0,32 + 128,160, 1 computes 64,96 + 192,224 - const int in = il%2; - - const int l0 = n*(2*ir + in); - const int q_offset = 32*im + l0; - const int y_offset = 64*im + l0; - - uint16_t aux[4]; - const uint8_t * sc = (const uint8_t *)aux; - -#if K_QUANTS_PER_ITERATION == 2 - uint32_t q32[4]; - const uint8_t * q4 = (const uint8_t *)q32; -#else - uint16_t q16[4]; - const uint8_t * q4 = (const uint8_t *)q16; -#endif - - float tmp = 0; // partial sum for thread in warp - - for (int i = ix; i < num_blocks_per_row; i += K_QUANTS_PER_ITERATION) { - - const float * y1 = yy + i*QK_K + y_offset; - const float * y2 = y1 + 128; - - const float dall = __low2half(x[i].dm); - const float dmin = __high2half(x[i].dm); - - const uint16_t * a = (const uint16_t *)x[i].scales; - aux[0] = a[im+0] & kmask1; - aux[1] = a[im+2] & kmask1; - aux[2] = ((a[im+4] >> 0) & kmask2) | ((a[im+0] & kmask3) >> 2); - aux[3] = ((a[im+4] >> 4) & kmask2) | ((a[im+2] & kmask3) >> 2); - -#if K_QUANTS_PER_ITERATION == 2 - const uint32_t * q1 = (const uint32_t *)(x[i].qs + q_offset); - const uint32_t * q2 = q1 + 16; - - q32[0] = q1[0] & 0x0f0f0f0f; - q32[1] = q1[0] & 0xf0f0f0f0; - q32[2] = q2[0] & 0x0f0f0f0f; - q32[3] = q2[0] & 0xf0f0f0f0; - - float4 s = {0.f, 0.f, 0.f, 0.f}; - float smin = 0; - for (int l = 0; l < 4; ++l) { - s.x += y1[l] * q4[l+0]; s.y += y1[l+32] * q4[l+ 4]; - s.z += y2[l] * q4[l+8]; s.w += y2[l+32] * q4[l+12]; - smin += y1[l] * sc[2] + y1[l+32] * sc[3] + y2[l] * sc[6] + y2[l+32] * sc[7]; - } - tmp += dall * (s.x * sc[0] + s.y * sc[1] * 1.f/16.f + s.z * sc[4] + s.w * sc[5] * 1.f/16.f) - dmin * smin; -#else - const uint16_t * q1 = (const uint16_t *)(x[i].qs + q_offset); - const uint16_t * q2 = q1 + 32; - - q16[0] = q1[0] & 0x0f0f; - q16[1] = q1[0] & 0xf0f0; - q16[2] = q2[0] & 0x0f0f; - q16[3] = q2[0] & 0xf0f0; - - float4 s = {0.f, 0.f, 0.f, 0.f}; - float smin = 0; - for (int l = 0; l < 2; ++l) { - s.x += y1[l] * q4[l+0]; s.y += y1[l+32] * q4[l+2]; - s.z += y2[l] * q4[l+4]; s.w += y2[l+32] * q4[l+6]; - smin += y1[l] * sc[2] + y1[l+32] * sc[3] + y2[l] * sc[6] + y2[l+32] * sc[7]; - } - tmp += dall * (s.x * sc[0] + s.y * sc[1] * 1.f/16.f + s.z * sc[4] + s.w * sc[5] * 1.f/16.f) - dmin * smin; -#endif - - } -#else - const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...15 - const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); - - const int step = tid * K_QUANTS_PER_ITERATION; - - uint16_t aux16[2]; - const uint8_t * s = (const uint8_t *)aux16; - - float tmp = 0; - - for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { - const uint8_t * q = x[i].qs + step; - const float * y = yy + i*QK_K + step; - const uint16_t * a = (const uint16_t *)x[i].scales; - aux16[0] = a[0] & 0x0f0f; - aux16[1] = (a[0] >> 4) & 0x0f0f; - const float d = (float)x[i].dm[0]; - const float m = (float)x[i].dm[1]; - float sum = 0.f; - for (int j = 0; j < K_QUANTS_PER_ITERATION; ++j) { - sum += y[j+ 0] * (d * s[0] * (q[j+ 0] & 0xF) - m * s[2]) - + y[j+16] * (d * s[0] * (q[j+16] & 0xF) - m * s[2]) - + y[j+32] * (d * s[1] * (q[j+ 0] >> 4) - m * s[3]) - + y[j+48] * (d * s[1] * (q[j+16] >> 4) - m * s[3]); - } - tmp += sum; - } - -#endif - - // sum up partial sums and write back result - tmp = warp_reduce_sum(tmp); - - if (tid == 0) { - dst[row] = tmp; - } -} - -static __global__ void dequantize_mul_mat_vec_q5_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols) { - - const int row = blockIdx.x; - const int num_blocks_per_row = ncols / QK_K; - const int ib0 = row*num_blocks_per_row; - - const block_q5_K * x = (const block_q5_K *)vx + ib0; - - float tmp = 0; // partial sum for thread in warp - -#if QK_K == 256 - const uint16_t kmask1 = 0x3f3f; - const uint16_t kmask2 = 0x0f0f; - const uint16_t kmask3 = 0xc0c0; - - const int tid = threadIdx.x/2; // 0...15 - const int ix = threadIdx.x%2; - - const int il = tid/4; // 0...3 - const int ir = tid - 4*il;// 0...3 - const int n = 2; - - const int im = il/2; // 0 or 1. 0 computes 0,32 + 128,160, 1 computes 64,96 + 192,224 - const int in = il%2; - - const int l0 = n*(2*ir + in); - const int q_offset = 32*im + l0; - const int y_offset = 64*im + l0; - - const uint8_t hm1 = 1 << (2*im); - const uint8_t hm2 = hm1 << 4; - - uint16_t aux[4]; - const uint8_t * sc = (const uint8_t *)aux; - - uint16_t q16[8]; - const uint8_t * q4 = (const uint8_t *)q16; - - for (int i = ix; i < num_blocks_per_row; i += 2) { - - const uint8_t * ql1 = x[i].qs + q_offset; - const uint8_t * qh = x[i].qh + l0; - const float * y1 = yy + i*QK_K + y_offset; - const float * y2 = y1 + 128; - - const float dall = __low2half(x[i].dm); - const float dmin = __high2half(x[i].dm); - - const uint16_t * a = (const uint16_t *)x[i].scales; - aux[0] = a[im+0] & kmask1; - aux[1] = a[im+2] & kmask1; - aux[2] = ((a[im+4] >> 0) & kmask2) | ((a[im+0] & kmask3) >> 2); - aux[3] = ((a[im+4] >> 4) & kmask2) | ((a[im+2] & kmask3) >> 2); - - float4 sum = {0.f, 0.f, 0.f, 0.f}; - float smin = 0; - const uint16_t * q1 = (const uint16_t *)ql1; - const uint16_t * q2 = q1 + 32; - q16[0] = q1[0] & 0x0f0f; - q16[1] = q1[8] & 0x0f0f; - q16[2] = (q1[0] >> 4) & 0x0f0f; - q16[3] = (q1[8] >> 4) & 0x0f0f; - q16[4] = q2[0] & 0x0f0f; - q16[5] = q2[8] & 0x0f0f; - q16[6] = (q2[0] >> 4) & 0x0f0f; - q16[7] = (q2[8] >> 4) & 0x0f0f; - for (int l = 0; l < n; ++l) { - sum.x += y1[l+ 0] * (q4[l +0] + (qh[l+ 0] & (hm1 << 0) ? 16 : 0)) - + y1[l+16] * (q4[l +2] + (qh[l+16] & (hm1 << 0) ? 16 : 0)); - sum.y += y1[l+32] * (q4[l +4] + (qh[l+ 0] & (hm1 << 1) ? 16 : 0)) - + y1[l+48] * (q4[l +6] + (qh[l+16] & (hm1 << 1) ? 16 : 0)); - sum.z += y2[l+ 0] * (q4[l +8] + (qh[l+ 0] & (hm2 << 0) ? 16 : 0)) - + y2[l+16] * (q4[l+10] + (qh[l+16] & (hm2 << 0) ? 16 : 0)); - sum.w += y2[l+32] * (q4[l+12] + (qh[l+ 0] & (hm2 << 1) ? 16 : 0)) - + y2[l+48] * (q4[l+14] + (qh[l+16] & (hm2 << 1) ? 16 : 0)); - smin += (y1[l] + y1[l+16]) * sc[2] + (y1[l+32] + y1[l+48]) * sc[3] - + (y2[l] + y2[l+16]) * sc[6] + (y2[l+32] + y2[l+48]) * sc[7]; - } - tmp += dall * (sum.x * sc[0] + sum.y * sc[1] + sum.z * sc[4] + sum.w * sc[5]) - dmin * smin; - } - -#else - const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...15 - const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); - const int step = tid * K_QUANTS_PER_ITERATION; - const int im = step/8; - const int in = step%8; - - for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { - const uint8_t * q = x[i].qs + step; - const int8_t * s = x[i].scales; - const float * y = yy + i*QK_K + step; - const float d = x[i].d; - float sum = 0.f; - for (int j = 0; j < K_QUANTS_PER_ITERATION; ++j) { - const uint8_t h = x[i].qh[in+j] >> im; - sum += y[j+ 0] * d * s[0] * ((q[j+ 0] & 0xF) - ((h >> 0) & 1 ? 0 : 16)) - + y[j+16] * d * s[1] * ((q[j+16] & 0xF) - ((h >> 2) & 1 ? 0 : 16)) - + y[j+32] * d * s[2] * ((q[j+ 0] >> 4) - ((h >> 4) & 1 ? 0 : 16)) - + y[j+48] * d * s[3] * ((q[j+16] >> 4) - ((h >> 6) & 1 ? 0 : 16)); - } - tmp += sum; - } -#endif - - // sum up partial sums and write back result - tmp = warp_reduce_sum(tmp); - - if (threadIdx.x == 0) { - dst[row] = tmp; - } -} - -static __global__ void dequantize_mul_mat_vec_q6_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols, int nrows) { - - static_assert(16%K_QUANTS_PER_ITERATION == 0, "16 must be divisible by K_QUANTS_PER_ITERATION"); - - const int row = blockIdx.x*blockDim.y + threadIdx.y; - if (row > nrows) return; - - const int num_blocks_per_row = ncols / QK_K; - const int ib0 = row*num_blocks_per_row; - - const block_q6_K * x = (const block_q6_K *)vx + ib0; - -#if QK_K == 256 - - const int tid = threadIdx.x/K_QUANTS_PER_ITERATION; // 0...31 or 0...16 - const int ix = threadIdx.x%K_QUANTS_PER_ITERATION; // 0 or 0, 1 - - const int step = 16/K_QUANTS_PER_ITERATION; // 16 or 8 - - const int im = tid/step; // 0 or 1. 0 computes 0..., 1 computes 128... - const int in = tid - step*im; // 0...15 or 0...7 - -#if K_QUANTS_PER_ITERATION == 1 - const int l0 = K_QUANTS_PER_ITERATION*in; // 0...15 - const int is = 0; -#else - const int l0 = 4 * in; // 0, 4, 8, ..., 28 - const int is = in / 4; -#endif - const int ql_offset = 64*im + l0; - const int qh_offset = 32*im + l0; - const int s_offset = 8*im + is; - const int y_offset = 128*im + l0; - - float tmp = 0; // partial sum for thread in warp - - for (int i = ix; i < num_blocks_per_row; i += K_QUANTS_PER_ITERATION) { - - const float * y = yy + i * QK_K + y_offset; - const uint8_t * ql = x[i].ql + ql_offset; - const uint8_t * qh = x[i].qh + qh_offset; - const int8_t * s = x[i].scales + s_offset; - - const float d = x[i].d; - -#if K_QUANTS_PER_ITERATION == 1 - float sum = y[ 0] * s[0] * d * ((int8_t)((ql[ 0] & 0xF) | ((qh[ 0] & 0x03) << 4)) - 32) - + y[16] * s[1] * d * ((int8_t)((ql[16] & 0xF) | ((qh[16] & 0x03) << 4)) - 32) - + y[32] * s[2] * d * ((int8_t)((ql[32] & 0xF) | ((qh[ 0] & 0x0c) << 2)) - 32) - + y[48] * s[3] * d * ((int8_t)((ql[48] & 0xF) | ((qh[16] & 0x0c) << 2)) - 32) - + y[64] * s[4] * d * ((int8_t)((ql[ 0] >> 4) | ((qh[ 0] & 0x30) >> 0)) - 32) - + y[80] * s[5] * d * ((int8_t)((ql[16] >> 4) | ((qh[16] & 0x30) >> 0)) - 32) - + y[96] * s[6] * d * ((int8_t)((ql[32] >> 4) | ((qh[ 0] & 0xc0) >> 2)) - 32) - +y[112] * s[7] * d * ((int8_t)((ql[48] >> 4) | ((qh[16] & 0xc0) >> 2)) - 32); - tmp += sum; -#else - float sum = 0; - for (int l = 0; l < 4; ++l) { - sum += y[l+ 0] * s[0] * d * ((int8_t)((ql[l+ 0] & 0xF) | (((qh[l] >> 0) & 3) << 4)) - 32) - + y[l+32] * s[2] * d * ((int8_t)((ql[l+32] & 0xF) | (((qh[l] >> 2) & 3) << 4)) - 32) - + y[l+64] * s[4] * d * ((int8_t)((ql[l+ 0] >> 4) | (((qh[l] >> 4) & 3) << 4)) - 32) - + y[l+96] * s[6] * d * ((int8_t)((ql[l+32] >> 4) | (((qh[l] >> 6) & 3) << 4)) - 32); - } - tmp += sum; -#endif - - } - -#else - - const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...7 - const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); // 0...3 - - const int step = tid * K_QUANTS_PER_ITERATION; - - float tmp = 0; // partial sum for thread in warp - - for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { - - const float * y = yy + i * QK_K + step; - const uint8_t * ql = x[i].ql + step; - const uint8_t * qh = x[i].qh + step; - const int8_t * s = x[i].scales; - - const float d = x[i+0].d; - - float sum = 0; - for (int j = 0; j < K_QUANTS_PER_ITERATION; ++j) { - sum += y[j+ 0] * s[0] * d * ((int8_t)((ql[j+ 0] & 0xF) | ((qh[j] & 0x03) << 4)) - 32) - + y[j+16] * s[1] * d * ((int8_t)((ql[j+16] & 0xF) | ((qh[j] & 0x0c) << 2)) - 32) - + y[j+32] * s[2] * d * ((int8_t)((ql[j+ 0] >> 4) | ((qh[j] & 0x30) >> 0)) - 32) - + y[j+48] * s[3] * d * ((int8_t)((ql[j+16] >> 4) | ((qh[j] & 0xc0) >> 2)) - 32); - } - tmp += sum; - - } - -#endif - - // sum up partial sums and write back result - tmp = warp_reduce_sum(tmp); - - if (tid == 0) { - dst[row] = tmp; - } -} - -static __device__ void convert_f16(const void * vx, const int ib, const int iqs, dfloat2 & v){ - const half * x = (const half *) vx; - - // automatic half -> float type cast if dfloat == float - v.x = x[ib + iqs + 0]; - v.y = x[ib + iqs + 1]; -} - -static __global__ void quantize_q8_1(const float * __restrict__ x, void * __restrict__ vy, const int kx, const int kx_padded) { - const int ix = blockDim.x*blockIdx.x + threadIdx.x; - - if (ix >= kx_padded) { - return; - } - - const int iy = blockDim.y*blockIdx.y + threadIdx.y; - - const int i_padded = iy*kx_padded + ix; - - block_q8_1 * y = (block_q8_1 *) vy; - - const int ib = i_padded / QK8_1; // block index - const int iqs = i_padded % QK8_1; // quant index - - const float xi = ix < kx ? x[iy*kx + ix] : 0.0f; - float amax = fabsf(xi); - float sum = xi; - - amax = warp_reduce_max(amax); - sum = warp_reduce_sum(sum); - - const float d = amax / 127; - const int8_t q = amax == 0.0f ? 0 : roundf(xi / d); - - y[ib].qs[iqs] = q; - - if (iqs > 0) { - return; - } - - reinterpret_cast(y[ib].ds.x) = d; - reinterpret_cast(y[ib].ds.y) = sum; -} - -template -static __global__ void k_get_rows( - const void * src0, const int32_t * src1, dst_t * dst, - int64_t ne00, /*int64_t ne01, int64_t ne02, int64_t ne03,*/ - /*int64_t ne10, int64_t ne11,*/ int64_t ne12, /*int64_t ne13,*/ - /*size_t s0,*/ size_t s1, size_t s2, size_t s3, - /*size_t nb00,*/ size_t nb01, size_t nb02, size_t nb03, - size_t s10, size_t s11, size_t s12/*, size_t s13*/) { - - const int i00 = (blockIdx.x*blockDim.x + threadIdx.x)*2; - const int i10 = blockDim.y*blockIdx.y + threadIdx.y; - const int i11 = (blockIdx.z*blockDim.z + threadIdx.z)/ne12; - const int i12 = (blockIdx.z*blockDim.z + threadIdx.z)%ne12; - - if (i00 >= ne00) { - return; - } - - const int i01 = src1[i10*s10 + i11*s11 + i12*s12]; - - dst_t * dst_row = dst + i10*s1 + i11*s2 + i12*s3; - const void * src0_row = (const char *)src0 + i01*nb01 + i11*nb02 + i12*nb03; - - const int ib = i00/qk; // block index - const int iqs = (i00%qk)/qr; // quant index - const int iybs = i00 - i00%qk; // dst block start index - const int y_offset = qr == 1 ? 1 : qk/2; - - // dequantize - dfloat2 v; - dequantize_kernel(src0_row, ib, iqs, v); - - dst_row[iybs + iqs + 0] = v.x; - dst_row[iybs + iqs + y_offset] = v.y; -} - -template -static __global__ void k_get_rows_float( - const src0_t * src0, const int32_t * src1, dst_t * dst, - int64_t ne00, /*int64_t ne01, int64_t ne02, int64_t ne03,*/ - /*int64_t ne10, int64_t ne11,*/ int64_t ne12, /*int64_t ne13,*/ - /*size_t s0,*/ size_t s1, size_t s2, size_t s3, - /*size_t nb00,*/ size_t nb01, size_t nb02, size_t nb03, - size_t s10, size_t s11, size_t s12/*, size_t s13*/) { - - const int i00 = blockIdx.x*blockDim.x + threadIdx.x; - const int i10 = blockDim.y*blockIdx.y + threadIdx.y; - const int i11 = (blockIdx.z*blockDim.z + threadIdx.z)/ne12; - const int i12 = (blockIdx.z*blockDim.z + threadIdx.z)%ne12; - - if (i00 >= ne00) { - return; - } - - const int i01 = src1[i10*s10 + i11*s11 + i12*s12]; - - dst_t * dst_row = dst + i10*s1 + i11*s2 + i12*s3; - const src0_t * src0_row = (const src0_t *)((const char *)src0 + i01*nb01 + i11*nb02 + i12*nb03); - - dst_row[i00] = src0_row[i00]; -} - -template -static __global__ void dequantize_block(const void * __restrict__ vx, dst_t * __restrict__ y, const int k) { - const int i = 2*(blockDim.x*blockIdx.x + threadIdx.x); - - if (i >= k) { - return; - } - - const int ib = i/qk; // block index - const int iqs = (i%qk)/qr; // quant index - const int iybs = i - i%qk; // y block start index - const int y_offset = qr == 1 ? 1 : qk/2; - - // dequantize - dfloat2 v; - dequantize_kernel(vx, ib, iqs, v); - - y[iybs + iqs + 0] = v.x; - y[iybs + iqs + y_offset] = v.y; -} - -template -static __global__ void convert_unary(const void * __restrict__ vx, dst_t * __restrict__ y, const int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - - const src_t * x = (src_t *) vx; - - y[i] = x[i]; -} - -template -static __global__ void dequantize_block_q8_0_f16(const void * __restrict__ vx, half * __restrict__ y, const int k) { -#if __CUDA_ARCH__ >= CC_PASCAL - constexpr int nint = CUDA_Q8_0_NE_ALIGN/sizeof(int) + WARP_SIZE; - - const int i0 = CUDA_Q8_0_NE_ALIGN*blockIdx.x; - const int * x0 = ((int *) vx) + blockIdx.x * nint; - half2 * y2 = (half2 *) (y + i0); - - __shared__ int vals[nint]; - -#pragma unroll - for (int ix0 = 0; ix0 < nint; ix0 += WARP_SIZE) { - if (need_check && i0*sizeof(block_q8_0)/QK8_0 + sizeof(int)*(ix0 + threadIdx.x) >= k*sizeof(block_q8_0)/QK8_0) { - break; - } - - const int ix = ix0 + threadIdx.x; - vals[ix] = x0[ix]; - } - -#pragma unroll - for (int iy = 0; iy < CUDA_Q8_0_NE_ALIGN; iy += 2*WARP_SIZE) { - if (need_check && i0 + iy + 2*threadIdx.x >= k) { - return; - } - - const half * b0 = ((const half *) vals) + (sizeof(block_q8_0)/sizeof(half)) * ((iy + 2*threadIdx.x)/QK8_0); - const half d = *b0; - const char2 qs = ((const char2 *) (b0 + 1))[threadIdx.x % (QK8_0/2)]; - - y2[iy/2 + threadIdx.x] = __hmul2(make_half2(qs.x, qs.y), __half2half2(d)); - } -#else - GGML_UNUSED(vx); - GGML_UNUSED(y); - GGML_UNUSED(k); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_PASCAL -} - -// VDR = vec dot ratio, how many contiguous integers each thread processes when the vec dot kernel is called -// MMVQ = mul_mat_vec_q, MMQ = mul_mat_q - -#define VDR_Q4_0_Q8_1_MMVQ 2 -#define VDR_Q4_0_Q8_1_MMQ 4 - -template static __device__ __forceinline__ float vec_dot_q4_0_q8_1_impl( - const int * v, const int * u, const float & d4, const half2 & ds8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - int sumi = 0; - -#pragma unroll - for (int i = 0; i < vdr; ++i) { - const int vi0 = (v[i] >> 0) & 0x0F0F0F0F; - const int vi1 = (v[i] >> 4) & 0x0F0F0F0F; - - // SIMD dot product of quantized values - sumi = __dp4a(vi0, u[2*i+0], sumi); - sumi = __dp4a(vi1, u[2*i+1], sumi); - } - - const float2 ds8f = __half22float2(ds8); - - // second part effectively subtracts 8 from each quant value - return d4 * (sumi * ds8f.x - (8*vdr/QI4_0) * ds8f.y); -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q4_1_Q8_1_MMVQ 2 -#define VDR_Q4_1_Q8_1_MMQ 4 - -template static __device__ __forceinline__ float vec_dot_q4_1_q8_1_impl( - const int * v, const int * u, const half2 & dm4, const half2 & ds8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - int sumi = 0; - -#pragma unroll - for (int i = 0; i < vdr; ++i) { - const int vi0 = (v[i] >> 0) & 0x0F0F0F0F; - const int vi1 = (v[i] >> 4) & 0x0F0F0F0F; - - // SIMD dot product of quantized values - sumi = __dp4a(vi0, u[2*i+0], sumi); - sumi = __dp4a(vi1, u[2*i+1], sumi); - } - -#ifdef GGML_CUDA_F16 - const float2 tmp = __half22float2(__hmul2(dm4, ds8)); - const float d4d8 = tmp.x; - const float m4s8 = tmp.y; -#else - const float2 dm4f = __half22float2(dm4); - const float2 ds8f = __half22float2(ds8); - const float d4d8 = dm4f.x * ds8f.x; - const float m4s8 = dm4f.y * ds8f.y; -#endif // GGML_CUDA_F16 - - // scale second part of sum by QI8_1/(vdr * QR4_1) to compensate for multiple threads adding it - return sumi * d4d8 + m4s8 / (QI8_1 / (vdr * QR4_1)); -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q5_0_Q8_1_MMVQ 2 -#define VDR_Q5_0_Q8_1_MMQ 4 - -template static __device__ __forceinline__ float vec_dot_q5_0_q8_1_impl( - const int * vl, const int * vh, const int * u, const float & d5, const half2 & ds8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - int sumi = 0; - -#pragma unroll - for (int i = 0; i < vdr; ++i) { - int vi0 = (vl[i] >> 0) & 0x0F0F0F0F; // lower 4 qs bits, still need qh as 5th bits - vi0 |= (vh[i] << 4) & 0x00000010; // 0 -> 4 - vi0 |= (vh[i] << 11) & 0x00001000; // 1 -> 12 - vi0 |= (vh[i] << 18) & 0x00100000; // 2 -> 20 - vi0 |= (vh[i] << 25) & 0x10000000; // 3 -> 28 - sumi = __dp4a(vi0, u[2*i+0], sumi); // SIMD dot product of quantized values - - int vi1 = (vl[i] >> 4) & 0x0F0F0F0F; // upper 4 qs bits, still need qh as 5th bits - vi1 |= (vh[i] >> 12) & 0x00000010; // 16 -> 4 - vi1 |= (vh[i] >> 5) & 0x00001000; // 17 -> 12 - vi1 |= (vh[i] << 2) & 0x00100000; // 18 -> 20 - vi1 |= (vh[i] << 9) & 0x10000000; // 19 -> 28 - sumi = __dp4a(vi1, u[2*i+1], sumi); // SIMD dot product of quantized values - } - - const float2 ds8f = __half22float2(ds8); - - // second part effectively subtracts 16 from each quant value - return d5 * (sumi * ds8f.x - (16*vdr/QI5_0) * ds8f.y); -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q5_1_Q8_1_MMVQ 2 -#define VDR_Q5_1_Q8_1_MMQ 4 - -template static __device__ __forceinline__ float vec_dot_q5_1_q8_1_impl( - const int * vl, const int * vh, const int * u, const half2 & dm5, const half2 & ds8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - int sumi = 0; - -#pragma unroll - for (int i = 0; i < vdr; ++i) { - int vi0 = (vl[i] >> 0) & 0x0F0F0F0F; // lower 4 qs bits, still need qh as 5th bits - vi0 |= (vh[i] << 4) & 0x00000010; // 0 -> 4 - vi0 |= (vh[i] << 11) & 0x00001000; // 1 -> 12 - vi0 |= (vh[i] << 18) & 0x00100000; // 2 -> 20 - vi0 |= (vh[i] << 25) & 0x10000000; // 3 -> 28 - sumi = __dp4a(vi0, u[2*i+0], sumi); // SIMD dot product of quantized values - - int vi1 = (vl[i] >> 4) & 0x0F0F0F0F; // upper 4 qs bits, still need qh as 5th bits - vi1 |= (vh[i] >> 12) & 0x00000010; // 16 -> 4 - vi1 |= (vh[i] >> 5) & 0x00001000; // 17 -> 12 - vi1 |= (vh[i] << 2) & 0x00100000; // 18 -> 20 - vi1 |= (vh[i] << 9) & 0x10000000; // 19 -> 28 - sumi = __dp4a(vi1, u[2*i+1], sumi); // SIMD dot product of quantized values - } - -#ifdef GGML_CUDA_F16 - const float2 tmp = __half22float2(__hmul2(dm5, ds8)); - const float d5d8 = tmp.x; - const float m5s8 = tmp.y; -#else - const float2 dm5f = __half22float2(dm5); - const float2 ds8f = __half22float2(ds8); - const float d5d8 = dm5f.x * ds8f.x; - const float m5s8 = dm5f.y * ds8f.y; -#endif // GGML_CUDA_F16 - - // scale second part of sum by QI5_1 / vdr to compensate for multiple threads adding it - return sumi*d5d8 + m5s8 / (QI5_1 / vdr); - -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q8_0_Q8_1_MMVQ 2 -#define VDR_Q8_0_Q8_1_MMQ 8 - -template static __device__ __forceinline__ float vec_dot_q8_0_q8_1_impl( - const int * v, const int * u, const float & d8_0, const float & d8_1) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - int sumi = 0; - -#pragma unroll - for (int i = 0; i < vdr; ++i) { - // SIMD dot product of quantized values - sumi = __dp4a(v[i], u[i], sumi); - } - - return d8_0*d8_1 * sumi; -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -template static __device__ __forceinline__ float vec_dot_q8_1_q8_1_impl( - const int * v, const int * u, const half2 & dm8, const half2 & ds8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - int sumi = 0; - -#pragma unroll - for (int i = 0; i < vdr; ++i) { - // SIMD dot product of quantized values - sumi = __dp4a(v[i], u[i], sumi); - } - -#ifdef GGML_CUDA_F16 - const float2 tmp = __half22float2(__hmul2(dm8, ds8)); - const float d8d8 = tmp.x; - const float m8s8 = tmp.y; -#else - const float2 dm8f = __half22float2(dm8); - const float2 ds8f = __half22float2(ds8); - const float d8d8 = dm8f.x * ds8f.x; - const float m8s8 = dm8f.y * ds8f.y; -#endif // GGML_CUDA_F16 - - // scale second part of sum by QI8_1/ vdr to compensate for multiple threads adding it - return sumi*d8d8 + m8s8 / (QI8_1 / vdr); -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q2_K_Q8_1_MMVQ 1 -#define VDR_Q2_K_Q8_1_MMQ 2 - -// contiguous v/x values -static __device__ __forceinline__ float vec_dot_q2_K_q8_1_impl_mmvq( - const int & v, const int * __restrict__ u, const uint8_t * __restrict__ scales, - const half2 & dm2, const float * __restrict__ d8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - float sumf_d = 0.0f; - float sumf_m = 0.0f; - -#pragma unroll - for (int i = 0; i < QR2_K; ++i) { - const int sc = scales[2*i]; - - const int vi = (v >> (2*i)) & 0x03030303; - - sumf_d += d8[i] * (__dp4a(vi, u[i], 0) * (sc & 0xF)); // SIMD dot product - - // fill int with 4x m - int m = sc >> 4; - m |= m << 8; - m |= m << 16; - sumf_m += d8[i] * __dp4a(m, u[i], 0); // multiply constant q2_K part with sum of q8_1 values - } - - const float2 dm2f = __half22float2(dm2); - - return dm2f.x*sumf_d - dm2f.y*sumf_m; -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -// contiguous u/y values -static __device__ __forceinline__ float vec_dot_q2_K_q8_1_impl_mmq( - const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ scales, - const half2 & dm2, const float & d8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - int sumi_d = 0; - int sumi_m = 0; - -#pragma unroll - for (int i0 = 0; i0 < QI8_1; i0 += QI8_1/2) { - int sumi_d_sc = 0; - - const int sc = scales[i0 / (QI8_1/2)]; - - // fill int with 4x m - int m = sc >> 4; - m |= m << 8; - m |= m << 16; - -#pragma unroll - for (int i = i0; i < i0 + QI8_1/2; ++i) { - sumi_d_sc = __dp4a(v[i], u[i], sumi_d_sc); // SIMD dot product - sumi_m = __dp4a(m, u[i], sumi_m); // multiply sum of q8_1 values with m - } - - sumi_d += sumi_d_sc * (sc & 0xF); - } - - const float2 dm2f = __half22float2(dm2); - - return d8 * (dm2f.x*sumi_d - dm2f.y*sumi_m); -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q3_K_Q8_1_MMVQ 1 -#define VDR_Q3_K_Q8_1_MMQ 2 - -// contiguous v/x values -static __device__ __forceinline__ float vec_dot_q3_K_q8_1_impl_mmvq( - const int & vl, const int & vh, const int * __restrict__ u, const uint8_t * __restrict__ scales, - const int & scale_offset, const float & d3, const float * __restrict__ d8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - float sumf = 0.0f; - -#pragma unroll - for (int i = 0; i < QR3_K; ++i) { - const int isc = scale_offset + 2*i; - - const int isc_low = isc % (QK_K/32); - const int sc_shift_low = 4 * (isc / (QK_K/32)); - const int sc_low = (scales[isc_low] >> sc_shift_low) & 0xF; - - const int isc_high = isc % (QK_K/64); - const int sc_shift_high = 2 * (isc / (QK_K/64)); - const int sc_high = ((scales[(QK_K/32) + isc_high] >> sc_shift_high) & 3) << 4; - - const int sc = (sc_low | sc_high) - 32; - - const int vil = (vl >> (2*i)) & 0x03030303; - - const int vih = ((vh >> i) << 2) & 0x04040404; - - const int vi = __vsubss4(vil, vih); - - sumf += d8[i] * (__dp4a(vi, u[i], 0) * sc); // SIMD dot product - } - - return d3 * sumf; -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -// contiguous u/y values -static __device__ __forceinline__ float vec_dot_q3_K_q8_1_impl_mmq( - const int * __restrict__ v, const int * __restrict__ u, const int8_t * __restrict__ scales, - const float & d3, const float & d8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - int sumi = 0; - -#pragma unroll - for (int i0 = 0; i0 < QR3_K*VDR_Q3_K_Q8_1_MMQ; i0 += QI8_1/2) { - int sumi_sc = 0; - - for (int i = i0; i < i0 + QI8_1/2; ++i) { - sumi_sc = __dp4a(v[i], u[i], sumi_sc); // SIMD dot product - } - - sumi += sumi_sc * scales[i0 / (QI8_1/2)]; - } - - return d3*d8 * sumi; -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q4_K_Q8_1_MMVQ 2 -#define VDR_Q4_K_Q8_1_MMQ 8 - -// contiguous v/x values -static __device__ __forceinline__ float vec_dot_q4_K_q8_1_impl_vmmq( - const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ sc, - const uint8_t * __restrict__ m, const half2 & dm4, const float * __restrict__ d8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - float sumf_d = 0.0f; - float sumf_m = 0.0f; - -#pragma unroll - for (int i = 0; i < QR4_K; ++i) { - const int v0i = (v[0] >> (4*i)) & 0x0F0F0F0F; - const int v1i = (v[1] >> (4*i)) & 0x0F0F0F0F; - - const int dot1 = __dp4a(v1i, u[2*i+1], __dp4a(v0i, u[2*i+0], 0)); // SIMD dot product - const int dot2 = __dp4a(0x01010101, u[2*i+1], __dp4a(0x01010101, u[2*i+0], 0)); // sum of u - - sumf_d += d8[i] * (dot1 * sc[i]); - sumf_m += d8[i] * (dot2 * m[i]); // multiply constant part of q4_K with sum of q8_1 values - } - - const float2 dm4f = __half22float2(dm4); - - return dm4f.x*sumf_d - dm4f.y*sumf_m; - -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -// contiguous u/y values -static __device__ __forceinline__ float vec_dot_q4_K_q8_1_impl_mmq( - const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ sc, - const uint8_t * __restrict__ m, const half2 & dm4, const half2 * __restrict__ ds8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - float sumf_d = 0.0f; - float sumf_m = 0.0f; - -#pragma unroll - for (int i = 0; i < QR4_K*VDR_Q4_K_Q8_1_MMQ/QI8_1; ++i) { - int sumi_d = 0; - -#pragma unroll - for (int j = 0; j < QI8_1; ++j) { - sumi_d = __dp4a((v[j] >> (4*i)) & 0x0F0F0F0F, u[i*QI8_1 + j], sumi_d); // SIMD dot product - } - - const float2 ds8f = __half22float2(ds8[i]); - - sumf_d += ds8f.x * (sc[i] * sumi_d); - sumf_m += ds8f.y * m[i]; // sum of q8_1 block * q4_K min val - } - - const float2 dm4f = __half22float2(dm4); - - return dm4f.x*sumf_d - dm4f.y*sumf_m; - -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q5_K_Q8_1_MMVQ 2 -#define VDR_Q5_K_Q8_1_MMQ 8 - -// contiguous v/x values -static __device__ __forceinline__ float vec_dot_q5_K_q8_1_impl_vmmq( - const int * __restrict__ vl, const int * __restrict__ vh, const int * __restrict__ u, const uint8_t * __restrict__ sc, - const uint8_t * __restrict__ m, const half2 & dm5, const float * __restrict__ d8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - float sumf_d = 0.0f; - float sumf_m = 0.0f; - -#pragma unroll - for (int i = 0; i < QR5_K; ++i) { - const int vl0i = (vl[0] >> (4*i)) & 0x0F0F0F0F; - const int vl1i = (vl[1] >> (4*i)) & 0x0F0F0F0F; - - const int vh0i = ((vh[0] >> i) << 4) & 0x10101010; - const int vh1i = ((vh[1] >> i) << 4) & 0x10101010; - - const int v0i = vl0i | vh0i; - const int v1i = vl1i | vh1i; - - const int dot1 = __dp4a(v0i, u[2*i+0], __dp4a(v1i, u[2*i+1], 0)); // SIMD dot product - const int dot2 = __dp4a(0x01010101, u[2*i+0], __dp4a(0x01010101, u[2*i+1], 0)); // sum of u - - sumf_d += d8[i] * (dot1 * sc[i]); - sumf_m += d8[i] * (dot2 * m[i]); - - } - - const float2 dm5f = __half22float2(dm5); - - return dm5f.x*sumf_d - dm5f.y*sumf_m; - -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -// contiguous u/y values -static __device__ __forceinline__ float vec_dot_q5_K_q8_1_impl_mmq( - const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ sc, - const uint8_t * __restrict__ m, const half2 & dm4, const half2 * __restrict__ ds8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - float sumf_d = 0.0f; - float sumf_m = 0.0f; - -#pragma unroll - for (int i = 0; i < QR5_K*VDR_Q5_K_Q8_1_MMQ/QI8_1; ++i) { - int sumi_d = 0; - -#pragma unroll - for (int j = 0; j < QI8_1; ++j) { - sumi_d = __dp4a(v[i*QI8_1 + j], u[i*QI8_1 + j], sumi_d); // SIMD dot product - } - - const float2 ds8f = __half22float2(ds8[i]); - - sumf_d += ds8f.x * (sc[i] * sumi_d); - sumf_m += ds8f.y * m[i]; // sum of q8_1 block * q4_K min val - } - - const float2 dm4f = __half22float2(dm4); - - return dm4f.x*sumf_d - dm4f.y*sumf_m; - -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -#define VDR_Q6_K_Q8_1_MMVQ 1 -#define VDR_Q6_K_Q8_1_MMQ 8 - -// contiguous v/x values -static __device__ __forceinline__ float vec_dot_q6_K_q8_1_impl_mmvq( - const int & vl, const int & vh, const int * __restrict__ u, const int8_t * __restrict__ scales, - const float & d, const float * __restrict__ d8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - float sumf = 0.0f; - -#pragma unroll - for (int i = 0; i < QR6_K; ++i) { - const int sc = scales[4*i]; - - const int vil = (vl >> (4*i)) & 0x0F0F0F0F; - - const int vih = ((vh >> (4*i)) << 4) & 0x30303030; - - const int vi = __vsubss4((vil | vih), 0x20202020); // vi = (vil | vih) - 32 - - sumf += d8[i] * (__dp4a(vi, u[i], 0) * sc); // SIMD dot product - } - - return d*sumf; -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -// contiguous u/y values -static __device__ __forceinline__ float vec_dot_q6_K_q8_1_impl_mmq( - const int * __restrict__ v, const int * __restrict__ u, const int8_t * __restrict__ sc, - const float & d6, const float * __restrict__ d8) { - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - float sumf_d = 0.0f; - -#pragma unroll - for (int i0 = 0; i0 < VDR_Q6_K_Q8_1_MMQ; i0 += 4) { - int2 sumi_d = {0, 0}; // 2 q6_K scales per q8_1 scale - -#pragma unroll - for (int i = i0; i < i0 + 2; ++i) { - sumi_d.x = __dp4a(v[2*i+0], u[2*i+0], sumi_d.x); // SIMD dot product - sumi_d.x = __dp4a(v[2*i+1], u[2*i+1], sumi_d.x); // SIMD dot product - - sumi_d.y = __dp4a(v[2*i+4], u[2*i+4], sumi_d.y); // SIMD dot product - sumi_d.y = __dp4a(v[2*i+5], u[2*i+5], sumi_d.y); // SIMD dot product - } - - sumf_d += d8[i0/4] * (sc[i0/2+0]*sumi_d.x + sc[i0/2+1]*sumi_d.y); - } - - return d6 * sumf_d; - -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A -} - -static __device__ __forceinline__ float vec_dot_q4_0_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_q4_0 * bq4_0 = (const block_q4_0 *) vbq; - - int v[VDR_Q4_0_Q8_1_MMVQ]; - int u[2*VDR_Q4_0_Q8_1_MMVQ]; - -#pragma unroll - for (int i = 0; i < VDR_Q4_0_Q8_1_MMVQ; ++i) { - v[i] = get_int_from_uint8(bq4_0->qs, iqs + i); - u[2*i+0] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); - u[2*i+1] = get_int_from_int8_aligned(bq8_1->qs, iqs + i + QI4_0); - } - - return vec_dot_q4_0_q8_1_impl(v, u, bq4_0->d, bq8_1->ds); -} - -template static __device__ __forceinline__ void allocate_tiles_q4_0(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); - GGML_UNUSED(x_sc); - - __shared__ int tile_x_qs[mmq_y * (WARP_SIZE) + mmq_y]; - __shared__ float tile_x_d[mmq_y * (WARP_SIZE/QI4_0) + mmq_y/QI4_0]; - - *x_ql = tile_x_qs; - *x_dm = (half2 *) tile_x_d; -} - -template static __device__ __forceinline__ void load_tiles_q4_0( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI4_0; - const int kqsx = k % QI4_0; - - const block_q4_0 * bx0 = (const block_q4_0 *) vx; - - float * x_dmf = (float *) x_dm; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q4_0 * bxi = bx0 + i*blocks_per_row + kbx; - - x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8(bxi->qs, kqsx); - // x_dmf[i * (WARP_SIZE/QI4_0) + i / QI4_0 + kbx] = bxi->d; - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI4_0; - const int kbxd = k % blocks_per_tile_x_row; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI4_0) { - int i = i0 + i_offset * QI4_0 + k / blocks_per_tile_x_row; - - if (need_check) { - i = min(i, i_max); - } - - const block_q4_0 * bxi = bx0 + i*blocks_per_row + kbxd; - - x_dmf[i * (WARP_SIZE/QI4_0) + i / QI4_0 + kbxd] = bxi->d; - } -} - -static __device__ __forceinline__ float vec_dot_q4_0_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - const int kyqs = k % (QI8_1/2) + QI8_1 * (k / (QI8_1/2)); - const float * x_dmf = (const float *) x_dm; - - int u[2*VDR_Q4_0_Q8_1_MMQ]; - -#pragma unroll - for (int l = 0; l < VDR_Q4_0_Q8_1_MMQ; ++l) { - u[2*l+0] = y_qs[j * WARP_SIZE + (kyqs + l) % WARP_SIZE]; - u[2*l+1] = y_qs[j * WARP_SIZE + (kyqs + l + QI4_0) % WARP_SIZE]; - } - - return vec_dot_q4_0_q8_1_impl - (&x_ql[i * (WARP_SIZE + 1) + k], u, x_dmf[i * (WARP_SIZE/QI4_0) + i/QI4_0 + k/QI4_0], - y_ds[j * (WARP_SIZE/QI8_1) + (2*k/QI8_1) % (WARP_SIZE/QI8_1)]); -} - -static __device__ __forceinline__ float vec_dot_q4_1_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_q4_1 * bq4_1 = (const block_q4_1 *) vbq; - - int v[VDR_Q4_1_Q8_1_MMVQ]; - int u[2*VDR_Q4_1_Q8_1_MMVQ]; - -#pragma unroll - for (int i = 0; i < VDR_Q4_1_Q8_1_MMVQ; ++i) { - v[i] = get_int_from_uint8_aligned(bq4_1->qs, iqs + i); - u[2*i+0] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); - u[2*i+1] = get_int_from_int8_aligned(bq8_1->qs, iqs + i + QI4_1); - } - - return vec_dot_q4_1_q8_1_impl(v, u, bq4_1->dm, bq8_1->ds); -} - -template static __device__ __forceinline__ void allocate_tiles_q4_1(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - __shared__ int tile_x_qs[mmq_y * (WARP_SIZE) + + mmq_y]; - __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI4_1) + mmq_y/QI4_1]; - - *x_ql = tile_x_qs; - *x_dm = tile_x_dm; -} - -template static __device__ __forceinline__ void load_tiles_q4_1( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI4_1; - const int kqsx = k % QI4_1; - - const block_q4_1 * bx0 = (const block_q4_1 *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q4_1 * bxi = bx0 + i*blocks_per_row + kbx; - - x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8_aligned(bxi->qs, kqsx); - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI4_1; - const int kbxd = k % blocks_per_tile_x_row; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI4_1) { - int i = i0 + i_offset * QI4_1 + k / blocks_per_tile_x_row; - - if (need_check) { - i = min(i, i_max); - } - - const block_q4_1 * bxi = bx0 + i*blocks_per_row + kbxd; - - x_dm[i * (WARP_SIZE/QI4_1) + i / QI4_1 + kbxd] = bxi->dm; - } -} - -static __device__ __forceinline__ float vec_dot_q4_1_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - const int kyqs = k % (QI8_1/2) + QI8_1 * (k / (QI8_1/2)); - - int u[2*VDR_Q4_1_Q8_1_MMQ]; - -#pragma unroll - for (int l = 0; l < VDR_Q4_1_Q8_1_MMQ; ++l) { - u[2*l+0] = y_qs[j * WARP_SIZE + (kyqs + l) % WARP_SIZE]; - u[2*l+1] = y_qs[j * WARP_SIZE + (kyqs + l + QI4_1) % WARP_SIZE]; - } - - return vec_dot_q4_1_q8_1_impl - (&x_ql[i * (WARP_SIZE + 1) + k], u, x_dm[i * (WARP_SIZE/QI4_1) + i/QI4_1 + k/QI4_1], - y_ds[j * (WARP_SIZE/QI8_1) + (2*k/QI8_1) % (WARP_SIZE/QI8_1)]); -} - -static __device__ __forceinline__ float vec_dot_q5_0_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_q5_0 * bq5_0 = (const block_q5_0 *) vbq; - - int vl[VDR_Q5_0_Q8_1_MMVQ]; - int vh[VDR_Q5_0_Q8_1_MMVQ]; - int u[2*VDR_Q5_0_Q8_1_MMVQ]; - -#pragma unroll - for (int i = 0; i < VDR_Q5_0_Q8_1_MMVQ; ++i) { - vl[i] = get_int_from_uint8(bq5_0->qs, iqs + i); - vh[i] = get_int_from_uint8(bq5_0->qh, 0) >> (4 * (iqs + i)); - u[2*i+0] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); - u[2*i+1] = get_int_from_int8_aligned(bq8_1->qs, iqs + i + QI5_0); - } - - return vec_dot_q5_0_q8_1_impl(vl, vh, u, bq5_0->d, bq8_1->ds); -} - -template static __device__ __forceinline__ void allocate_tiles_q5_0(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - __shared__ int tile_x_ql[mmq_y * (2*WARP_SIZE) + mmq_y]; - __shared__ float tile_x_d[mmq_y * (WARP_SIZE/QI5_0) + mmq_y/QI5_0]; - - *x_ql = tile_x_ql; - *x_dm = (half2 *) tile_x_d; -} - -template static __device__ __forceinline__ void load_tiles_q5_0( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI5_0; - const int kqsx = k % QI5_0; - - const block_q5_0 * bx0 = (const block_q5_0 *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q5_0 * bxi = bx0 + i*blocks_per_row + kbx; - - const int ql = get_int_from_uint8(bxi->qs, kqsx); - const int qh = get_int_from_uint8(bxi->qh, 0) >> (4 * (k % QI5_0)); - - int qs0 = (ql >> 0) & 0x0F0F0F0F; - qs0 |= (qh << 4) & 0x00000010; // 0 -> 4 - qs0 |= (qh << 11) & 0x00001000; // 1 -> 12 - qs0 |= (qh << 18) & 0x00100000; // 2 -> 20 - qs0 |= (qh << 25) & 0x10000000; // 3 -> 28 - qs0 = __vsubss4(qs0, 0x10101010); // subtract 16 - - x_ql[i * (2*WARP_SIZE + 1) + 2*k+0] = qs0; - - int qs1 = (ql >> 4) & 0x0F0F0F0F; - qs1 |= (qh >> 12) & 0x00000010; // 16 -> 4 - qs1 |= (qh >> 5) & 0x00001000; // 17 -> 12 - qs1 |= (qh << 2) & 0x00100000; // 18 -> 20 - qs1 |= (qh << 9) & 0x10000000; // 19 -> 28 - qs1 = __vsubss4(qs1, 0x10101010); // subtract 16 - - x_ql[i * (2*WARP_SIZE + 1) + 2*k+1] = qs1; - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI5_0; - const int kbxd = k % blocks_per_tile_x_row; - float * x_dmf = (float *) x_dm; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI5_0) { - int i = i0 + i_offset * QI5_0 + k / blocks_per_tile_x_row; - - if (need_check) { - i = min(i, i_max); - } - - const block_q5_0 * bxi = bx0 + i*blocks_per_row + kbxd; - - x_dmf[i * (WARP_SIZE/QI5_0) + i / QI5_0 + kbxd] = bxi->d; - } -} - -static __device__ __forceinline__ float vec_dot_q5_0_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - const int kyqs = k % (QI8_1/2) + QI8_1 * (k / (QI8_1/2)); - const int index_bx = i * (WARP_SIZE/QI5_0) + i/QI5_0 + k/QI5_0; - const float * x_dmf = (const float *) x_dm; - const float * y_df = (const float *) y_ds; - - int u[2*VDR_Q5_0_Q8_1_MMQ]; - -#pragma unroll - for (int l = 0; l < VDR_Q5_0_Q8_1_MMQ; ++l) { - u[2*l+0] = y_qs[j * WARP_SIZE + (kyqs + l) % WARP_SIZE]; - u[2*l+1] = y_qs[j * WARP_SIZE + (kyqs + l + QI5_0) % WARP_SIZE]; - } - - return vec_dot_q8_0_q8_1_impl - (&x_ql[i * (2*WARP_SIZE + 1) + 2 * k], u, x_dmf[index_bx], y_df[j * (WARP_SIZE/QI8_1) + (2*k/QI8_1) % (WARP_SIZE/QI8_1)]); -} - -static __device__ __forceinline__ float vec_dot_q5_1_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_q5_1 * bq5_1 = (const block_q5_1 *) vbq; - - int vl[VDR_Q5_1_Q8_1_MMVQ]; - int vh[VDR_Q5_1_Q8_1_MMVQ]; - int u[2*VDR_Q5_1_Q8_1_MMVQ]; - -#pragma unroll - for (int i = 0; i < VDR_Q5_1_Q8_1_MMVQ; ++i) { - vl[i] = get_int_from_uint8_aligned(bq5_1->qs, iqs + i); - vh[i] = get_int_from_uint8_aligned(bq5_1->qh, 0) >> (4 * (iqs + i)); - u[2*i+0] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); - u[2*i+1] = get_int_from_int8_aligned(bq8_1->qs, iqs + i + QI5_1); - } - - return vec_dot_q5_1_q8_1_impl(vl, vh, u, bq5_1->dm, bq8_1->ds); -} - -template static __device__ __forceinline__ void allocate_tiles_q5_1(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - __shared__ int tile_x_ql[mmq_y * (2*WARP_SIZE) + mmq_y]; - __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI5_1) + mmq_y/QI5_1]; - - *x_ql = tile_x_ql; - *x_dm = tile_x_dm; -} - -template static __device__ __forceinline__ void load_tiles_q5_1( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI5_1; - const int kqsx = k % QI5_1; - - const block_q5_1 * bx0 = (const block_q5_1 *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q5_1 * bxi = bx0 + i*blocks_per_row + kbx; - - const int ql = get_int_from_uint8_aligned(bxi->qs, kqsx); - const int qh = get_int_from_uint8_aligned(bxi->qh, 0) >> (4 * (k % QI5_1)); - - int qs0 = (ql >> 0) & 0x0F0F0F0F; - qs0 |= (qh << 4) & 0x00000010; // 0 -> 4 - qs0 |= (qh << 11) & 0x00001000; // 1 -> 12 - qs0 |= (qh << 18) & 0x00100000; // 2 -> 20 - qs0 |= (qh << 25) & 0x10000000; // 3 -> 28 - - x_ql[i * (2*WARP_SIZE + 1) + 2*k+0] = qs0; - - int qs1 = (ql >> 4) & 0x0F0F0F0F; - qs1 |= (qh >> 12) & 0x00000010; // 16 -> 4 - qs1 |= (qh >> 5) & 0x00001000; // 17 -> 12 - qs1 |= (qh << 2) & 0x00100000; // 18 -> 20 - qs1 |= (qh << 9) & 0x10000000; // 19 -> 28 - - x_ql[i * (2*WARP_SIZE + 1) + 2*k+1] = qs1; - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI5_1; - const int kbxd = k % blocks_per_tile_x_row; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI5_1) { - int i = i0 + i_offset * QI5_1 + k / blocks_per_tile_x_row; - - if (need_check) { - i = min(i, i_max); - } - - const block_q5_1 * bxi = bx0 + i*blocks_per_row + kbxd; - - x_dm[i * (WARP_SIZE/QI5_1) + i / QI5_1 + kbxd] = bxi->dm; - } -} - -static __device__ __forceinline__ float vec_dot_q5_1_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - const int kyqs = k % (QI8_1/2) + QI8_1 * (k / (QI8_1/2)); - const int index_bx = i * (WARP_SIZE/QI5_1) + + i/QI5_1 + k/QI5_1; - - int u[2*VDR_Q5_1_Q8_1_MMQ]; - -#pragma unroll - for (int l = 0; l < VDR_Q5_1_Q8_1_MMQ; ++l) { - u[2*l+0] = y_qs[j * WARP_SIZE + (kyqs + l) % WARP_SIZE]; - u[2*l+1] = y_qs[j * WARP_SIZE + (kyqs + l + QI5_1) % WARP_SIZE]; - } - - return vec_dot_q8_1_q8_1_impl - (&x_ql[i * (2*WARP_SIZE + 1) + 2 * k], u, x_dm[index_bx], y_ds[j * (WARP_SIZE/QI8_1) + (2*k/QI8_1) % (WARP_SIZE/QI8_1)]); -} - -static __device__ __forceinline__ float vec_dot_q8_0_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_q8_0 * bq8_0 = (const block_q8_0 *) vbq; - - int v[VDR_Q8_0_Q8_1_MMVQ]; - int u[VDR_Q8_0_Q8_1_MMVQ]; - -#pragma unroll - for (int i = 0; i < VDR_Q8_0_Q8_1_MMVQ; ++i) { - v[i] = get_int_from_int8(bq8_0->qs, iqs + i); - u[i] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); - } - - return vec_dot_q8_0_q8_1_impl(v, u, bq8_0->d, __low2half(bq8_1->ds)); -} - -template static __device__ __forceinline__ void allocate_tiles_q8_0(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - __shared__ int tile_x_qs[mmq_y * (WARP_SIZE) + mmq_y]; - __shared__ float tile_x_d[mmq_y * (WARP_SIZE/QI8_0) + mmq_y/QI8_0]; - - *x_ql = tile_x_qs; - *x_dm = (half2 *) tile_x_d; -} - -template static __device__ __forceinline__ void load_tiles_q8_0( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI8_0; - const int kqsx = k % QI8_0; - float * x_dmf = (float *) x_dm; - - const block_q8_0 * bx0 = (const block_q8_0 *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q8_0 * bxi = bx0 + i*blocks_per_row + kbx; - - x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_int8(bxi->qs, kqsx); - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI8_0; - const int kbxd = k % blocks_per_tile_x_row; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI8_0) { - int i = i0 + i_offset * QI8_0 + k / blocks_per_tile_x_row; - - if (need_check) { - i = min(i, i_max); - } - - const block_q8_0 * bxi = bx0 + i*blocks_per_row + kbxd; - - x_dmf[i * (WARP_SIZE/QI8_0) + i / QI8_0 + kbxd] = bxi->d; - } -} - -static __device__ __forceinline__ float vec_dot_q8_0_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); - - const float * x_dmf = (const float *) x_dm; - const float * y_df = (const float *) y_ds; - - return vec_dot_q8_0_q8_1_impl - (&x_ql[i * (WARP_SIZE + 1) + k], &y_qs[j * WARP_SIZE + k], x_dmf[i * (WARP_SIZE/QI8_0) + i/QI8_0 + k/QI8_0], - y_df[j * (WARP_SIZE/QI8_1) + k/QI8_1]); -} - -static __device__ __forceinline__ float vec_dot_q2_K_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_q2_K * bq2_K = (const block_q2_K *) vbq; - - const int bq8_offset = QR2_K * (iqs / QI8_1); - const int scale_offset = iqs - iqs % QI8_1 + (iqs % QI8_1) / (QI8_1/2); - - const uint8_t * scales = bq2_K->scales + scale_offset; - - const int v = get_int_from_uint8_aligned(bq2_K->qs, iqs); - int u[QR2_K]; - float d8[QR2_K]; - -#pragma unroll - for (int i = 0; i < QR2_K; ++ i) { - u[i] = get_int_from_int8_aligned(bq8_1[bq8_offset + i].qs, iqs % QI8_1); - d8[i] = __low2float(bq8_1[bq8_offset + i].ds); - } - - return vec_dot_q2_K_q8_1_impl_mmvq(v, u, scales, bq2_K->dm, d8); -} - -template static __device__ __forceinline__ void allocate_tiles_q2_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); - - __shared__ int tile_x_ql[mmq_y * (WARP_SIZE) + mmq_y]; - __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI2_K) + mmq_y/QI2_K]; - __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/4) + mmq_y/4]; - - *x_ql = tile_x_ql; - *x_dm = tile_x_dm; - *x_sc = tile_x_sc; -} - -template static __device__ __forceinline__ void load_tiles_q2_K( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI2_K; - const int kqsx = k % QI2_K; - - const block_q2_K * bx0 = (const block_q2_K *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q2_K * bxi = bx0 + i*blocks_per_row + kbx; - - x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8_aligned(bxi->qs, kqsx); - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI2_K; - const int kbxd = k % blocks_per_tile_x_row; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI2_K) { - int i = (i0 + i_offset * QI2_K + k / blocks_per_tile_x_row) % mmq_y; - - if (need_check) { - i = min(i, i_max); - } - - const block_q2_K * bxi = bx0 + i*blocks_per_row + kbxd; - - x_dm[i * (WARP_SIZE/QI2_K) + i / QI2_K + kbxd] = bxi->dm; - } - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 4) { - int i = i0 + i_offset * 4 + k / (WARP_SIZE/4); - - if (need_check) { - i = min(i, i_max); - } - - const block_q2_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/4)) / (QI2_K/4); - - x_sc[i * (WARP_SIZE/4) + i / 4 + k % (WARP_SIZE/4)] = get_int_from_uint8_aligned(bxi->scales, k % (QI2_K/4)); - } -} - -static __device__ __forceinline__ float vec_dot_q2_K_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); - - const int kbx = k / QI2_K; - const int ky = (k % QI2_K) * QR2_K; - const float * y_df = (const float *) y_ds; - - int v[QR2_K*VDR_Q2_K_Q8_1_MMQ]; - - const int kqsx = i * (WARP_SIZE + 1) + kbx*QI2_K + (QI2_K/2) * (ky/(2*QI2_K)) + ky % (QI2_K/2); - const int shift = 2 * ((ky % (2*QI2_K)) / (QI2_K/2)); - -#pragma unroll - for (int l = 0; l < QR2_K*VDR_Q2_K_Q8_1_MMQ; ++l) { - v[l] = (x_ql[kqsx + l] >> shift) & 0x03030303; - } - - const uint8_t * scales = ((const uint8_t *) &x_sc[i * (WARP_SIZE/4) + i/4 + kbx*4]) + ky/4; - - const int index_y = j * WARP_SIZE + (QR2_K*k) % WARP_SIZE; - return vec_dot_q2_K_q8_1_impl_mmq(v, &y_qs[index_y], scales, x_dm[i * (WARP_SIZE/QI2_K) + i/QI2_K + kbx], y_df[index_y/QI8_1]); -} - -static __device__ __forceinline__ float vec_dot_q3_K_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_q3_K * bq3_K = (const block_q3_K *) vbq; - - const int bq8_offset = QR3_K * (iqs / (QI3_K/2)); - const int scale_offset = iqs - iqs % QI8_1 + (iqs % QI8_1) / (QI8_1/2); - - const float d = bq3_K->d; - - const int vl = get_int_from_uint8(bq3_K->qs, iqs); - - // invert the mask with ~ so that a 0/1 results in 4/0 being subtracted - const int vh = ~get_int_from_uint8(bq3_K->hmask, iqs % (QI3_K/2)) >> bq8_offset; - - int u[QR3_K]; - float d8[QR3_K]; - -#pragma unroll - for (int i = 0; i < QR3_K; ++i) { - u[i] = get_int_from_int8_aligned(bq8_1[bq8_offset + i].qs, iqs % QI8_1); - d8[i] = __low2float(bq8_1[bq8_offset + i].ds); - } - - return vec_dot_q3_K_q8_1_impl_mmvq(vl, vh, u, bq3_K->scales, scale_offset, d, d8); -} - -template static __device__ __forceinline__ void allocate_tiles_q3_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - - __shared__ int tile_x_ql[mmq_y * (WARP_SIZE) + mmq_y]; - __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI3_K) + mmq_y/QI3_K]; - __shared__ int tile_x_qh[mmq_y * (WARP_SIZE/2) + mmq_y/2]; - __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/4) + mmq_y/4]; - - *x_ql = tile_x_ql; - *x_dm = tile_x_dm; - *x_qh = tile_x_qh; - *x_sc = tile_x_sc; -} - -template static __device__ __forceinline__ void load_tiles_q3_K( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI3_K; - const int kqsx = k % QI3_K; - - const block_q3_K * bx0 = (const block_q3_K *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q3_K * bxi = bx0 + i*blocks_per_row + kbx; - - x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8(bxi->qs, kqsx); - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI3_K; - const int kbxd = k % blocks_per_tile_x_row; - float * x_dmf = (float *) x_dm; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI3_K) { - int i = (i0 + i_offset * QI3_K + k / blocks_per_tile_x_row) % mmq_y; - - if (need_check) { - i = min(i, i_max); - } - - const block_q3_K * bxi = bx0 + i*blocks_per_row + kbxd; - - x_dmf[i * (WARP_SIZE/QI3_K) + i / QI3_K + kbxd] = bxi->d; - } - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 2) { - int i = i0 + i_offset * 2 + k / (WARP_SIZE/2); - - if (need_check) { - i = min(i, i_max); - } - - const block_q3_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/2)) / (QI3_K/2); - - // invert the mask with ~ so that a 0/1 results in 4/0 being subtracted - x_qh[i * (WARP_SIZE/2) + i / 2 + k % (WARP_SIZE/2)] = ~get_int_from_uint8(bxi->hmask, k % (QI3_K/2)); - } - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 4) { - int i = i0 + i_offset * 4 + k / (WARP_SIZE/4); - - if (need_check) { - i = min(i, i_max); - } - - const block_q3_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/4)) / (QI3_K/4); - - const int ksc = k % (QI3_K/4); - - const int ksc_low = ksc % (QI3_K/8); - const int shift_low = 4 * (ksc / (QI3_K/8)); - const int sc_low = (get_int_from_uint8(bxi->scales, ksc_low) >> shift_low) & 0x0F0F0F0F; - - const int ksc_high = QI3_K/8; - const int shift_high = 2 * ksc; - const int sc_high = ((get_int_from_uint8(bxi->scales, ksc_high) >> shift_high) << 4) & 0x30303030; - - const int sc = __vsubss4(sc_low | sc_high, 0x20202020); - - x_sc[i * (WARP_SIZE/4) + i / 4 + k % (WARP_SIZE/4)] = sc; - } -} - -static __device__ __forceinline__ float vec_dot_q3_K_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - - const int kbx = k / QI3_K; - const int ky = (k % QI3_K) * QR3_K; - const float * x_dmf = (const float *) x_dm; - const float * y_df = (const float *) y_ds; - - const int8_t * scales = ((const int8_t *) (x_sc + i * (WARP_SIZE/4) + i/4 + kbx*4)) + ky/4; - - int v[QR3_K*VDR_Q3_K_Q8_1_MMQ]; - -#pragma unroll - for (int l = 0; l < QR3_K*VDR_Q3_K_Q8_1_MMQ; ++l) { - const int kqsx = i * (WARP_SIZE + 1) + kbx*QI3_K + (QI3_K/2) * (ky/(2*QI3_K)) + ky % (QI3_K/2); - const int shift = 2 * ((ky % 32) / 8); - const int vll = (x_ql[kqsx + l] >> shift) & 0x03030303; - - const int vh = x_qh[i * (WARP_SIZE/2) + i/2 + kbx * (QI3_K/2) + (ky+l)%8] >> ((ky+l) / 8); - const int vlh = (vh << 2) & 0x04040404; - - v[l] = __vsubss4(vll, vlh); - } - - const int index_y = j * WARP_SIZE + (k*QR3_K) % WARP_SIZE; - return vec_dot_q3_K_q8_1_impl_mmq(v, &y_qs[index_y], scales, x_dmf[i * (WARP_SIZE/QI3_K) + i/QI3_K + kbx], y_df[index_y/QI8_1]); -} - -static __device__ __forceinline__ float vec_dot_q4_K_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - -#ifndef GGML_QKK_64 - const block_q4_K * bq4_K = (const block_q4_K *) vbq; - - int v[2]; - int u[2*QR4_K]; - float d8[QR4_K]; - - // iqs is in 0,2..30. bq8_offset = iqs/4 -> bq8_offset = 0, 2, 4, 6 - const int bq8_offset = QR4_K * ((iqs/2) / (QI8_1/2)); - - // iqs = 0....3 -> bq8_offset = 0, want q4_offset = 0, 4, 8, 12 - // iqs = 4....7 -> bq8_offset = 2, want q4_offset = 32, 36, 40, 44 - // iqs = 8...11 -> bq8_offset = 4, want q4_offset = 64, 68, 72, 76 - // iqs = 12..15 -> bq8_offset = 6, want q4_offset = 96, 100, 104, 108 - - const int * q4 = (const int *)(bq4_K->qs + 16 * bq8_offset + 4 * ((iqs/2)%4)); - v[0] = q4[0]; - v[1] = q4[4]; - - const uint16_t * scales = (const uint16_t *)bq4_K->scales; - uint16_t aux[2]; - const int j = bq8_offset/2; - if (j < 2) { - aux[0] = scales[j+0] & 0x3f3f; - aux[1] = scales[j+2] & 0x3f3f; - } else { - aux[0] = ((scales[j+2] >> 0) & 0x0f0f) | ((scales[j-2] & 0xc0c0) >> 2); - aux[1] = ((scales[j+2] >> 4) & 0x0f0f) | ((scales[j-0] & 0xc0c0) >> 2); - } - const uint8_t * sc = (const uint8_t *)aux; - const uint8_t * m = sc + 2; - - for (int i = 0; i < QR4_K; ++i) { - const block_q8_1 * bq8i = bq8_1 + bq8_offset + i; - d8[i] = __low2float(bq8i->ds); - - const int * q8 = (const int *)bq8i->qs + ((iqs/2)%4); - u[2*i+0] = q8[0]; - u[2*i+1] = q8[4]; - } - - return vec_dot_q4_K_q8_1_impl_vmmq(v, u, sc, m, bq4_K->dm, d8); - -#else - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - const block_q4_K * bq4_K = (const block_q4_K *) vbq; - - float sumf_d = 0.0f; - float sumf_m = 0.0f; - - uint16_t aux16[2]; - const uint8_t * s = (const uint8_t *)aux16; - - const uint16_t * a = (const uint16_t *)bq4_K->scales; - aux16[0] = a[0] & 0x0f0f; - aux16[1] = (a[0] >> 4) & 0x0f0f; - - const float dall = bq4_K->dm[0]; - const float dmin = bq4_K->dm[1]; - - const float d8_1 = __low2float(bq8_1[0].ds); - const float d8_2 = __low2float(bq8_1[1].ds); - - const int ui1 = *((const int *)bq8_1[0].qs + (iqs/2)); - const int ui2 = *((const int *)bq8_1[0].qs + (iqs/2) + 4); - const int ui3 = *((const int *)bq8_1[1].qs + (iqs/2)); - const int ui4 = *((const int *)bq8_1[1].qs + (iqs/2) + 4); - - const int * q4 = (const int *)bq4_K->qs + (iqs/2); - const int v1 = q4[0]; - const int v2 = q4[4]; - - const int dot1 = __dp4a(ui2, v2 & 0x0f0f0f0f, __dp4a(ui1, v1 & 0x0f0f0f0f, 0)); - const int dot2 = __dp4a(ui4, (v2 >> 4) & 0x0f0f0f0f, __dp4a(ui3, (v1 >> 4) & 0x0f0f0f0f, 0)); - const int dot3 = __dp4a(0x01010101, ui2, __dp4a(0x01010101, ui1, 0)); - const int dot4 = __dp4a(0x01010101, ui4, __dp4a(0x01010101, ui3, 0)); - - sumf_d += d8_1 * (dot1 * s[0]) + d8_2 * (dot2 * s[1]); - sumf_m += d8_1 * (dot3 * s[2]) + d8_2 * (dot4 * s[3]); - - return dall * sumf_d - dmin * sumf_m; - -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A - -#endif -} - -template static __device__ __forceinline__ void allocate_tiles_q4_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); - - __shared__ int tile_x_ql[mmq_y * (WARP_SIZE) + mmq_y]; - __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI4_K) + mmq_y/QI4_K]; - __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/8) + mmq_y/8]; - - *x_ql = tile_x_ql; - *x_dm = tile_x_dm; - *x_sc = tile_x_sc; -} - -template static __device__ __forceinline__ void load_tiles_q4_K( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI4_K; // == 0 if QK_K == 256 - const int kqsx = k % QI4_K; // == k if QK_K == 256 - - const block_q4_K * bx0 = (const block_q4_K *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q4_K * bxi = bx0 + i*blocks_per_row + kbx; - - x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8_aligned(bxi->qs, kqsx); - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI4_K; // == 1 if QK_K == 256 - const int kbxd = k % blocks_per_tile_x_row; // == 0 if QK_K == 256 - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI4_K) { - int i = (i0 + i_offset * QI4_K + k / blocks_per_tile_x_row) % mmq_y; - - if (need_check) { - i = min(i, i_max); - } - - const block_q4_K * bxi = bx0 + i*blocks_per_row + kbxd; - -#if QK_K == 256 - x_dm[i * (WARP_SIZE/QI4_K) + i / QI4_K + kbxd] = bxi->dm; -#else - x_dm[i * (WARP_SIZE/QI4_K) + i / QI4_K + kbxd] = {bxi->dm[0], bxi->dm[1]}; -#endif - } - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 8) { - int i = (i0 + i_offset * 8 + k / (WARP_SIZE/8)) % mmq_y; - - if (need_check) { - i = min(i, i_max); - } - - const block_q4_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/8)) / (QI4_K/8); - - const int * scales = (const int *) bxi->scales; - - const int ksc = k % (WARP_SIZE/8); - - // scale arrangement after the following two lines: sc0,...,sc3, sc4,...,sc7, m0,...,m3, m4,...,m8 - int scales8 = (scales[(ksc%2) + (ksc!=0)] >> (4 * (ksc & (ksc/2)))) & 0x0F0F0F0F; // lower 4 bits - scales8 |= (scales[ksc/2] >> (2 * (ksc % 2))) & 0x30303030; // upper 2 bits - - x_sc[i * (WARP_SIZE/8) + i / 8 + ksc] = scales8; - } -} - -static __device__ __forceinline__ float vec_dot_q4_K_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); - - const uint8_t * sc = ((const uint8_t *) &x_sc[i * (WARP_SIZE/8) + i/8 + k/16]) + 2*((k % 16) / 8); - - const int index_y = j * WARP_SIZE + (QR4_K*k) % WARP_SIZE; - return vec_dot_q4_K_q8_1_impl_mmq(&x_ql[i * (WARP_SIZE + 1) + k], &y_qs[index_y], sc, sc+8, - x_dm[i * (WARP_SIZE/QI4_K) + i/QI4_K], &y_ds[index_y/QI8_1]); -} - -static __device__ __forceinline__ float vec_dot_q5_K_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - -#ifndef GGML_QKK_64 - const block_q5_K * bq5_K = (const block_q5_K *) vbq; - - int vl[2]; - int vh[2]; - int u[2*QR5_K]; - float d8[QR5_K]; - - const int bq8_offset = QR5_K * ((iqs/2) / (QI8_1/2)); - const int * ql = (const int *)(bq5_K->qs + 16 * bq8_offset + 4 * ((iqs/2)%4)); - const int * qh = (const int *)(bq5_K->qh + 4 * ((iqs/2)%4)); - - vl[0] = ql[0]; - vl[1] = ql[4]; - - vh[0] = qh[0] >> bq8_offset; - vh[1] = qh[4] >> bq8_offset; - - const uint16_t * scales = (const uint16_t *)bq5_K->scales; - uint16_t aux[2]; - const int j = bq8_offset/2; - if (j < 2) { - aux[0] = scales[j+0] & 0x3f3f; - aux[1] = scales[j+2] & 0x3f3f; - } else { - aux[0] = ((scales[j+2] >> 0) & 0x0f0f) | ((scales[j-2] & 0xc0c0) >> 2); - aux[1] = ((scales[j+2] >> 4) & 0x0f0f) | ((scales[j-0] & 0xc0c0) >> 2); - } - const uint8_t * sc = (const uint8_t *)aux; - const uint8_t * m = sc + 2; - -#pragma unroll - for (int i = 0; i < QR5_K; ++i) { - const block_q8_1 * bq8i = bq8_1 + bq8_offset + i; - d8[i] = __low2float(bq8i->ds); - - const int * q8 = (const int *)bq8i->qs + ((iqs/2)%4); - u[2*i+0] = q8[0]; - u[2*i+1] = q8[4]; - } - - return vec_dot_q5_K_q8_1_impl_vmmq(vl, vh, u, sc, m, bq5_K->dm, d8); - -#else - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - const block_q5_K * bq5_K = (const block_q5_K *) vbq; - - const int8_t * s = bq5_K->scales; - - const float d = bq5_K->d; - - const float d8_1 = __low2half(bq8_1[0].ds); - const float d8_2 = __low2half(bq8_1[1].ds); - - const int ui1 = *((const int *)bq8_1[0].qs + (iqs/2)); - const int ui2 = *((const int *)bq8_1[0].qs + (iqs/2) + 4); - const int ui3 = *((const int *)bq8_1[1].qs + (iqs/2)); - const int ui4 = *((const int *)bq8_1[1].qs + (iqs/2) + 4); - - const int * ql = (const int *)bq5_K->qs + (iqs/2); - const int vl1 = ql[0]; - const int vl2 = ql[4]; - - const int step = 4 * (iqs/2); // 0, 4, 8, 12 - const int im = step/8; // = 0 for iqs = 0, 2, = 1 for iqs = 4, 6 - const int in = step%8; // 0, 4, 0, 4 - const int vh = (*((const int *)(bq5_K->qh + in))) >> im; - - const int v1 = (((vh << 4) & 0x10101010) ^ 0x10101010) | ((vl1 >> 0) & 0x0f0f0f0f); - const int v2 = (((vh << 2) & 0x10101010) ^ 0x10101010) | ((vl2 >> 0) & 0x0f0f0f0f); - const int v3 = (((vh >> 0) & 0x10101010) ^ 0x10101010) | ((vl1 >> 4) & 0x0f0f0f0f); - const int v4 = (((vh >> 2) & 0x10101010) ^ 0x10101010) | ((vl2 >> 4) & 0x0f0f0f0f); - - const float sumf_d = d8_1 * (__dp4a(ui1, v1, 0) * s[0] + __dp4a(ui2, v2, 0) * s[1]) - + d8_2 * (__dp4a(ui3, v3, 0) * s[2] + __dp4a(ui4, v4, 0) * s[3]); - - return d * sumf_d; - -#else - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= MIN_CC_DP4A - -#endif -} - -template static __device__ __forceinline__ void allocate_tiles_q5_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); - - __shared__ int tile_x_ql[mmq_y * (2*WARP_SIZE) + mmq_y]; - __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI5_K) + mmq_y/QI5_K]; - __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/8) + mmq_y/8]; - - *x_ql = tile_x_ql; - *x_dm = tile_x_dm; - *x_sc = tile_x_sc; -} - -template static __device__ __forceinline__ void load_tiles_q5_K( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI5_K; // == 0 if QK_K == 256 - const int kqsx = k % QI5_K; // == k if QK_K == 256 - - const block_q5_K * bx0 = (const block_q5_K *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q5_K * bxi = bx0 + i*blocks_per_row + kbx; - const int ky = QR5_K*kqsx; - - const int ql = get_int_from_uint8_aligned(bxi->qs, kqsx); - const int ql0 = (ql >> 0) & 0x0F0F0F0F; - const int ql1 = (ql >> 4) & 0x0F0F0F0F; - - const int qh = get_int_from_uint8_aligned(bxi->qh, kqsx % (QI5_K/4)); - const int qh0 = ((qh >> (2 * (kqsx / (QI5_K/4)) + 0)) << 4) & 0x10101010; - const int qh1 = ((qh >> (2 * (kqsx / (QI5_K/4)) + 1)) << 4) & 0x10101010; - - const int kq0 = ky - ky % (QI5_K/2) + k % (QI5_K/4) + 0; - const int kq1 = ky - ky % (QI5_K/2) + k % (QI5_K/4) + (QI5_K/4); - - x_ql[i * (2*WARP_SIZE + 1) + kq0] = ql0 | qh0; - x_ql[i * (2*WARP_SIZE + 1) + kq1] = ql1 | qh1; - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI5_K; // == 1 if QK_K == 256 - const int kbxd = k % blocks_per_tile_x_row; // == 0 if QK_K == 256 - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI5_K) { - int i = (i0 + i_offset * QI5_K + k / blocks_per_tile_x_row) % mmq_y; - - if (need_check) { - i = min(i, i_max); - } - - const block_q5_K * bxi = bx0 + i*blocks_per_row + kbxd; - -#if QK_K == 256 - x_dm[i * (WARP_SIZE/QI5_K) + i / QI5_K + kbxd] = bxi->dm; -#endif - } - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 8) { - int i = (i0 + i_offset * 8 + k / (WARP_SIZE/8)) % mmq_y; - - if (need_check) { - i = min(i, i_max); - } - - const block_q5_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/8)) / (QI5_K/8); - - const int * scales = (const int *) bxi->scales; - - const int ksc = k % (WARP_SIZE/8); - - // scale arrangement after the following two lines: sc0,...,sc3, sc4,...,sc7, m0,...,m3, m4,...,m8 - int scales8 = (scales[(ksc%2) + (ksc!=0)] >> (4 * (ksc & (ksc/2)))) & 0x0F0F0F0F; // lower 4 bits - scales8 |= (scales[ksc/2] >> (2 * (ksc % 2))) & 0x30303030; // upper 2 bits - - x_sc[i * (WARP_SIZE/8) + i / 8 + ksc] = scales8; - } -} - -static __device__ __forceinline__ float vec_dot_q5_K_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); - - const uint8_t * sc = ((const uint8_t *) &x_sc[i * (WARP_SIZE/8) + i/8 + k/16]) + 2 * ((k % 16) / 8); - - const int index_x = i * (QR5_K*WARP_SIZE + 1) + QR5_K*k; - const int index_y = j * WARP_SIZE + (QR5_K*k) % WARP_SIZE; - return vec_dot_q5_K_q8_1_impl_mmq(&x_ql[index_x], &y_qs[index_y], sc, sc+8, - x_dm[i * (WARP_SIZE/QI5_K) + i/QI5_K], &y_ds[index_y/QI8_1]); -} - -static __device__ __forceinline__ float vec_dot_q6_K_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_q6_K * bq6_K = (const block_q6_K *) vbq; - - const int bq8_offset = 2 * QR6_K * (iqs / (QI6_K/2)) + (iqs % (QI6_K/2)) / (QI6_K/4); - const int scale_offset = (QI6_K/4) * (iqs / (QI6_K/2)) + (iqs % (QI6_K/2)) / (QI6_K/8); - const int vh_shift = 2 * ((iqs % (QI6_K/2)) / (QI6_K/4)); - - const int vl = get_int_from_uint8(bq6_K->ql, iqs); - const int vh = get_int_from_uint8(bq6_K->qh, (QI6_K/4) * (iqs / (QI6_K/2)) + iqs % (QI6_K/4)) >> vh_shift; - - const int8_t * scales = bq6_K->scales + scale_offset; - - int u[QR6_K]; - float d8[QR6_K]; - -#pragma unroll - for (int i = 0; i < QR6_K; ++i) { - u[i] = get_int_from_int8_aligned(bq8_1[bq8_offset + 2*i].qs, iqs % QI8_1); - d8[i] = __low2float(bq8_1[bq8_offset + 2*i].ds); - } - - return vec_dot_q6_K_q8_1_impl_mmvq(vl, vh, u, scales, bq6_K->d, d8); -} - -template static __device__ __forceinline__ void allocate_tiles_q6_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { - GGML_UNUSED(x_qh); - - __shared__ int tile_x_ql[mmq_y * (2*WARP_SIZE) + mmq_y]; - __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI6_K) + mmq_y/QI6_K]; - __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/8) + mmq_y/8]; - - *x_ql = tile_x_ql; - *x_dm = tile_x_dm; - *x_sc = tile_x_sc; -} - -template static __device__ __forceinline__ void load_tiles_q6_K( - const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, - int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { - GGML_UNUSED(x_qh); - - GGML_CUDA_ASSUME(i_offset >= 0); - GGML_CUDA_ASSUME(i_offset < nwarps); - GGML_CUDA_ASSUME(k >= 0); - GGML_CUDA_ASSUME(k < WARP_SIZE); - - const int kbx = k / QI6_K; // == 0 if QK_K == 256 - const int kqsx = k % QI6_K; // == k if QK_K == 256 - - const block_q6_K * bx0 = (const block_q6_K *) vx; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { - int i = i0 + i_offset; - - if (need_check) { - i = min(i, i_max); - } - - const block_q6_K * bxi = bx0 + i*blocks_per_row + kbx; - const int ky = QR6_K*kqsx; - - const int ql = get_int_from_uint8(bxi->ql, kqsx); - const int ql0 = (ql >> 0) & 0x0F0F0F0F; - const int ql1 = (ql >> 4) & 0x0F0F0F0F; - - const int qh = get_int_from_uint8(bxi->qh, (QI6_K/4) * (kqsx / (QI6_K/2)) + kqsx % (QI6_K/4)); - const int qh0 = ((qh >> (2 * ((kqsx % (QI6_K/2)) / (QI6_K/4)))) << 4) & 0x30303030; - const int qh1 = (qh >> (2 * ((kqsx % (QI6_K/2)) / (QI6_K/4)))) & 0x30303030; - - const int kq0 = ky - ky % QI6_K + k % (QI6_K/2) + 0; - const int kq1 = ky - ky % QI6_K + k % (QI6_K/2) + (QI6_K/2); - - x_ql[i * (2*WARP_SIZE + 1) + kq0] = __vsubss4(ql0 | qh0, 0x20202020); - x_ql[i * (2*WARP_SIZE + 1) + kq1] = __vsubss4(ql1 | qh1, 0x20202020); - } - - const int blocks_per_tile_x_row = WARP_SIZE / QI6_K; // == 1 if QK_K == 256 - const int kbxd = k % blocks_per_tile_x_row; // == 0 if QK_K == 256 - float * x_dmf = (float *) x_dm; - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI6_K) { - int i = (i0 + i_offset * QI6_K + k / blocks_per_tile_x_row) % mmq_y; - - if (need_check) { - i = min(i, i_max); - } - - const block_q6_K * bxi = bx0 + i*blocks_per_row + kbxd; - - x_dmf[i * (WARP_SIZE/QI6_K) + i / QI6_K + kbxd] = bxi->d; - } - -#pragma unroll - for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 8) { - int i = (i0 + i_offset * 8 + k / (WARP_SIZE/8)) % mmq_y; - - if (need_check) { - i = min(i, i_max); - } - - const block_q6_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/8)) / 4; - - x_sc[i * (WARP_SIZE/8) + i / 8 + k % (WARP_SIZE/8)] = get_int_from_int8(bxi->scales, k % (QI6_K/8)); - } -} - -static __device__ __forceinline__ float vec_dot_q6_K_q8_1_mul_mat( - const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, - const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { - GGML_UNUSED(x_qh); - - const float * x_dmf = (const float *) x_dm; - const float * y_df = (const float *) y_ds; - - const int8_t * sc = ((const int8_t *) &x_sc[i * (WARP_SIZE/8) + i/8 + k/8]); - - const int index_x = i * (QR6_K*WARP_SIZE + 1) + QR6_K*k; - const int index_y = j * WARP_SIZE + (QR6_K*k) % WARP_SIZE; - return vec_dot_q6_K_q8_1_impl_mmq(&x_ql[index_x], &y_qs[index_y], sc, x_dmf[i * (WARP_SIZE/QI6_K) + i/QI6_K], &y_df[index_y/QI8_1]); -} - -static __device__ __forceinline__ float vec_dot_iq2_xxs_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { -#if QK_K == 256 - const block_iq2_xxs * bq2 = (const block_iq2_xxs *) vbq; - -#if QR2_XXS == 8 - const int ib32 = iqs; - const uint16_t * q2 = bq2->qs + 4*ib32; - const uint8_t * aux8 = (const uint8_t *)q2; - const int8_t * q8 = bq8_1[ib32].qs; - uint32_t aux32 = q2[2] | (q2[3] << 16); - int sumi = 0; - for (int l = 0; l < 4; ++l) { - const uint8_t * grid = (const uint8_t *)(iq2xxs_grid + aux8[l]); - const uint8_t signs = ksigns_iq2xs[aux32 & 127]; - for (int j = 0; j < 8; ++j) { - sumi += q8[j] * grid[j] * (signs & kmask_iq2xs[j] ? -1 : 1); - } - q8 += 8; - aux32 >>= 7; - } - const float d = (float)bq2->d * (0.5f + aux32) * __low2float(bq8_1[ib32].ds) * 0.25f; - return d * sumi; -#else - // iqs is 0...15 - const int ib32 = iqs/2; - const int il = iqs%2; - const uint16_t * q2 = bq2->qs + 4*ib32; - const uint8_t * aux8 = (const uint8_t *)q2; - const uint8_t * grid1 = (const uint8_t *)(iq2xxs_grid + aux8[2*il+0]); - const uint8_t * grid2 = (const uint8_t *)(iq2xxs_grid + aux8[2*il+1]); - const uint32_t aux32 = q2[2] | (q2[3] << 16); - const float d = (float)bq2->d * (0.5f + (aux32 >> 28)) * __low2float(bq8_1[ib32].ds) * 0.25f; - const uint8_t signs1 = ksigns_iq2xs[(aux32 >> 14*il) & 127]; - const uint8_t signs2 = ksigns_iq2xs[(aux32 >> (14*il + 7)) & 127]; - const int8_t * q8 = bq8_1[ib32].qs + 16*il; - int sumi1 = 0, sumi2 = 0; - for (int j = 0; j < 8; ++j) { - sumi1 += q8[j+0] * grid1[j] * (signs1 & kmask_iq2xs[j] ? -1 : 1); - sumi2 += q8[j+8] * grid2[j] * (signs2 & kmask_iq2xs[j] ? -1 : 1); - } - return d * (sumi1 + sumi2); -#endif -#else - assert(false); - return 0.f; -#endif -} - -static __device__ __forceinline__ float vec_dot_iq2_xs_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics -#if QK_K == 256 - const block_iq2_xs * bq2 = (const block_iq2_xs *) vbq; - - const int ib32 = iqs; - const uint16_t * q2 = bq2->qs + 4*ib32; - const int8_t * q8 = bq8_1[ib32].qs; - const uint8_t ls1 = bq2->scales[ib32] & 0xf; - const uint8_t ls2 = bq2->scales[ib32] >> 4; - int sumi1 = 0; - for (int l = 0; l < 2; ++l) { - const uint32_t * grid = (const uint32_t *)(iq2xs_grid + (q2[l] & 511)); - const uint32_t * signs = (const uint32_t *)(ksigns64 + (q2[l] >> 9)); - const int grid_l = __vsub4(grid[0] ^ signs[0], signs[0]); - const int grid_h = __vsub4(grid[1] ^ signs[1], signs[1]); - sumi1 = __dp4a(grid_l, *((const int *)q8 + 0), sumi1); - sumi1 = __dp4a(grid_h, *((const int *)q8 + 1), sumi1); - q8 += 8; - } - int sumi2 = 0; - for (int l = 2; l < 4; ++l) { - const uint32_t * grid = (const uint32_t *)(iq2xs_grid + (q2[l] & 511)); - const uint32_t * signs = (const uint32_t *)(ksigns64 + (q2[l] >> 9)); - const int grid_l = __vsub4(grid[0] ^ signs[0], signs[0]); - const int grid_h = __vsub4(grid[1] ^ signs[1], signs[1]); - sumi2 = __dp4a(grid_l, *((const int *)q8 + 0), sumi2); - sumi2 = __dp4a(grid_h, *((const int *)q8 + 1), sumi2); - q8 += 8; - } - const float d = (float)bq2->d * __low2float(bq8_1[ib32].ds) * 0.25f; - return d * ((0.5f + ls1) * sumi1 + (0.5f + ls2) * sumi2); -#else - GGML_UNUSED(ksigns64); - assert(false); - return 0.f; -#endif -#else - GGML_UNUSED(ksigns64); - assert(false); - return 0.f; -#endif -} - -// TODO -static __device__ __forceinline__ float vec_dot_iq2_s_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics -#if QK_K == 256 - const block_iq2_s * bq2 = (const block_iq2_s *) vbq; - - const int ib32 = iqs; - const int8_t * q8 = bq8_1[ib32].qs; - const uint8_t * signs = bq2->qs + QK_K/8 + 4*ib32; - const uint8_t ls1 = bq2->scales[ib32] & 0xf; - const uint8_t ls2 = bq2->scales[ib32] >> 4; - int sumi1 = 0; - for (int l = 0; l < 2; ++l) { - const uint32_t * grid = (const uint32_t *)(iq2s_grid + (bq2->qs[4*ib32+l] | ((bq2->qh[ib32] << (8-2*l)) & 0x300))); - const uint32_t signs0 = __vcmpeq4(((signs[l] & 0xf) * 0x01010101) & 0x08040201, 0x08040201); - const uint32_t signs1 = __vcmpeq4(((signs[l] >> 4) * 0x01010101) & 0x08040201, 0x08040201); - const int grid_l = __vsub4(grid[0] ^ signs0, signs0); - const int grid_h = __vsub4(grid[1] ^ signs1, signs1); - sumi1 = __dp4a(grid_l, *((const int *)q8 + 0), sumi1); - sumi1 = __dp4a(grid_h, *((const int *)q8 + 1), sumi1); - q8 += 8; - } - int sumi2 = 0; - for (int l = 2; l < 4; ++l) { - const uint32_t * grid = (const uint32_t *)(iq2s_grid + (bq2->qs[4*ib32+l] | ((bq2->qh[ib32] << (8-2*l)) & 0x300))); - const uint32_t signs0 = __vcmpeq4(((signs[l] & 0xf) * 0x01010101) & 0x08040201, 0x08040201); - const uint32_t signs1 = __vcmpeq4(((signs[l] >> 4) * 0x01010101) & 0x08040201, 0x08040201); - const int grid_l = __vsub4(grid[0] ^ signs0, signs0); - const int grid_h = __vsub4(grid[1] ^ signs1, signs1); - sumi2 = __dp4a(grid_l, *((const int *)q8 + 0), sumi2); - sumi2 = __dp4a(grid_h, *((const int *)q8 + 1), sumi2); - q8 += 8; - } - const float d = (float)bq2->d * __low2float(bq8_1[ib32].ds) * 0.25f; - return d * ((0.5f + ls1) * sumi1 + (0.5f + ls2) * sumi2); -#else - GGML_UNUSED(ksigns64); - assert(false); - return 0.f; -#endif -#else - GGML_UNUSED(ksigns64); - assert(false); - return 0.f; -#endif -} - -static __device__ __forceinline__ float vec_dot_iq3_xxs_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics -#if QK_K == 256 - const block_iq3_xxs * bq2 = (const block_iq3_xxs *) vbq; - - const int ib32 = iqs; - const uint8_t * q3 = bq2->qs + 8*ib32; - const uint16_t * gas = (const uint16_t *)(bq2->qs + QK_K/4) + 2*ib32; - const int8_t * q8 = bq8_1[ib32].qs; - uint32_t aux32 = gas[0] | (gas[1] << 16); - int sumi = 0; - for (int l = 0; l < 4; ++l) { - const uint32_t * grid1 = iq3xxs_grid + q3[2*l+0]; - const uint32_t * grid2 = iq3xxs_grid + q3[2*l+1]; - const uint32_t * signs = (const uint32_t *)(ksigns64 + (aux32 & 127)); - const int grid_l = __vsub4(grid1[0] ^ signs[0], signs[0]); - const int grid_h = __vsub4(grid2[0] ^ signs[1], signs[1]); - sumi = __dp4a(grid_l, *((int *)q8+0), sumi); - sumi = __dp4a(grid_h, *((int *)q8+1), sumi); - q8 += 8; - aux32 >>= 7; - } - const float d = (float)bq2->d * (0.5f + aux32) * __low2float(bq8_1[ib32].ds) * 0.5f; - return d * sumi; -#else - assert(false); - return 0.f; -#endif -#else - assert(false); - return 0.f; -#endif -} - -// TODO: don't use lookup table for signs -static __device__ __forceinline__ float vec_dot_iq3_s_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics -#if QK_K == 256 - const block_iq3_s * bq2 = (const block_iq3_s *) vbq; - - const int ib32 = iqs; - const uint8_t * qs = bq2->qs + 8*ib32; - const int8_t * q8 = bq8_1[ib32].qs; - int sumi = 0; - for (int l = 0; l < 4; ++l) { - const uint32_t * grid1 = iq3s_grid + (qs[2*l+0] | ((bq2->qh[ib32] << (8 - 2*l)) & 256)); - const uint32_t * grid2 = iq3s_grid + (qs[2*l+1] | ((bq2->qh[ib32] << (7 - 2*l)) & 256)); - uint32_t signs0 = __vcmpeq4(((bq2->signs[4*ib32+l] & 0xf) * 0x01010101) & 0x08040201, 0x08040201); - uint32_t signs1 = __vcmpeq4(((bq2->signs[4*ib32+l] >> 4) * 0x01010101) & 0x08040201, 0x08040201); - const int grid_l = __vsub4(grid1[0] ^ signs0, signs0); - const int grid_h = __vsub4(grid2[0] ^ signs1, signs1); - sumi = __dp4a(grid_l, *((int *)q8+0), sumi); - sumi = __dp4a(grid_h, *((int *)q8+1), sumi); - q8 += 8; - } - const float d = (float)bq2->d * (1 + 2*((bq2->scales[ib32/2] >> 4*(ib32%2)) & 0xf)) * __low2float(bq8_1[ib32].ds); - return d * sumi; -#else - assert(false); - return 0.f; -#endif -#else - assert(false); - return 0.f; -#endif -} - -static __device__ __forceinline__ float vec_dot_iq1_s_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { -#if QK_K == 256 - const block_iq1_s * bq1 = (const block_iq1_s *) vbq; - - const int ib32 = iqs; - int sumi = 0; -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - const int * q8 = (const int *)bq8_1[ib32].qs; - for (int l = 0; l < 4; ++l) { - const int * grid = (const int *)(iq1s_grid_gpu + (bq1->qs[4*ib32+l] | (((bq1->qh[ib32] >> 3*l) & 7) << 8))); - int grid0 = grid[0] & 0x0f0f0f0f; - int grid1 = (grid[0] >> 4) & 0x0f0f0f0f; - sumi = __dp4a(q8[2*l+1], grid1, __dp4a(q8[2*l+0], grid0, sumi)); - } -#else - const int8_t * q8 = bq8_1[ib32].qs; - for (int l = 0; l < 4; ++l) { - const uint8_t * grid = (const uint8_t *)(iq1s_grid_gpu + (bq1->qs[4*ib32+l] | (((bq1->qh[ib32] >> 3*l) & 7) << 8))); - for (int j = 0; j < 4; ++j) { - sumi += q8[j] * (grid[j] & 0xf) + q8[j+4] * (grid[j] >> 4); - } - q8 += 8; - } -#endif - const float delta = bq1->qh[ib32] & 0x8000 ? -1-IQ1S_DELTA : -1+IQ1S_DELTA; - const float d1q = (float)bq1->d * (2*((bq1->qh[ib32] >> 12) & 7) + 1); - const float d = d1q * __low2float (bq8_1[ib32].ds); - const float m = d1q * __high2float(bq8_1[ib32].ds); - return d * sumi + m * delta; -#else - assert(false); - return 0.f; -#endif -} - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics -static __device__ __forceinline__ void get_int_from_table_16(const uint32_t & q4, const uint8_t * values, - int & val1, int & val2) { - - uint32_t aux32; const uint8_t * q8 = (const uint8_t *)&aux32; - aux32 = q4 & 0x0f0f0f0f; - uint16_t v1 = values[q8[0]] | (values[q8[1]] << 8); - uint16_t v2 = values[q8[2]] | (values[q8[3]] << 8); - val1 = v1 | (v2 << 16); - aux32 = (q4 >> 4) & 0x0f0f0f0f; - v1 = values[q8[0]] | (values[q8[1]] << 8); - v2 = values[q8[2]] | (values[q8[3]] << 8); - val2 = v1 | (v2 << 16); -} -#endif - -static __device__ __forceinline__ float vec_dot_iq4_nl_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - - const block_iq4_nl * bq = (const block_iq4_nl *) vbq; - -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - const uint16_t * q4 = (const uint16_t *)bq->qs + 2*iqs; - const int32_t * q8 = (const int32_t *)bq8_1->qs + iqs; - - const uint8_t * values = (const uint8_t *)kvalues_iq4nl; - - int v1, v2; - int sumi1 = 0, sumi2 = 0; - for (int l = 0; l < VDR_Q4_0_Q8_1_MMVQ; ++l) { - const uint32_t aux = q4[2*l] | (q4[2*l+1] << 16); - get_int_from_table_16(aux, values, v1, v2); - sumi1 = __dp4a(v1, q8[l+0], sumi1); - sumi2 = __dp4a(v2, q8[l+4], sumi2); - } - -#else - const uint8_t * q4 = bq->qs + 4*iqs; - const int8_t * q8 = bq8_1->qs + 4*iqs; - - int sumi1 = 0, sumi2 = 0; - for (int l = 0; l < 4*VDR_Q4_0_Q8_1_MMVQ; ++l) { - sumi1 += q8[l+ 0] * kvalues_iq4nl[q4[l] & 0xf]; - sumi2 += q8[l+16] * kvalues_iq4nl[q4[l] >> 4]; - } -#endif - const float d = (float)bq->d * __low2float(bq8_1->ds); - return d * (sumi1 + sumi2); -} - -static __device__ __forceinline__ float vec_dot_iq4_xs_q8_1( - const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { - -#if QK_K == 256 -#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics - - const block_iq4_xs * bq4 = (const block_iq4_xs *) vbq; - const uint8_t * values = (const uint8_t *)kvalues_iq4nl; - - //// iqs is 0...7 - //const int ib64 = iqs/2; - //const int il = iqs%2; - //const int32_t * q8_1 = (const int *)bq8_1[2*ib64+0].qs + 2*il; - //const int32_t * q8_2 = (const int *)bq8_1[2*ib64+1].qs + 2*il; - //const uint32_t * q4_1 = (const uint32_t *)bq4->qs + 8*ib64 + 2*il; - //const uint32_t * q4_2 = q4_1 + 4; - //const int8_t ls1 = (bq4->scales_l[ib64] & 0xf) | (((bq4->scales_h >> (4*ib64+0)) & 3) << 4); - //const int8_t ls2 = (bq4->scales_l[ib64] >> 4) | (((bq4->scales_h >> (4*ib64+2)) & 3) << 4); - //const float d1 = (float)bq4->d * (ls1 - 32) * __low2float(bq8_1[2*ib64+0].ds); - //const float d2 = (float)bq4->d * (ls2 - 32) * __low2float(bq8_1[2*ib64+1].ds); - //int v1, v2; - //int sumi1 = 0, sumi2 = 0; - //for (int j = 0; j < 2; ++j) { - // get_int_from_table_16(q4_1[j], values, v1, v2); - // sumi1 = __dp4a(v2, q8_1[j+4], __dp4a(v1, q8_1[j+0], sumi1)); - // get_int_from_table_16(q4_2[j], values, v1, v2); - // sumi2 = __dp4a(v2, q8_2[j+4], __dp4a(v1, q8_2[j+0], sumi2)); - //} - //return d1 * sumi1 + d2 * sumi2; - - // iqs is 0...7 - const int ib32 = iqs; - const int32_t * q8 = (const int *)bq8_1[ib32].qs; - const uint32_t * q4 = (const uint32_t *)bq4->qs + 4*ib32; - const int8_t ls = ((bq4->scales_l[ib32/2] >> 4*(ib32%2)) & 0xf) | (((bq4->scales_h >> 2*ib32) & 3) << 4); - const float d = (float)bq4->d * (ls - 32) * __low2float(bq8_1[ib32].ds); - int v1, v2; - int sumi1 = 0, sumi2 = 0; - for (int j = 0; j < 4; ++j) { - get_int_from_table_16(q4[j], values, v1, v2); - sumi1 = __dp4a(v1, q8[j+0], sumi1); - sumi2 = __dp4a(v2, q8[j+4], sumi2); - } - return d * (sumi1 + sumi2); - - //// iqs is 0...15 - //const int ib32 = iqs/2; - //const int il = iqs%2; - //const int32_t * q8 = (const int *)bq8_1[ib32].qs + 2*il; - //const uint32_t * q4 = (const uint32_t *)bq4->qs + 4*ib32 + 2*il; - //const int8_t ls = ((bq4->scales_l[ib32/2] >> 4*(ib32%2)) & 0xf) | (((bq4->scales_h >> 2*ib32) & 3) << 4); - //const float d = (float)bq4->d * (ls - 32) * __low2float(bq8_1[ib32].ds); - //int v1, v2; - //int sumi1 = 0, sumi2 = 0; - //for (int j = 0; j < 2; ++j) { - // get_int_from_table_16(q4[j], values, v1, v2); - // sumi1 = __dp4a(v1, q8[j+0], sumi1); - // sumi2 = __dp4a(v2, q8[j+4], sumi2); - //} - //return d * (sumi1 + sumi2); -#else - assert(false); - return 0.f; -#endif -#else - return vec_dot_iq4_xs_q8_1(vbq, bq8_1, iqs); -#endif -} - -template -static __device__ __forceinline__ void mul_mat_q( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - - const block_q_t * x = (const block_q_t *) vx; - const block_q8_1 * y = (const block_q8_1 *) vy; - - const int blocks_per_row_x = ncols_x / qk; - const int blocks_per_col_y = nrows_y / QK8_1; - const int blocks_per_warp = WARP_SIZE / qi; - - const int & ncols_dst = ncols_y; - - const int row_dst_0 = blockIdx.x*mmq_y; - const int & row_x_0 = row_dst_0; - - const int col_dst_0 = blockIdx.y*mmq_x; - const int & col_y_0 = col_dst_0; - - int * tile_x_ql = nullptr; - half2 * tile_x_dm = nullptr; - int * tile_x_qh = nullptr; - int * tile_x_sc = nullptr; - - allocate_tiles(&tile_x_ql, &tile_x_dm, &tile_x_qh, &tile_x_sc); - - __shared__ int tile_y_qs[mmq_x * WARP_SIZE]; - __shared__ half2 tile_y_ds[mmq_x * WARP_SIZE/QI8_1]; - - float sum[mmq_y/WARP_SIZE][mmq_x/nwarps] = {{0.0f}}; - - for (int ib0 = 0; ib0 < blocks_per_row_x; ib0 += blocks_per_warp) { - - load_tiles(x + row_x_0*blocks_per_row_x + ib0, tile_x_ql, tile_x_dm, tile_x_qh, tile_x_sc, - threadIdx.y, nrows_x-row_x_0-1, threadIdx.x, blocks_per_row_x); - -#pragma unroll - for (int ir = 0; ir < qr; ++ir) { - const int kqs = ir*WARP_SIZE + threadIdx.x; - const int kbxd = kqs / QI8_1; - -#pragma unroll - for (int i = 0; i < mmq_x; i += nwarps) { - const int col_y_eff = min(col_y_0 + threadIdx.y + i, ncols_y-1); // to prevent out-of-bounds memory accesses - - const block_q8_1 * by0 = &y[col_y_eff*blocks_per_col_y + ib0 * (qk/QK8_1) + kbxd]; - - const int index_y = (threadIdx.y + i) * WARP_SIZE + kqs % WARP_SIZE; - tile_y_qs[index_y] = get_int_from_int8_aligned(by0->qs, threadIdx.x % QI8_1); - } - -#pragma unroll - for (int ids0 = 0; ids0 < mmq_x; ids0 += nwarps * QI8_1) { - const int ids = (ids0 + threadIdx.y * QI8_1 + threadIdx.x / (WARP_SIZE/QI8_1)) % mmq_x; - const int kby = threadIdx.x % (WARP_SIZE/QI8_1); - const int col_y_eff = min(col_y_0 + ids, ncols_y-1); - - // if the sum is not needed it's faster to transform the scale to f32 ahead of time - const half2 * dsi_src = &y[col_y_eff*blocks_per_col_y + ib0 * (qk/QK8_1) + ir*(WARP_SIZE/QI8_1) + kby].ds; - half2 * dsi_dst = &tile_y_ds[ids * (WARP_SIZE/QI8_1) + kby]; - if (need_sum) { - *dsi_dst = *dsi_src; - } else { - float * dfi_dst = (float *) dsi_dst; - *dfi_dst = __low2float(*dsi_src); - } - } - - __syncthreads(); - -// #pragma unroll // unrolling this loop causes too much register pressure - for (int k = ir*WARP_SIZE/qr; k < (ir+1)*WARP_SIZE/qr; k += vdr) { -#pragma unroll - for (int j = 0; j < mmq_x; j += nwarps) { -#pragma unroll - for (int i = 0; i < mmq_y; i += WARP_SIZE) { - sum[i/WARP_SIZE][j/nwarps] += vec_dot( - tile_x_ql, tile_x_dm, tile_x_qh, tile_x_sc, tile_y_qs, tile_y_ds, - threadIdx.x + i, threadIdx.y + j, k); - } - } - } - - __syncthreads(); - } - } - -#pragma unroll - for (int j = 0; j < mmq_x; j += nwarps) { - const int col_dst = col_dst_0 + j + threadIdx.y; - - if (col_dst >= ncols_dst) { - return; - } - -#pragma unroll - for (int i = 0; i < mmq_y; i += WARP_SIZE) { - const int row_dst = row_dst_0 + threadIdx.x + i; - - if (row_dst >= nrows_dst) { - continue; - } - - dst[col_dst*nrows_dst + row_dst] = sum[i/WARP_SIZE][j/nwarps]; - } - } -} - -#define MMQ_X_Q4_0_RDNA2 64 -#define MMQ_Y_Q4_0_RDNA2 128 -#define NWARPS_Q4_0_RDNA2 8 -#define MMQ_X_Q4_0_RDNA1 64 -#define MMQ_Y_Q4_0_RDNA1 64 -#define NWARPS_Q4_0_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q4_0_AMPERE 4 -#define MMQ_Y_Q4_0_AMPERE 32 -#define NWARPS_Q4_0_AMPERE 4 -#else -#define MMQ_X_Q4_0_AMPERE 64 -#define MMQ_Y_Q4_0_AMPERE 128 -#define NWARPS_Q4_0_AMPERE 4 -#endif -#define MMQ_X_Q4_0_PASCAL 64 -#define MMQ_Y_Q4_0_PASCAL 64 -#define NWARPS_Q4_0_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q4_0_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) - mul_mat_q4_0( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q4_0_RDNA2; - const int mmq_y = MMQ_Y_Q4_0_RDNA2; - const int nwarps = NWARPS_Q4_0_RDNA2; -#else - const int mmq_x = MMQ_X_Q4_0_RDNA1; - const int mmq_y = MMQ_Y_Q4_0_RDNA1; - const int nwarps = NWARPS_Q4_0_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q4_0, VDR_Q4_0_Q8_1_MMQ, vec_dot_q4_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q4_0_AMPERE; - const int mmq_y = MMQ_Y_Q4_0_AMPERE; - const int nwarps = NWARPS_Q4_0_AMPERE; - - mul_mat_q, - load_tiles_q4_0, VDR_Q4_0_Q8_1_MMQ, vec_dot_q4_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q4_0_PASCAL; - const int mmq_y = MMQ_Y_Q4_0_PASCAL; - const int nwarps = NWARPS_Q4_0_PASCAL; - - mul_mat_q, - load_tiles_q4_0, VDR_Q4_0_Q8_1_MMQ, vec_dot_q4_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q4_0_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q4_1_RDNA2 64 -#define MMQ_Y_Q4_1_RDNA2 128 -#define NWARPS_Q4_1_RDNA2 8 -#define MMQ_X_Q4_1_RDNA1 64 -#define MMQ_Y_Q4_1_RDNA1 64 -#define NWARPS_Q4_1_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q4_1_AMPERE 4 -#define MMQ_Y_Q4_1_AMPERE 32 -#define NWARPS_Q4_1_AMPERE 4 -#else -#define MMQ_X_Q4_1_AMPERE 64 -#define MMQ_Y_Q4_1_AMPERE 128 -#define NWARPS_Q4_1_AMPERE 4 -#endif -#define MMQ_X_Q4_1_PASCAL 64 -#define MMQ_Y_Q4_1_PASCAL 64 -#define NWARPS_Q4_1_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q4_1_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#elif __CUDA_ARCH__ < CC_VOLTA - __launch_bounds__(WARP_SIZE*NWARPS_Q4_1_PASCAL, 2) -#endif // __CUDA_ARCH__ < CC_VOLTA - mul_mat_q4_1( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q4_1_RDNA2; - const int mmq_y = MMQ_Y_Q4_1_RDNA2; - const int nwarps = NWARPS_Q4_1_RDNA2; -#else - const int mmq_x = MMQ_X_Q4_1_RDNA1; - const int mmq_y = MMQ_Y_Q4_1_RDNA1; - const int nwarps = NWARPS_Q4_1_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q4_1, VDR_Q4_1_Q8_1_MMQ, vec_dot_q4_1_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q4_1_AMPERE; - const int mmq_y = MMQ_Y_Q4_1_AMPERE; - const int nwarps = NWARPS_Q4_1_AMPERE; - - mul_mat_q, - load_tiles_q4_1, VDR_Q4_1_Q8_1_MMQ, vec_dot_q4_1_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q4_1_PASCAL; - const int mmq_y = MMQ_Y_Q4_1_PASCAL; - const int nwarps = NWARPS_Q4_1_PASCAL; - - mul_mat_q, - load_tiles_q4_1, VDR_Q4_1_Q8_1_MMQ, vec_dot_q4_1_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q4_1_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q5_0_RDNA2 64 -#define MMQ_Y_Q5_0_RDNA2 128 -#define NWARPS_Q5_0_RDNA2 8 -#define MMQ_X_Q5_0_RDNA1 64 -#define MMQ_Y_Q5_0_RDNA1 64 -#define NWARPS_Q5_0_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q5_0_AMPERE 4 -#define MMQ_Y_Q5_0_AMPERE 32 -#define NWARPS_Q5_0_AMPERE 4 -#else -#define MMQ_X_Q5_0_AMPERE 128 -#define MMQ_Y_Q5_0_AMPERE 64 -#define NWARPS_Q5_0_AMPERE 4 -#endif -#define MMQ_X_Q5_0_PASCAL 64 -#define MMQ_Y_Q5_0_PASCAL 64 -#define NWARPS_Q5_0_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q5_0_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) - mul_mat_q5_0( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q5_0_RDNA2; - const int mmq_y = MMQ_Y_Q5_0_RDNA2; - const int nwarps = NWARPS_Q5_0_RDNA2; -#else - const int mmq_x = MMQ_X_Q5_0_RDNA1; - const int mmq_y = MMQ_Y_Q5_0_RDNA1; - const int nwarps = NWARPS_Q5_0_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q5_0, VDR_Q5_0_Q8_1_MMQ, vec_dot_q5_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q5_0_AMPERE; - const int mmq_y = MMQ_Y_Q5_0_AMPERE; - const int nwarps = NWARPS_Q5_0_AMPERE; - - mul_mat_q, - load_tiles_q5_0, VDR_Q5_0_Q8_1_MMQ, vec_dot_q5_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q5_0_PASCAL; - const int mmq_y = MMQ_Y_Q5_0_PASCAL; - const int nwarps = NWARPS_Q5_0_PASCAL; - - mul_mat_q, - load_tiles_q5_0, VDR_Q5_0_Q8_1_MMQ, vec_dot_q5_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q5_0_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q5_1_RDNA2 64 -#define MMQ_Y_Q5_1_RDNA2 128 -#define NWARPS_Q5_1_RDNA2 8 -#define MMQ_X_Q5_1_RDNA1 64 -#define MMQ_Y_Q5_1_RDNA1 64 -#define NWARPS_Q5_1_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q5_1_AMPERE 4 -#define MMQ_Y_Q5_1_AMPERE 32 -#define NWARPS_Q5_1_AMPERE 4 -#else -#define MMQ_X_Q5_1_AMPERE 128 -#define MMQ_Y_Q5_1_AMPERE 64 -#define NWARPS_Q5_1_AMPERE 4 -#endif -#define MMQ_X_Q5_1_PASCAL 64 -#define MMQ_Y_Q5_1_PASCAL 64 -#define NWARPS_Q5_1_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q5_1_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -mul_mat_q5_1( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q5_1_RDNA2; - const int mmq_y = MMQ_Y_Q5_1_RDNA2; - const int nwarps = NWARPS_Q5_1_RDNA2; -#else - const int mmq_x = MMQ_X_Q5_1_RDNA1; - const int mmq_y = MMQ_Y_Q5_1_RDNA1; - const int nwarps = NWARPS_Q5_1_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q5_1, VDR_Q5_1_Q8_1_MMQ, vec_dot_q5_1_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q5_1_AMPERE; - const int mmq_y = MMQ_Y_Q5_1_AMPERE; - const int nwarps = NWARPS_Q5_1_AMPERE; - - mul_mat_q, - load_tiles_q5_1, VDR_Q5_1_Q8_1_MMQ, vec_dot_q5_1_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q5_1_PASCAL; - const int mmq_y = MMQ_Y_Q5_1_PASCAL; - const int nwarps = NWARPS_Q5_1_PASCAL; - - mul_mat_q, - load_tiles_q5_1, VDR_Q5_1_Q8_1_MMQ, vec_dot_q5_1_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q5_1_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q8_0_RDNA2 64 -#define MMQ_Y_Q8_0_RDNA2 128 -#define NWARPS_Q8_0_RDNA2 8 -#define MMQ_X_Q8_0_RDNA1 64 -#define MMQ_Y_Q8_0_RDNA1 64 -#define NWARPS_Q8_0_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q8_0_AMPERE 4 -#define MMQ_Y_Q8_0_AMPERE 32 -#define NWARPS_Q8_0_AMPERE 4 -#else -#define MMQ_X_Q8_0_AMPERE 128 -#define MMQ_Y_Q8_0_AMPERE 64 -#define NWARPS_Q8_0_AMPERE 4 -#endif -#define MMQ_X_Q8_0_PASCAL 64 -#define MMQ_Y_Q8_0_PASCAL 64 -#define NWARPS_Q8_0_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q8_0_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) - mul_mat_q8_0( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q8_0_RDNA2; - const int mmq_y = MMQ_Y_Q8_0_RDNA2; - const int nwarps = NWARPS_Q8_0_RDNA2; -#else - const int mmq_x = MMQ_X_Q8_0_RDNA1; - const int mmq_y = MMQ_Y_Q8_0_RDNA1; - const int nwarps = NWARPS_Q8_0_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q8_0, VDR_Q8_0_Q8_1_MMQ, vec_dot_q8_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q8_0_AMPERE; - const int mmq_y = MMQ_Y_Q8_0_AMPERE; - const int nwarps = NWARPS_Q8_0_AMPERE; - - mul_mat_q, - load_tiles_q8_0, VDR_Q8_0_Q8_1_MMQ, vec_dot_q8_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q8_0_PASCAL; - const int mmq_y = MMQ_Y_Q8_0_PASCAL; - const int nwarps = NWARPS_Q8_0_PASCAL; - - mul_mat_q, - load_tiles_q8_0, VDR_Q8_0_Q8_1_MMQ, vec_dot_q8_0_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q8_0_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q2_K_RDNA2 64 -#define MMQ_Y_Q2_K_RDNA2 128 -#define NWARPS_Q2_K_RDNA2 8 -#define MMQ_X_Q2_K_RDNA1 128 -#define MMQ_Y_Q2_K_RDNA1 32 -#define NWARPS_Q2_K_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q2_K_AMPERE 4 -#define MMQ_Y_Q2_K_AMPERE 32 -#define NWARPS_Q2_K_AMPERE 4 -#else -#define MMQ_X_Q2_K_AMPERE 64 -#define MMQ_Y_Q2_K_AMPERE 128 -#define NWARPS_Q2_K_AMPERE 4 -#endif -#define MMQ_X_Q2_K_PASCAL 64 -#define MMQ_Y_Q2_K_PASCAL 64 -#define NWARPS_Q2_K_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q2_K_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -mul_mat_q2_K( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q2_K_RDNA2; - const int mmq_y = MMQ_Y_Q2_K_RDNA2; - const int nwarps = NWARPS_Q2_K_RDNA2; -#else - const int mmq_x = MMQ_X_Q2_K_RDNA1; - const int mmq_y = MMQ_Y_Q2_K_RDNA1; - const int nwarps = NWARPS_Q2_K_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q2_K, VDR_Q2_K_Q8_1_MMQ, vec_dot_q2_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q2_K_AMPERE; - const int mmq_y = MMQ_Y_Q2_K_AMPERE; - const int nwarps = NWARPS_Q2_K_AMPERE; - - mul_mat_q, - load_tiles_q2_K, VDR_Q2_K_Q8_1_MMQ, vec_dot_q2_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q2_K_PASCAL; - const int mmq_y = MMQ_Y_Q2_K_PASCAL; - const int nwarps = NWARPS_Q2_K_PASCAL; - - mul_mat_q, - load_tiles_q2_K, VDR_Q2_K_Q8_1_MMQ, vec_dot_q2_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q2_K_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q3_K_RDNA2 128 -#define MMQ_Y_Q3_K_RDNA2 64 -#define NWARPS_Q3_K_RDNA2 8 -#define MMQ_X_Q3_K_RDNA1 32 -#define MMQ_Y_Q3_K_RDNA1 128 -#define NWARPS_Q3_K_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q3_K_AMPERE 4 -#define MMQ_Y_Q3_K_AMPERE 32 -#define NWARPS_Q3_K_AMPERE 4 -#else -#define MMQ_X_Q3_K_AMPERE 128 -#define MMQ_Y_Q3_K_AMPERE 128 -#define NWARPS_Q3_K_AMPERE 4 -#endif -#define MMQ_X_Q3_K_PASCAL 64 -#define MMQ_Y_Q3_K_PASCAL 64 -#define NWARPS_Q3_K_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q3_K_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#elif __CUDA_ARCH__ < CC_VOLTA - __launch_bounds__(WARP_SIZE*NWARPS_Q3_K_PASCAL, 2) -#endif // __CUDA_ARCH__ < CC_VOLTA - mul_mat_q3_K( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q3_K_RDNA2; - const int mmq_y = MMQ_Y_Q3_K_RDNA2; - const int nwarps = NWARPS_Q3_K_RDNA2; -#else - const int mmq_x = MMQ_X_Q3_K_RDNA1; - const int mmq_y = MMQ_Y_Q3_K_RDNA1; - const int nwarps = NWARPS_Q3_K_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q3_K, VDR_Q3_K_Q8_1_MMQ, vec_dot_q3_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q3_K_AMPERE; - const int mmq_y = MMQ_Y_Q3_K_AMPERE; - const int nwarps = NWARPS_Q3_K_AMPERE; - - mul_mat_q, - load_tiles_q3_K, VDR_Q3_K_Q8_1_MMQ, vec_dot_q3_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q3_K_PASCAL; - const int mmq_y = MMQ_Y_Q3_K_PASCAL; - const int nwarps = NWARPS_Q3_K_PASCAL; - - mul_mat_q, - load_tiles_q3_K, VDR_Q3_K_Q8_1_MMQ, vec_dot_q3_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q3_K_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q4_K_RDNA2 64 -#define MMQ_Y_Q4_K_RDNA2 128 -#define NWARPS_Q4_K_RDNA2 8 -#define MMQ_X_Q4_K_RDNA1 32 -#define MMQ_Y_Q4_K_RDNA1 64 -#define NWARPS_Q4_K_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q4_K_AMPERE 4 -#define MMQ_Y_Q4_K_AMPERE 32 -#define NWARPS_Q4_K_AMPERE 4 -#else -#define MMQ_X_Q4_K_AMPERE 64 -#define MMQ_Y_Q4_K_AMPERE 128 -#define NWARPS_Q4_K_AMPERE 4 -#endif -#define MMQ_X_Q4_K_PASCAL 64 -#define MMQ_Y_Q4_K_PASCAL 64 -#define NWARPS_Q4_K_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q4_K_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#elif __CUDA_ARCH__ < CC_VOLTA - __launch_bounds__(WARP_SIZE*NWARPS_Q4_K_PASCAL, 2) -#endif // __CUDA_ARCH__ < CC_VOLTA - mul_mat_q4_K( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q4_K_RDNA2; - const int mmq_y = MMQ_Y_Q4_K_RDNA2; - const int nwarps = NWARPS_Q4_K_RDNA2; -#else - const int mmq_x = MMQ_X_Q4_K_RDNA1; - const int mmq_y = MMQ_Y_Q4_K_RDNA1; - const int nwarps = NWARPS_Q4_K_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q4_K, VDR_Q4_K_Q8_1_MMQ, vec_dot_q4_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q4_K_AMPERE; - const int mmq_y = MMQ_Y_Q4_K_AMPERE; - const int nwarps = NWARPS_Q4_K_AMPERE; - - mul_mat_q, - load_tiles_q4_K, VDR_Q4_K_Q8_1_MMQ, vec_dot_q4_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q4_K_PASCAL; - const int mmq_y = MMQ_Y_Q4_K_PASCAL; - const int nwarps = NWARPS_Q4_K_PASCAL; - - mul_mat_q, - load_tiles_q4_K, VDR_Q4_K_Q8_1_MMQ, vec_dot_q4_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q4_K_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q5_K_RDNA2 64 -#define MMQ_Y_Q5_K_RDNA2 128 -#define NWARPS_Q5_K_RDNA2 8 -#define MMQ_X_Q5_K_RDNA1 32 -#define MMQ_Y_Q5_K_RDNA1 64 -#define NWARPS_Q5_K_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q5_K_AMPERE 4 -#define MMQ_Y_Q5_K_AMPERE 32 -#define NWARPS_Q5_K_AMPERE 4 -#else -#define MMQ_X_Q5_K_AMPERE 64 -#define MMQ_Y_Q5_K_AMPERE 128 -#define NWARPS_Q5_K_AMPERE 4 -#endif -#define MMQ_X_Q5_K_PASCAL 64 -#define MMQ_Y_Q5_K_PASCAL 64 -#define NWARPS_Q5_K_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q5_K_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -mul_mat_q5_K( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q5_K_RDNA2; - const int mmq_y = MMQ_Y_Q5_K_RDNA2; - const int nwarps = NWARPS_Q5_K_RDNA2; -#else - const int mmq_x = MMQ_X_Q5_K_RDNA1; - const int mmq_y = MMQ_Y_Q5_K_RDNA1; - const int nwarps = NWARPS_Q5_K_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q5_K, VDR_Q5_K_Q8_1_MMQ, vec_dot_q5_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q5_K_AMPERE; - const int mmq_y = MMQ_Y_Q5_K_AMPERE; - const int nwarps = NWARPS_Q5_K_AMPERE; - - mul_mat_q, - load_tiles_q5_K, VDR_Q5_K_Q8_1_MMQ, vec_dot_q5_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q5_K_PASCAL; - const int mmq_y = MMQ_Y_Q5_K_PASCAL; - const int nwarps = NWARPS_Q5_K_PASCAL; - - mul_mat_q, - load_tiles_q5_K, VDR_Q5_K_Q8_1_MMQ, vec_dot_q5_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q5_K_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -#define MMQ_X_Q6_K_RDNA2 64 -#define MMQ_Y_Q6_K_RDNA2 128 -#define NWARPS_Q6_K_RDNA2 8 -#define MMQ_X_Q6_K_RDNA1 32 -#define MMQ_Y_Q6_K_RDNA1 64 -#define NWARPS_Q6_K_RDNA1 8 -#if defined(CUDA_USE_TENSOR_CORES) -#define MMQ_X_Q6_K_AMPERE 4 -#define MMQ_Y_Q6_K_AMPERE 32 -#define NWARPS_Q6_K_AMPERE 4 -#else -#define MMQ_X_Q6_K_AMPERE 64 -#define MMQ_Y_Q6_K_AMPERE 64 -#define NWARPS_Q6_K_AMPERE 4 -#endif -#define MMQ_X_Q6_K_PASCAL 64 -#define MMQ_Y_Q6_K_PASCAL 64 -#define NWARPS_Q6_K_PASCAL 8 - -template static __global__ void -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - __launch_bounds__(WARP_SIZE*NWARPS_Q6_K_RDNA2, 2) -#endif // defined(RDNA3) || defined(RDNA2) -#elif __CUDA_ARCH__ < CC_VOLTA - __launch_bounds__(WARP_SIZE*NWARPS_Q6_K_PASCAL, 2) -#endif // __CUDA_ARCH__ < CC_VOLTA - mul_mat_q6_K( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) -#if defined(RDNA3) || defined(RDNA2) - const int mmq_x = MMQ_X_Q6_K_RDNA2; - const int mmq_y = MMQ_Y_Q6_K_RDNA2; - const int nwarps = NWARPS_Q6_K_RDNA2; -#else - const int mmq_x = MMQ_X_Q6_K_RDNA1; - const int mmq_y = MMQ_Y_Q6_K_RDNA1; - const int nwarps = NWARPS_Q6_K_RDNA1; -#endif // defined(RDNA3) || defined(RDNA2) - - mul_mat_q, - load_tiles_q6_K, VDR_Q6_K_Q8_1_MMQ, vec_dot_q6_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= CC_VOLTA - const int mmq_x = MMQ_X_Q6_K_AMPERE; - const int mmq_y = MMQ_Y_Q6_K_AMPERE; - const int nwarps = NWARPS_Q6_K_AMPERE; - - mul_mat_q, - load_tiles_q6_K, VDR_Q6_K_Q8_1_MMQ, vec_dot_q6_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - -#elif __CUDA_ARCH__ >= MIN_CC_DP4A - const int mmq_x = MMQ_X_Q6_K_PASCAL; - const int mmq_y = MMQ_Y_Q6_K_PASCAL; - const int nwarps = NWARPS_Q6_K_PASCAL; - - mul_mat_q, - load_tiles_q6_K, VDR_Q6_K_Q8_1_MMQ, vec_dot_q6_K_q8_1_mul_mat> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); -#else - GGML_UNUSED(vec_dot_q6_K_q8_1_mul_mat); - NO_DEVICE_CODE; -#endif // __CUDA_ARCH__ >= CC_VOLTA -} - -template -#if !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) -// tell the compiler to use as many registers as it wants, see nwarps definition below -__launch_bounds__((ncols_y <= 4 ? 4 : 2)*WARP_SIZE, 1) -#endif // !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) -static __global__ void mul_mat_vec_q( - const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, - const int ncols_x, const int nrows_x, const int nrows_y, const int nrows_dst) { - -#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) && (defined(RDNA2) || defined(RDNA3)) - constexpr int nwarps = 1; - constexpr int rows_per_cuda_block = 1; -#else - constexpr int nwarps = ncols_y <= 4 ? 4 : 2; - constexpr int rows_per_cuda_block = ncols_y == 1 ? 1 : 2; -#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) && !defined(RDNA2) && !defined(RDNA3) - - const int tid = WARP_SIZE*threadIdx.y + threadIdx.x; - const int row0 = rows_per_cuda_block*blockIdx.x; - const int blocks_per_row_x = ncols_x / qk; - const int blocks_per_col_y = nrows_y / QK8_1; - constexpr int blocks_per_iter = vdr * nwarps*WARP_SIZE / qi; - -// partial sum for each thread - float tmp[ncols_y][rows_per_cuda_block] = {0.0f}; - - const block_q_t * x = (const block_q_t *) vx; - const block_q8_1 * y = (const block_q8_1 *) vy; - - for (int kbx = tid / (qi/vdr); kbx < blocks_per_row_x; kbx += blocks_per_iter) { - const int kby = kbx * (qk/QK8_1); // y block index that aligns with kbx - - // x block quant index when casting the quants to int - const int kqs = vdr * (tid % (qi/vdr)); - -#pragma unroll - for (int j = 0; j < ncols_y; ++j) { -#pragma unroll - for (int i = 0; i < rows_per_cuda_block; ++i) { - tmp[j][i] += vec_dot_q_cuda( - &x[kbx + (row0 + i)*blocks_per_row_x], &y[j*blocks_per_col_y + kby], kqs); - } - } - } - - __shared__ float tmp_shared[nwarps-1 > 0 ? nwarps-1 : 1][ncols_y][rows_per_cuda_block][WARP_SIZE]; - if (threadIdx.y > 0) { -#pragma unroll - for (int j = 0; j < ncols_y; ++j) { -#pragma unroll - for (int i = 0; i < rows_per_cuda_block; ++i) { - tmp_shared[threadIdx.y-1][j][i][threadIdx.x] = tmp[j][i]; - } - } - } - __syncthreads(); - if (threadIdx.y > 0) { - return; - } - - // sum up partial sums and write back result -#pragma unroll - for (int j = 0; j < ncols_y; ++j) { -#pragma unroll - for (int i = 0; i < rows_per_cuda_block; ++i) { -#pragma unroll - for (int l = 0; l < nwarps-1; ++l) { - tmp[j][i] += tmp_shared[l][j][i][threadIdx.x]; - } - tmp[j][i] = warp_reduce_sum(tmp[j][i]); - } - - if (threadIdx.x < rows_per_cuda_block) { - dst[j*nrows_dst + row0 + threadIdx.x] = tmp[j][threadIdx.x]; - } - } -} - -template -static __global__ void dequantize_mul_mat_vec(const void * __restrict__ vx, const dfloat * __restrict__ y, float * __restrict__ dst, const int ncols, const int nrows) { - // qk = quantized weights per x block - // qr = number of quantized weights per data value in x block - const int row = blockIdx.x*blockDim.y + threadIdx.y; - - if (row >= nrows) { - return; - } - - const int tid = threadIdx.x; - - const int iter_stride = 2*GGML_CUDA_DMMV_X; - const int vals_per_iter = iter_stride / WARP_SIZE; // num quantized vals per thread and i iter - const int y_offset = qr == 1 ? 1 : qk/2; - -// partial sum for each thread -#ifdef GGML_CUDA_F16 - half2 tmp = {0.0f, 0.0f}; // two sums for f16 to take advantage of half2 intrinsics -#else - float tmp = 0.0f; -#endif // GGML_CUDA_F16 - - for (int i = 0; i < ncols; i += iter_stride) { - const int col = i + vals_per_iter*tid; - const int ib = (row*ncols + col)/qk; // x block index - const int iqs = (col%qk)/qr; // x quant index - const int iybs = col - col%qk; // y block start index - -// processing >2 values per i iter is faster for fast GPUs -#pragma unroll - for (int j = 0; j < vals_per_iter; j += 2) { - // process 2 vals per j iter - - // dequantize - // for qr = 2 the iqs needs to increase by 1 per j iter because 2 weights per data val - dfloat2 v; - dequantize_kernel(vx, ib, iqs + j/qr, v); - - // matrix multiplication - // for qr = 2 the y index needs to increase by 1 per j iter because of y_offset = qk/2 -#ifdef GGML_CUDA_F16 - tmp += __hmul2(v, { - y[iybs + iqs + j/qr + 0], - y[iybs + iqs + j/qr + y_offset] - }); -#else - tmp += v.x * y[iybs + iqs + j/qr + 0]; - tmp += v.y * y[iybs + iqs + j/qr + y_offset]; -#endif // GGML_CUDA_F16 - } - } - - // sum up partial sums and write back result - tmp = warp_reduce_sum(tmp); - - if (tid == 0) { -#ifdef GGML_CUDA_F16 - dst[row] = tmp.x + tmp.y; -#else - dst[row] = tmp; -#endif // GGML_CUDA_F16 - } -} - static __global__ void mul_mat_p021_f16_f32( const void * __restrict__ vx, const float * __restrict__ y, float * __restrict__ dst, const int ncols_x, const int nrows_x, const int nchannels_x, const int nchannels_y) { @@ -6618,1916 +1149,6 @@ static __global__ void mul_mat_vec_nc_f16_f32( // nc == non-contiguous } } -static __device__ void cpy_1_f32_f32(const char * cxi, char * cdsti) { - const float * xi = (const float *) cxi; - float * dsti = (float *) cdsti; - - *dsti = *xi; -} - -static __device__ void cpy_1_f32_f16(const char * cxi, char * cdsti) { - const float * xi = (const float *) cxi; - half * dsti = (half *) cdsti; - - *dsti = __float2half(*xi); -} - -static __device__ void cpy_1_f16_f16(const char * cxi, char * cdsti) { - const half * xi = (const half *) cxi; - half * dsti = (half *) cdsti; - - *dsti = *xi; -} - -static __device__ void cpy_1_f16_f32(const char * cxi, char * cdsti) { - const half * xi = (const half *) cxi; - float * dsti = (float *) cdsti; - - *dsti = *xi; -} - -template -static __global__ void cpy_f32_f16(const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, - const int nb12, const int nb13) { - const int64_t i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= ne) { - return; - } - - // determine indices i03/i13, i02/i12, i01/i11, i00/i10 as a function of index i of flattened tensor - // then combine those indices with the corresponding byte offsets to get the total offsets - const int64_t i03 = i/(ne00 * ne01 * ne02); - const int64_t i02 = (i - i03*ne00*ne01*ne02 )/ (ne00*ne01); - const int64_t i01 = (i - i03*ne00*ne01*ne02 - i02*ne01*ne00) / ne00; - const int64_t i00 = i - i03*ne00*ne01*ne02 - i02*ne01*ne00 - i01*ne00; - const int64_t x_offset = i00*nb00 + i01*nb01 + i02*nb02 + i03 * nb03; - - const int64_t i13 = i/(ne10 * ne11 * ne12); - const int64_t i12 = (i - i13*ne10*ne11*ne12) / (ne10*ne11); - const int64_t i11 = (i - i13*ne10*ne11*ne12 - i12*ne10*ne11) / ne10; - const int64_t i10 = i - i13*ne10*ne11*ne12 - i12*ne10*ne11 - i11*ne10; - const int64_t dst_offset = i10*nb10 + i11*nb11 + i12*nb12 + i13 * nb13; - - cpy_1(cx + x_offset, cdst + dst_offset); -} - -static __device__ void cpy_blck_f32_q8_0(const char * cxi, char * cdsti) { - const float * xi = (const float *) cxi; - block_q8_0 * dsti = (block_q8_0 *) cdsti; - - float amax = 0.0f; // absolute max - - for (int j = 0; j < QK8_0; j++) { - const float v = xi[j]; - amax = fmaxf(amax, fabsf(v)); - } - - const float d = amax / ((1 << 7) - 1); - const float id = d ? 1.0f/d : 0.0f; - - dsti->d = d; - - for (int j = 0; j < QK8_0; ++j) { - const float x0 = xi[j]*id; - - dsti->qs[j] = roundf(x0); - } -} - -static __device__ void cpy_blck_f32_q4_0(const char * cxi, char * cdsti) { - const float * xi = (const float *) cxi; - block_q4_0 * dsti = (block_q4_0 *) cdsti; - - float amax = 0.0f; - float vmax = 0.0f; - - for (int j = 0; j < QK4_0; ++j) { - const float v = xi[j]; - if (amax < fabsf(v)) { - amax = fabsf(v); - vmax = v; - } - } - - const float d = vmax / -8; - const float id = d ? 1.0f/d : 0.0f; - - dsti->d = d; - - for (int j = 0; j < QK4_0/2; ++j) { - const float x0 = xi[0 + j]*id; - const float x1 = xi[QK4_0/2 + j]*id; - - const uint8_t xi0 = min(15, (int8_t)(x0 + 8.5f)); - const uint8_t xi1 = min(15, (int8_t)(x1 + 8.5f)); - - dsti->qs[j] = xi0; - dsti->qs[j] |= xi1 << 4; - } -} - -static __device__ void cpy_blck_f32_q4_1(const char * cxi, char * cdsti) { - const float * xi = (const float *) cxi; - block_q4_1 * dsti = (block_q4_1 *) cdsti; - - float vmin = FLT_MAX; - float vmax = -FLT_MAX; - - for (int j = 0; j < QK4_1; ++j) { - const float v = xi[j]; - - if (v < vmin) vmin = v; - if (v > vmax) vmax = v; - } - - const float d = (vmax - vmin) / ((1 << 4) - 1); - const float id = d ? 1.0f/d : 0.0f; - - dsti->dm.x = d; - dsti->dm.y = vmin; - - for (int j = 0; j < QK4_1/2; ++j) { - const float x0 = (xi[0 + j] - vmin)*id; - const float x1 = (xi[QK4_1/2 + j] - vmin)*id; - - const uint8_t xi0 = min(15, (int8_t)(x0 + 0.5f)); - const uint8_t xi1 = min(15, (int8_t)(x1 + 0.5f)); - - dsti->qs[j] = xi0; - dsti->qs[j] |= xi1 << 4; - } -} - -static __device__ void cpy_blck_f32_q5_0(const char * cxi, char * cdsti) { - const float * xi = (const float *) cxi; - block_q5_0 * dsti = (block_q5_0 *) cdsti; - - float amax = 0.0f; - float vmax = 0.0f; - - for (int j = 0; j < QK5_0; ++j) { - const float v = xi[j]; - if (amax < fabsf(v)) { - amax = fabsf(v); - vmax = v; - } - } - - const float d = vmax / -16; - const float id = d ? 1.0f/d : 0.0f; - - dsti->d = d; - - uint32_t qh = 0; - for (int j = 0; j < QK5_0/2; ++j) { - const float x0 = xi[0 + j]*id; - const float x1 = xi[QK5_0/2 + j]*id; - - const uint8_t xi0 = min(31, (int8_t)(x0 + 16.5f)); - const uint8_t xi1 = min(31, (int8_t)(x1 + 16.5f)); - - dsti->qs[j] = (xi0 & 0xf) | ((xi1 & 0xf) << 4); - qh |= ((xi0 & 0x10u) >> 4) << (j + 0); - qh |= ((xi1 & 0x10u) >> 4) << (j + QK5_0/2); - } - memcpy(dsti->qh, &qh, sizeof(qh)); -} - -static __device__ void cpy_blck_f32_q5_1(const char * cxi, char * cdsti) { - const float * xi = (const float *) cxi; - block_q5_1 * dsti = (block_q5_1 *) cdsti; - - float min = xi[0]; - float max = xi[0]; - - for (int j = 1; j < QK5_1; ++j) { - const float v = xi[j]; - min = v < min ? v : min; - max = v > max ? v : max; - } - - const float d = (max - min) / 31; - const float id = d ? 1.0f/d : 0.0f; - - dsti->dm.x = d; - dsti->dm.y = min; - - uint32_t qh = 0; - for (int j = 0; j < QK5_1/2; ++j) { - const float x0 = (xi[0 + j] - min)*id; - const float x1 = (xi[QK5_1/2 + j] - min)*id; - - const uint8_t xi0 = (uint8_t)(x0 + 0.5f); - const uint8_t xi1 = (uint8_t)(x1 + 0.5f); - - dsti->qs[j] = (xi0 & 0xf) | ((xi1 & 0xf) << 4); - qh |= ((xi0 & 0x10u) >> 4) << (j + 0); - qh |= ((xi1 & 0x10u) >> 4) << (j + QK5_1/2); - } - memcpy(dsti->qh, &qh, sizeof(qh)); -} - -static __device__ __forceinline__ int best_index_int8(int n, const int8_t * val, float x) { - if (x <= val[0]) return 0; - if (x >= val[n-1]) return n-1; - int ml = 0, mu = n-1; - while (mu-ml > 1) { - int mav = (ml+mu)/2; - if (x < val[mav]) mu = mav; else ml = mav; - } - return x - val[mu-1] < val[mu] - x ? mu-1 : mu; -} - -static __device__ void cpy_blck_f32_iq4_nl(const char * cxi, char * cdsti) { - const float * xi = (const float *) cxi; - block_iq4_nl * dsti = (block_iq4_nl *) cdsti; - - float amax = 0.0f; - float vmax = 0.0f; - - for (int j = 0; j < QK4_NL; ++j) { - const float v = xi[j]; - if (amax < fabsf(v)) { - amax = fabsf(v); - vmax = v; - } - } - - float d = vmax / kvalues_iq4nl[0]; - const float id = d ? 1.0f/d : 0.0f; - - float sumqx = 0, sumq2 = 0; - for (int j = 0; j < QK4_NL/2; ++j) { - const float x0 = xi[0 + j]*id; - const float x1 = xi[QK4_NL/2 + j]*id; - const uint8_t xi0 = best_index_int8(16, kvalues_iq4nl, x0); - const uint8_t xi1 = best_index_int8(16, kvalues_iq4nl, x1); - dsti->qs[j] = xi0 | (xi1 << 4); - const float v0 = kvalues_iq4nl[xi0]; - const float v1 = kvalues_iq4nl[xi1]; - const float w0 = xi[0 + j]*xi[0 + j]; - const float w1 = xi[QK4_NL/2 + j]*xi[QK4_NL/2 + j]; - sumqx += w0*v0*xi[j] + w1*v1*xi[QK4_NL/2 + j]; - sumq2 += w0*v0*v0 + w1*v1*v1; - } - - dsti->d = sumq2 > 0 ? sumqx/sumq2 : d; -} - - -template -static __global__ void cpy_f32_q(const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, - const int nb12, const int nb13) { - const int i = (blockDim.x*blockIdx.x + threadIdx.x)*qk; - - if (i >= ne) { - return; - } - - const int i03 = i/(ne00 * ne01 * ne02); - const int i02 = (i - i03*ne00*ne01*ne02 )/ (ne00*ne01); - const int i01 = (i - i03*ne00*ne01*ne02 - i02*ne01*ne00) / ne00; - const int i00 = i - i03*ne00*ne01*ne02 - i02*ne01*ne00 - i01*ne00; - const int x_offset = i00*nb00 + i01*nb01 + i02*nb02 + i03 * nb03; - - const int i13 = i/(ne10 * ne11 * ne12); - const int i12 = (i - i13*ne10*ne11*ne12) / (ne10*ne11); - const int i11 = (i - i13*ne10*ne11*ne12 - i12*ne10*ne11) / ne10; - const int i10 = i - i13*ne10*ne11*ne12 - i12*ne10*ne11 - i11*ne10; - const int dst_offset = (i10/qk)*nb10 + i11*nb11 + i12*nb12 + i13*nb13; - - cpy_blck(cx + x_offset, cdst + dst_offset); -} - -static __device__ float rope_yarn_ramp(const float low, const float high, const int i0) { - const float y = (i0 / 2 - low) / max(0.001f, high - low); - return 1.0f - min(1.0f, max(0.0f, y)); -} - -struct rope_corr_dims { - float v[4]; -}; - -// YaRN algorithm based on LlamaYaRNScaledRotaryEmbedding.py from https://github.com/jquesnelle/yarn -// MIT licensed. Copyright (c) 2023 Jeffrey Quesnelle and Bowen Peng. -static __device__ void rope_yarn( - float theta_extrap, float freq_scale, rope_corr_dims corr_dims, int64_t i0, float ext_factor, float mscale, - float * cos_theta, float * sin_theta -) { - // Get n-d rotational scaling corrected for extrapolation - float theta_interp = freq_scale * theta_extrap; - float theta = theta_interp; - if (ext_factor != 0.0f) { - float ramp_mix = rope_yarn_ramp(corr_dims.v[0], corr_dims.v[1], i0) * ext_factor; - theta = theta_interp * (1 - ramp_mix) + theta_extrap * ramp_mix; - - // Get n-d magnitude scaling corrected for interpolation - mscale *= 1.0f + 0.1f * logf(1.0f / freq_scale); - } - *cos_theta = cosf(theta) * mscale; - *sin_theta = sinf(theta) * mscale; -} - -// rope == RoPE == rotary positional embedding -template -static __global__ void rope( - const T * x, T * dst, int ncols, const int32_t * pos, float freq_scale, int p_delta_rows, float freq_base, - float ext_factor, float attn_factor, rope_corr_dims corr_dims -) { - const int col = 2*(blockDim.y*blockIdx.y + threadIdx.y); - - if (col >= ncols) { - return; - } - - const int row = blockDim.x*blockIdx.x + threadIdx.x; - const int i = row*ncols + col; - const int i2 = row/p_delta_rows; - - const int p = has_pos ? pos[i2] : 0; - const float theta_base = p*powf(freq_base, -float(col)/ncols); - - float cos_theta, sin_theta; - rope_yarn(theta_base, freq_scale, corr_dims, col, ext_factor, attn_factor, &cos_theta, &sin_theta); - - const float x0 = x[i + 0]; - const float x1 = x[i + 1]; - - dst[i + 0] = x0*cos_theta - x1*sin_theta; - dst[i + 1] = x0*sin_theta + x1*cos_theta; -} - -template -static __global__ void rope_neox( - const T * x, T * dst, int ncols, int n_dims, const int32_t * pos, float freq_scale, int p_delta_rows, - float ext_factor, float attn_factor, rope_corr_dims corr_dims, float theta_scale, float inv_ndims -) { - const int col = 2*(blockDim.y*blockIdx.y + threadIdx.y); - - if (col >= ncols) { - return; - } - - const int row = blockDim.x*blockIdx.x + threadIdx.x; - const int ib = col / n_dims; - const int ic = col % n_dims; - - if (ib > 0) { - const int i = row*ncols + ib*n_dims + ic; - - dst[i + 0] = x[i + 0]; - dst[i + 1] = x[i + 1]; - - return; - } - - const int i = row*ncols + ib*n_dims + ic/2; - const int i2 = row/p_delta_rows; - - float cur_rot = inv_ndims * ic - ib; - - const int p = has_pos ? pos[i2] : 0; - const float theta_base = p*freq_scale*powf(theta_scale, col/2.0f); - - float cos_theta, sin_theta; - rope_yarn(theta_base, freq_scale, corr_dims, cur_rot, ext_factor, attn_factor, &cos_theta, &sin_theta); - - const float x0 = x[i + 0]; - const float x1 = x[i + n_dims/2]; - - dst[i + 0] = x0*cos_theta - x1*sin_theta; - dst[i + n_dims/2] = x0*sin_theta + x1*cos_theta; -} - -static __global__ void rope_glm_f32( - const float * x, float * dst, int ncols, const int32_t * pos, float freq_scale, int p_delta_rows, float freq_base, - int n_ctx -) { - const int col = blockDim.x*blockIdx.x + threadIdx.x; - const int half_n_dims = ncols/4; - - if (col >= half_n_dims) { - return; - } - - const int row = blockDim.y*blockIdx.y + threadIdx.y; - const int i = row*ncols + col; - const int i2 = row/p_delta_rows; - - const float col_theta_scale = powf(freq_base, -2.0f*col/ncols); - // FIXME: this is likely wrong - const int p = pos != nullptr ? pos[i2] : 0; - - const float theta = min(p, n_ctx - 2)*freq_scale*col_theta_scale; - const float sin_theta = sinf(theta); - const float cos_theta = cosf(theta); - - const float x0 = x[i + 0]; - const float x1 = x[i + half_n_dims]; - - dst[i + 0] = x0*cos_theta - x1*sin_theta; - dst[i + half_n_dims] = x0*sin_theta + x1*cos_theta; - - const float block_theta = ((float)max(p - n_ctx - 2, 0))*col_theta_scale; - const float sin_block_theta = sinf(block_theta); - const float cos_block_theta = cosf(block_theta); - - const float x2 = x[i + half_n_dims * 2]; - const float x3 = x[i + half_n_dims * 3]; - - dst[i + half_n_dims * 2] = x2*cos_block_theta - x3*sin_block_theta; - dst[i + half_n_dims * 3] = x2*sin_block_theta + x3*cos_block_theta; -} - -static __global__ void alibi_f32(const float * x, float * dst, const int ncols, const int k_rows, - const int n_heads_log2_floor, const float m0, const float m1) { - const int col = blockDim.x*blockIdx.x + threadIdx.x; - - if (col >= ncols) { - return; - } - - const int row = blockDim.y*blockIdx.y + threadIdx.y; - const int i = row*ncols + col; - - const int k = row/k_rows; - - float m_k; - if (k < n_heads_log2_floor) { - m_k = powf(m0, k + 1); - } else { - m_k = powf(m1, 2 * (k - n_heads_log2_floor) + 1); - } - - dst[i] = col * m_k + x[i]; -} - -static __global__ void k_sum_rows_f32(const float * x, float * dst, const int ncols) { - const int row = blockIdx.x; - const int col = threadIdx.x; - - float sum = 0.0f; - for (int i = col; i < ncols; i += blockDim.x) { - sum += x[row * ncols + i]; - } - - sum = warp_reduce_sum(sum); - - if (col == 0) { - dst[row] = sum; - } -} - -template -static inline __device__ void ggml_cuda_swap(T & a, T & b) { - T tmp = a; - a = b; - b = tmp; -} - -template -static __global__ void k_argsort_f32_i32(const float * x, int * dst, const int ncols) { - // bitonic sort - int col = threadIdx.x; - int row = blockIdx.y; - - if (col >= ncols) return; - - const float * x_row = x + row * ncols; - int * dst_row = dst + row * ncols; - - // initialize indices - if (col < ncols) { - dst_row[col] = col; - } - __syncthreads(); - - for (int k = 2; k <= ncols; k *= 2) { - for (int j = k / 2; j > 0; j /= 2) { - int ixj = col ^ j; - if (ixj > col) { - if ((col & k) == 0) { - if (order == GGML_SORT_ORDER_ASC ? x_row[dst_row[col]] > x_row[dst_row[ixj]] : x_row[dst_row[col]] < x_row[dst_row[ixj]]) { - ggml_cuda_swap(dst_row[col], dst_row[ixj]); - } - } else { - if (order == GGML_SORT_ORDER_ASC ? x_row[dst_row[col]] < x_row[dst_row[ixj]] : x_row[dst_row[col]] > x_row[dst_row[ixj]]) { - ggml_cuda_swap(dst_row[col], dst_row[ixj]); - } - } - } - __syncthreads(); - } - } -} - -static __global__ void diag_mask_inf_f32(const float * x, float * dst, const int ncols, const int rows_per_channel, const int n_past) { - const int col = blockDim.y*blockIdx.y + threadIdx.y; - const int row = blockDim.x*blockIdx.x + threadIdx.x; - - if (col >= ncols) { - return; - } - - const int i = row*ncols + col; - //dst[i] = col > (n_past + row % rows_per_channel) ? -INFINITY : x[i]; - //dst[i] = x[i] - (col > n_past + row % rows_per_channel) * INT_MAX; // equivalent within rounding error but slightly faster on GPU - dst[i] = x[i] - (col > n_past + row % rows_per_channel) * FLT_MAX; -} - -template -static __global__ void soft_max_f32(const float * x, const float * mask, const float * pos, float * dst, const int ncols_par, const int nrows_y, const float scale, const float max_bias, const float m0, const float m1, uint32_t n_head_log2) { - const int ncols = ncols_template == 0 ? ncols_par : ncols_template; - - const int tid = threadIdx.x; - const int rowx = blockIdx.x; - const int rowy = rowx % nrows_y; // broadcast the mask in the row dimension - - const int block_size = block_size_template == 0 ? blockDim.x : block_size_template; - - const int warp_id = threadIdx.x / WARP_SIZE; - const int lane_id = threadIdx.x % WARP_SIZE; - - float slope = 0.0f; - - // ALiBi - if (max_bias > 0.0f) { - const int h = rowx/nrows_y; // head index - - const float base = h < n_head_log2 ? m0 : m1; - const int exp = h < n_head_log2 ? h + 1 : 2*(h - n_head_log2) + 1; - - slope = powf(base, exp); - } - - extern __shared__ float data_soft_max_f32[]; - float * buf_iw = data_soft_max_f32; // shared memory buffer for inter-warp communication - // shared memory buffer to cache values between iterations: - float * vals = vals_smem ? buf_iw + WARP_SIZE : dst + rowx*ncols; - - float max_val = -INFINITY; - -#pragma unroll - for (int col0 = 0; col0 < ncols; col0 += block_size) { - const int col = col0 + tid; - - if (ncols_template == 0 && col >= ncols) { - break; - } - - const int ix = rowx*ncols + col; - const int iy = rowy*ncols + col; - - const float val = x[ix]*scale + (mask ? mask[iy] : 0.0f) + (pos ? slope*pos[col] : 0.0f); - - vals[col] = val; - max_val = max(max_val, val); - } - - // find the max value in the block - max_val = warp_reduce_max(max_val); - if (block_size > WARP_SIZE) { - if (warp_id == 0) { - buf_iw[lane_id] = -INFINITY; - } - __syncthreads(); - - if (lane_id == 0) { - buf_iw[warp_id] = max_val; - } - __syncthreads(); - - max_val = buf_iw[lane_id]; - max_val = warp_reduce_max(max_val); - } - - float tmp = 0.0f; // partial sum - -#pragma unroll - for (int col0 = 0; col0 < ncols; col0 += block_size) { - const int col = col0 + tid; - - if (ncols_template == 0 && col >= ncols) { - break; - } - - const float val = expf(vals[col] - max_val); - tmp += val; - vals[col] = val; - } - - // find the sum of exps in the block - tmp = warp_reduce_sum(tmp); - if (block_size > WARP_SIZE) { - __syncthreads(); - if (warp_id == 0) { - buf_iw[lane_id] = 0.0f; - } - __syncthreads(); - - if (lane_id == 0) { - buf_iw[warp_id] = tmp; - } - __syncthreads(); - - tmp = buf_iw[lane_id]; - tmp = warp_reduce_sum(tmp); - } - - const float inv_sum = 1.0f / tmp; - -#pragma unroll - for (int col0 = 0; col0 < ncols; col0 += block_size) { - const int col = col0 + tid; - - if (ncols_template == 0 && col >= ncols) { - return; - } - - const int idst = rowx*ncols + col; - dst[idst] = vals[col] * inv_sum; - } -} - -static __global__ void scale_f32(const float * x, float * dst, const float scale, const int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - - dst[i] = scale * x[i]; -} - -static __global__ void clamp_f32(const float * x, float * dst, const float min, const float max, const int k) { - const int i = blockDim.x*blockIdx.x + threadIdx.x; - - if (i >= k) { - return; - } - - dst[i] = x[i] < min ? min : (x[i] > max ? max : x[i]); -} - -template -static __global__ void im2col_kernel( - const float * x, T * dst, int64_t batch_offset, - int64_t offset_delta, int64_t IC, int64_t IW, int64_t IH, int64_t OH, int64_t OW, int64_t KW, int64_t KH, int64_t pelements, int64_t CHW, - int s0, int s1, int p0, int p1, int d0, int d1) { - const int64_t i = threadIdx.x + blockIdx.x * blockDim.x; - if (i >= pelements) { - return; - } - - const int64_t ksize = OW * (KH > 1 ? KW : 1); - const int64_t kx = i / ksize; - const int64_t kd = kx * ksize; - const int64_t ky = (i - kd) / OW; - const int64_t ix = i % OW; - - const int64_t oh = blockIdx.y; - const int64_t batch = blockIdx.z / IC; - const int64_t ic = blockIdx.z % IC; - - const int64_t iiw = ix * s0 + kx * d0 - p0; - const int64_t iih = oh * s1 + ky * d1 - p1; - - const int64_t offset_dst = - ((batch * OH + oh) * OW + ix) * CHW + - (ic * (KW * KH) + ky * KW + kx); - - if (iih < 0 || iih >= IH || iiw < 0 || iiw >= IW) { - dst[offset_dst] = 0.0f; - } else { - const int64_t offset_src = ic * offset_delta + batch * batch_offset; - dst[offset_dst] = x[offset_src + iih * IW + iiw]; - } -} - -template -static __global__ void pool2d_nchw_kernel( - const int ih, const int iw, const int oh, const int ow, - const int kh, const int kw, const int sh, const int sw, - const int ph, const int pw, const int parallel_elements, - const Ti* src, To* dst, const enum ggml_op_pool op) { - int idx = threadIdx.x + blockIdx.x * blockDim.x; - if (idx >= parallel_elements) { - return; - } - - const int I_HW = ih * iw; - const int O_HW = oh * ow; - const int nc = idx / O_HW; - const int cur_oh = idx % O_HW / ow; - const int cur_ow = idx % O_HW % ow; - const Ti* i_ptr = src + nc * I_HW; - To* o_ptr = dst + nc * O_HW; - const int start_h = cur_oh * sh - ph; - const int bh = max(0, start_h); - const int eh = min(ih, start_h + kh); - const int start_w = cur_ow * sw - pw; - const int bw = max(0, start_w); - const int ew = min(iw, start_w + kw); - const To scale = 1. / (kh * kw); - To res = 0; - - switch (op) { - case GGML_OP_POOL_AVG: res = 0; break; - case GGML_OP_POOL_MAX: res = -FLT_MAX; break; - default: assert(false); - } - - for (int i = bh; i < eh; i += 1) { - for (int j = bw; j < ew; j += 1) { -#if __CUDA_ARCH__ >= 350 - Ti cur = __ldg(i_ptr + i * iw + j); -#else - Ti cur = i_ptr[i * iw + j]; -#endif - switch (op) { - case GGML_OP_POOL_AVG: res += cur * scale; break; - case GGML_OP_POOL_MAX: res = max(res, (To)cur); break; - default: assert(false); - } - } - } - o_ptr[cur_oh * ow + cur_ow] = res; -} - -template -static void get_rows_cuda(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const void * src0_dd, const int32_t * src1_dd, float * dst_dd, cudaStream_t stream) { - - GGML_TENSOR_BINARY_OP_LOCALS - - const dim3 block_dims(CUDA_GET_ROWS_BLOCK_SIZE, 1, 1); - const int block_num_x = (ne00 + 2*CUDA_GET_ROWS_BLOCK_SIZE - 1) / (2*CUDA_GET_ROWS_BLOCK_SIZE); - const dim3 block_nums(block_num_x, ne10, ne11*ne12); - - // strides in elements - //const size_t s0 = nb0 / ggml_element_size(dst); - const size_t s1 = nb1 / ggml_element_size(dst); - const size_t s2 = nb2 / ggml_element_size(dst); - const size_t s3 = nb3 / ggml_element_size(dst); - - const size_t s10 = nb10 / ggml_element_size(src1); - const size_t s11 = nb11 / ggml_element_size(src1); - const size_t s12 = nb12 / ggml_element_size(src1); - //const size_t s13 = nb13 / ggml_element_size(src1); - - GGML_ASSERT(ne00 % 2 == 0); - - k_get_rows<<>>( - src0_dd, src1_dd, dst_dd, - ne00, /*ne01, ne02, ne03,*/ - /*ne10, ne11,*/ ne12, /*ne13,*/ - /* s0,*/ s1, s2, s3, - /* nb00,*/ nb01, nb02, nb03, - s10, s11, s12/*, s13*/); - - GGML_UNUSED(dst); -} - -template -static void get_rows_cuda_float(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const src0_t * src0_dd, const int32_t * src1_dd, float * dst_dd, cudaStream_t stream) { - - GGML_TENSOR_BINARY_OP_LOCALS - - const dim3 block_dims(CUDA_GET_ROWS_BLOCK_SIZE, 1, 1); - const int block_num_x = (ne00 + CUDA_GET_ROWS_BLOCK_SIZE - 1) / CUDA_GET_ROWS_BLOCK_SIZE; - const dim3 block_nums(block_num_x, ne10, ne11*ne12); - - // strides in elements - //const size_t s0 = nb0 / ggml_element_size(dst); - const size_t s1 = nb1 / ggml_element_size(dst); - const size_t s2 = nb2 / ggml_element_size(dst); - const size_t s3 = nb3 / ggml_element_size(dst); - - const size_t s10 = nb10 / ggml_element_size(src1); - const size_t s11 = nb11 / ggml_element_size(src1); - const size_t s12 = nb12 / ggml_element_size(src1); - //const size_t s13 = nb13 / ggml_element_size(src1); - - k_get_rows_float<<>>( - src0_dd, src1_dd, dst_dd, - ne00, /*ne01, ne02, ne03,*/ - /*ne10, ne11,*/ ne12, /*ne13,*/ - /* s0,*/ s1, s2, s3, - /* nb00,*/ nb01, nb02, nb03, - s10, s11, s12/*, s13*/); - - GGML_UNUSED(dst); -} - -template -struct bin_bcast_cuda { - template - void operator()(const struct ggml_tensor * src0, const struct ggml_tensor * src1, struct ggml_tensor * dst, - const src0_t * src0_dd, const src1_t * src1_dd, dst_t * dst_dd, - cudaStream_t stream) { - - GGML_TENSOR_BINARY_OP_LOCALS - - int nr0 = ne10/ne0; - int nr1 = ne11/ne1; - int nr2 = ne12/ne2; - int nr3 = ne13/ne3; - - int nr[4] = { nr0, nr1, nr2, nr3 }; - - // collapse dimensions until first broadcast dimension - int64_t cne0[] = {ne0, ne1, ne2, ne3}; - int64_t cne1[] = {ne10, ne11, ne12, ne13}; - size_t cnb0[] = {nb0, nb1, nb2, nb3}; - size_t cnb1[] = {nb10, nb11, nb12, nb13}; - auto collapse = [](int64_t cne[]) { - cne[0] *= cne[1]; - cne[1] = cne[2]; - cne[2] = cne[3]; - cne[3] = 1; - }; - - auto collapse_nb = [](size_t cnb[], const int64_t cne[]) { - cnb[1] *= cne[1]; - cnb[2] *= cne[2]; - cnb[3] *= cne[3]; - }; - - for (int i = 0; i < 4; i++) { - if (nr[i] != 1) { - break; - } - if (i > 0) { - collapse_nb(cnb0, cne0); - collapse_nb(cnb1, cne1); - collapse(cne0); - collapse(cne1); - } - } - { - int64_t ne0 = cne0[0]; - int64_t ne1 = cne0[1]; - int64_t ne2 = cne0[2]; - int64_t ne3 = cne0[3]; - - int64_t ne10 = cne1[0]; - int64_t ne11 = cne1[1]; - int64_t ne12 = cne1[2]; - int64_t ne13 = cne1[3]; - - size_t nb0 = cnb0[0]; - size_t nb1 = cnb0[1]; - size_t nb2 = cnb0[2]; - size_t nb3 = cnb0[3]; - - size_t nb10 = cnb1[0]; - size_t nb11 = cnb1[1]; - size_t nb12 = cnb1[2]; - size_t nb13 = cnb1[3]; - - size_t s0 = nb0 / sizeof(dst_t); - size_t s1 = nb1 / sizeof(dst_t); - size_t s2 = nb2 / sizeof(dst_t); - size_t s3 = nb3 / sizeof(dst_t); - - size_t s10 = nb10 / sizeof(src1_t); - size_t s11 = nb11 / sizeof(src1_t); - size_t s12 = nb12 / sizeof(src1_t); - size_t s13 = nb13 / sizeof(src1_t); - - GGML_ASSERT(s0 == 1); - GGML_ASSERT(s10 == 1); - - const int block_size = 128; - - int64_t hne0 = std::max(ne0/2LL, 1LL); - - dim3 block_dims; - block_dims.x = std::min(hne0, block_size); - block_dims.y = std::min(ne1, block_size / block_dims.x); - block_dims.z = std::min(std::min(ne2*ne3, block_size / block_dims.x / block_dims.y), 64U); - - dim3 block_nums( - (hne0 + block_dims.x - 1) / block_dims.x, - (ne1 + block_dims.y - 1) / block_dims.y, - (ne2*ne3 + block_dims.z - 1) / block_dims.z - ); - - if (block_nums.z > 65535) { - // this is the maximum number of blocks in z direction, fallback to 1D grid kernel - int block_num = (ne0*ne1*ne2*ne3 + block_size - 1) / block_size; - k_bin_bcast_unravel<<>>( - src0_dd, src1_dd, dst_dd, - ne0, ne1, ne2, ne3, - ne10, ne11, ne12, ne13, - /* s0, */ s1, s2, s3, - /* s10, */ s11, s12, s13); - } else { - k_bin_bcast<<>>( - src0_dd, src1_dd, dst_dd, - ne0, ne1, ne2, ne3, - ne10, ne11, ne12, ne13, - /* s0, */ s1, s2, s3, - /* s10, */ s11, s12, s13); - } - } - } -}; - -static void acc_f32_cuda(const float * x, const float * y, float * dst, const int n_elements, - const int ne10, const int ne11, const int ne12, - const int nb1, const int nb2, const int offset, cudaStream_t stream) { - int num_blocks = (n_elements + CUDA_ACC_BLOCK_SIZE - 1) / CUDA_ACC_BLOCK_SIZE; - acc_f32<<>>(x, y, dst, n_elements, ne10, ne11, ne12, nb1, nb2, offset); -} - -static void gelu_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_GELU_BLOCK_SIZE - 1) / CUDA_GELU_BLOCK_SIZE; - gelu_f32<<>>(x, dst, k); -} - -static void silu_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_SILU_BLOCK_SIZE - 1) / CUDA_SILU_BLOCK_SIZE; - silu_f32<<>>(x, dst, k); -} - -static void gelu_quick_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_GELU_BLOCK_SIZE - 1) / CUDA_GELU_BLOCK_SIZE; - gelu_quick_f32<<>>(x, dst, k); -} - -static void tanh_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_TANH_BLOCK_SIZE - 1) / CUDA_TANH_BLOCK_SIZE; - tanh_f32<<>>(x, dst, k); -} - -static void relu_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_RELU_BLOCK_SIZE - 1) / CUDA_RELU_BLOCK_SIZE; - relu_f32<<>>(x, dst, k); -} - -static void hardsigmoid_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_HARDSIGMOID_BLOCK_SIZE - 1) / CUDA_HARDSIGMOID_BLOCK_SIZE; - hardsigmoid_f32<<>>(x, dst, k); -} - -static void hardswish_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_HARDSWISH_BLOCK_SIZE - 1) / CUDA_HARDSWISH_BLOCK_SIZE; - hardswish_f32<<>>(x, dst, k); -} - -static void leaky_relu_f32_cuda(const float * x, float * dst, const int k, const float negative_slope, cudaStream_t stream) { - const int num_blocks = (k + CUDA_RELU_BLOCK_SIZE - 1) / CUDA_RELU_BLOCK_SIZE; - leaky_relu_f32<<>>(x, dst, k, negative_slope); -} - -static void sqr_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_SQR_BLOCK_SIZE - 1) / CUDA_SQR_BLOCK_SIZE; - sqr_f32<<>>(x, dst, k); -} - -static void norm_f32_cuda(const float * x, float * dst, const int ncols, const int nrows, const float eps, cudaStream_t stream) { - GGML_ASSERT(ncols % WARP_SIZE == 0); - if (ncols < 1024) { - const dim3 block_dims(WARP_SIZE, 1, 1); - norm_f32<<>>(x, dst, ncols, eps); - } else { - const dim3 block_dims(1024, 1, 1); - norm_f32<1024><<>>(x, dst, ncols, eps); - } -} - -static void group_norm_f32_cuda(const float * x, float * dst, const int num_groups, const int group_size, const int ne_elements, cudaStream_t stream) { - static const float eps = 1e-6f; - if (group_size < 1024) { - const dim3 block_dims(WARP_SIZE, 1, 1); - group_norm_f32<<>>(x, dst, group_size, ne_elements, eps); - } else { - const dim3 block_dims(1024, 1, 1); - group_norm_f32<1024><<>>(x, dst, group_size, ne_elements, eps); - } -} - -static void concat_f32_cuda(const float * x, const float * y, float * dst, const int ne0, int ne1, int ne2, int ne02, cudaStream_t stream) { - int num_blocks = (ne0 + CUDA_CONCAT_BLOCK_SIZE - 1) / CUDA_CONCAT_BLOCK_SIZE; - dim3 gridDim(num_blocks, ne1, ne2); - concat_f32<<>>(x, y, dst, ne0, ne02); -} - -static void upscale_f32_cuda(const float * x, float * dst, const int ne00, const int ne01, const int ne02, const int ne03, - const int scale_factor, cudaStream_t stream) { - int ne0 = (ne00 * scale_factor); - int num_blocks = (ne0 + CUDA_UPSCALE_BLOCK_SIZE - 1) / CUDA_UPSCALE_BLOCK_SIZE; - dim3 gridDim(num_blocks, (ne01 * scale_factor), ne02*ne03); - upscale_f32<<>>(x, dst, ne00, ne00 * ne01, scale_factor); -} - -static void pad_f32_cuda(const float * x, float * dst, - const int ne00, const int ne01, const int ne02, const int ne03, - const int ne0, const int ne1, const int ne2, const int ne3, cudaStream_t stream) { - int num_blocks = (ne0 + CUDA_PAD_BLOCK_SIZE - 1) / CUDA_PAD_BLOCK_SIZE; - dim3 gridDim(num_blocks, ne1, ne2*ne3); - pad_f32<<>>(x, dst, ne0, ne00, ne01, ne02, ne03); -} - -static void arange_f32_cuda(float * dst, const int ne0, const float start, const float step, cudaStream_t stream) { - int num_blocks = (ne0 + CUDA_ARANGE_BLOCK_SIZE - 1) / CUDA_ARANGE_BLOCK_SIZE; - arange_f32<<>>(dst, ne0, start, step); -} - -static void timestep_embedding_f32_cuda(const float * x, float * dst, const int ne00, const int nb1, - const int dim, const int max_period, cudaStream_t stream) { - int half_ceil = (dim + 1) / 2; - int num_blocks = (half_ceil + CUDA_TIMESTEP_EMBEDDING_BLOCK_SIZE - 1) / CUDA_TIMESTEP_EMBEDDING_BLOCK_SIZE; - dim3 gridDim(num_blocks, ne00, 1); - timestep_embedding_f32<<>>(x, dst, nb1, dim, max_period); -} - -static void rms_norm_f32_cuda(const float * x, float * dst, const int ncols, const int nrows, const float eps, cudaStream_t stream) { - GGML_ASSERT(ncols % WARP_SIZE == 0); - if (ncols < 1024) { - const dim3 block_dims(WARP_SIZE, 1, 1); - rms_norm_f32<<>>(x, dst, ncols, eps); - } else { - const dim3 block_dims(1024, 1, 1); - rms_norm_f32<1024><<>>(x, dst, ncols, eps); - } -} - -static void quantize_row_q8_1_cuda(const float * x, void * vy, const int kx, const int ky, const int kx_padded, cudaStream_t stream) { - const int block_num_x = (kx_padded + CUDA_QUANTIZE_BLOCK_SIZE - 1) / CUDA_QUANTIZE_BLOCK_SIZE; - const dim3 num_blocks(block_num_x, ky, 1); - const dim3 block_size(CUDA_DEQUANTIZE_BLOCK_SIZE, 1, 1); - quantize_q8_1<<>>(x, vy, kx, kx_padded); -} - -template -static void dequantize_block_cuda(const void * __restrict__ vx, dst_t * __restrict__ y, const int k, cudaStream_t stream) { - const int num_blocks = (k + 2*CUDA_DEQUANTIZE_BLOCK_SIZE - 1) / (2*CUDA_DEQUANTIZE_BLOCK_SIZE); - dequantize_block<<>>(vx, y, k); -} - -static void dequantize_block_q8_0_f16_cuda(const void * __restrict__ vx, half * __restrict__ y, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_Q8_0_NE_ALIGN - 1) / CUDA_Q8_0_NE_ALIGN; - if (k % CUDA_Q8_0_NE_ALIGN == 0) { - const bool need_check = false; - dequantize_block_q8_0_f16<<>>(vx, y, k); - } else { - const bool need_check = true; - dequantize_block_q8_0_f16<<>>(vx, y, k); - } -} - -template -static void dequantize_row_q2_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; -#if QK_K == 256 - dequantize_block_q2_K<<>>(vx, y); -#else - dequantize_block_q2_K<<>>(vx, y); -#endif -} - -template -static void dequantize_row_q3_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; -#if QK_K == 256 - dequantize_block_q3_K<<>>(vx, y); -#else - dequantize_block_q3_K<<>>(vx, y); -#endif -} - -template -static void dequantize_row_q4_0_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb32 = k / 32; - const int nb = (k + 255) / 256; - dequantize_block_q4_0<<>>(vx, y, nb32); -} - -template -static void dequantize_row_q4_1_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb32 = k / 32; - const int nb = (k + 255) / 256; - dequantize_block_q4_1<<>>(vx, y, nb32); -} - -template -static void dequantize_row_q4_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; - dequantize_block_q4_K<<>>(vx, y); -} - -template -static void dequantize_row_q5_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; -#if QK_K == 256 - dequantize_block_q5_K<<>>(vx, y); -#else - dequantize_block_q5_K<<>>(vx, y); -#endif -} - -template -static void dequantize_row_q6_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; -#if QK_K == 256 - dequantize_block_q6_K<<>>(vx, y); -#else - dequantize_block_q6_K<<>>(vx, y); -#endif -} - -template -static void dequantize_row_iq2_xxs_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; - dequantize_block_iq2_xxs<<>>(vx, y); -} - -template -static void dequantize_row_iq2_xs_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; - dequantize_block_iq2_xs<<>>(vx, y); -} - -template -static void dequantize_row_iq2_s_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; - dequantize_block_iq2_s<<>>(vx, y); -} - -template -static void dequantize_row_iq3_xxs_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; - dequantize_block_iq3_xxs<<>>(vx, y); -} - -template -static void dequantize_row_iq3_s_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; - dequantize_block_iq3_s<<>>(vx, y); -} - -template -static void dequantize_row_iq1_s_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = k / QK_K; - dequantize_block_iq1_s<<>>(vx, y); -} - -template -static void dequantize_row_iq4_nl_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = (k + QK_K - 1) / QK_K; - dequantize_block_iq4_nl<<>>(vx, y); -} - -template -static void dequantize_row_iq4_xs_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { - const int nb = (k + QK_K - 1) / QK_K; -#if QK_K == 64 - dequantize_block_iq4_nl<<>>(vx, y); -#else - dequantize_block_iq4_xs<<>>(vx, y); -#endif -} - -template -static void convert_unary_cuda(const void * __restrict__ vx, dst_t * __restrict__ y, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_DEQUANTIZE_BLOCK_SIZE - 1) / CUDA_DEQUANTIZE_BLOCK_SIZE; - convert_unary<<>>(vx, y, k); -} - -static to_fp16_cuda_t ggml_get_to_fp16_cuda(ggml_type type) { - int id; - switch (type) { - case GGML_TYPE_Q4_0: - return dequantize_row_q4_0_cuda; - case GGML_TYPE_Q4_1: - return dequantize_row_q4_1_cuda; - case GGML_TYPE_Q5_0: - return dequantize_block_cuda; - case GGML_TYPE_Q5_1: - return dequantize_block_cuda; - case GGML_TYPE_Q8_0: - CUDA_CHECK(cudaGetDevice(&id)); - if (get_cuda_global_info().devices[id].cc >= CC_PASCAL) { - return dequantize_block_q8_0_f16_cuda; - } - return dequantize_block_cuda; - case GGML_TYPE_Q2_K: - return dequantize_row_q2_K_cuda; - case GGML_TYPE_Q3_K: - return dequantize_row_q3_K_cuda; - case GGML_TYPE_Q4_K: - return dequantize_row_q4_K_cuda; - case GGML_TYPE_Q5_K: - return dequantize_row_q5_K_cuda; - case GGML_TYPE_Q6_K: - return dequantize_row_q6_K_cuda; - case GGML_TYPE_IQ2_XXS: - return dequantize_row_iq2_xxs_cuda; - case GGML_TYPE_IQ2_XS: - return dequantize_row_iq2_xs_cuda; - case GGML_TYPE_IQ2_S: - return dequantize_row_iq2_s_cuda; - case GGML_TYPE_IQ3_XXS: - return dequantize_row_iq3_xxs_cuda; - case GGML_TYPE_IQ1_S: - return dequantize_row_iq1_s_cuda; - case GGML_TYPE_IQ4_NL: - return dequantize_row_iq4_nl_cuda; - case GGML_TYPE_IQ4_XS: - return dequantize_row_iq4_xs_cuda; - case GGML_TYPE_IQ3_S: - return dequantize_row_iq3_s_cuda; - case GGML_TYPE_F32: - return convert_unary_cuda; - default: - return nullptr; - } -} - -static to_fp32_cuda_t ggml_get_to_fp32_cuda(ggml_type type) { - switch (type) { - case GGML_TYPE_Q4_0: - return dequantize_row_q4_0_cuda; - case GGML_TYPE_Q4_1: - return dequantize_row_q4_1_cuda; - case GGML_TYPE_Q5_0: - return dequantize_block_cuda; - case GGML_TYPE_Q5_1: - return dequantize_block_cuda; - case GGML_TYPE_Q8_0: - return dequantize_block_cuda; - case GGML_TYPE_Q2_K: - return dequantize_row_q2_K_cuda; - case GGML_TYPE_Q3_K: - return dequantize_row_q3_K_cuda; - case GGML_TYPE_Q4_K: - return dequantize_row_q4_K_cuda; - case GGML_TYPE_Q5_K: - return dequantize_row_q5_K_cuda; - case GGML_TYPE_Q6_K: - return dequantize_row_q6_K_cuda; - case GGML_TYPE_IQ2_XXS: - return dequantize_row_iq2_xxs_cuda; - case GGML_TYPE_IQ2_XS: - return dequantize_row_iq2_xs_cuda; - case GGML_TYPE_IQ2_S: - return dequantize_row_iq2_s_cuda; - case GGML_TYPE_IQ3_XXS: - return dequantize_row_iq3_xxs_cuda; - case GGML_TYPE_IQ1_S: - return dequantize_row_iq1_s_cuda; - case GGML_TYPE_IQ4_NL: - return dequantize_row_iq4_nl_cuda; - case GGML_TYPE_IQ4_XS: - return dequantize_row_iq4_xs_cuda; - case GGML_TYPE_IQ3_S: - return dequantize_row_iq3_s_cuda; - case GGML_TYPE_F16: - return convert_unary_cuda; - default: - return nullptr; - } -} - -static void dequantize_mul_mat_vec_q4_0_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); - const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; - // the number of rows may exceed maximum grid size in the y or z dimensions, use the x dimension instead - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); - dequantize_mul_mat_vec - <<>>(vx, y, dst, ncols, nrows); -} - -static void dequantize_mul_mat_vec_q4_1_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); - const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); - dequantize_mul_mat_vec - <<>>(vx, y, dst, ncols, nrows); -} - -static void dequantize_mul_mat_vec_q5_0_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); - const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); - dequantize_mul_mat_vec - <<>>(vx, y, dst, ncols, nrows); -} - -static void dequantize_mul_mat_vec_q5_1_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); - const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); - dequantize_mul_mat_vec - <<>>(vx, y, dst, ncols, nrows); -} - -static void dequantize_mul_mat_vec_q8_0_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); - const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); - dequantize_mul_mat_vec - <<>>(vx, y, dst, ncols, nrows); -} - -static void dequantize_mul_mat_vec_q2_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % QK_K == 0); - const int ny = 2; // very slightly faster than 1 even when K_QUANTS_PER_ITERATION = 2 - const int block_num_y = (nrows + ny - 1) / ny; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(32, ny, 1); - dequantize_mul_mat_vec_q2_k<<>>(vx, y, dst, ncols, nrows); -} - -static void dequantize_mul_mat_vec_q3_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % QK_K == 0); - const int ny = 2 / K_QUANTS_PER_ITERATION; - const int block_num_y = (nrows + ny - 1) / ny; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(32, ny, 1); - dequantize_mul_mat_vec_q3_k<<>>(vx, y, dst, ncols, nrows); -} - -static void dequantize_mul_mat_vec_q4_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % QK_K == 0); - const int ny = 2 / K_QUANTS_PER_ITERATION; - const int block_num_y = (nrows + ny - 1) / ny; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(32, ny, 1); - dequantize_mul_mat_vec_q4_k<<>>(vx, y, dst, ncols, nrows); -} - -static void dequantize_mul_mat_vec_q5_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % QK_K == 0); - const dim3 block_dims(32, 1, 1); - dequantize_mul_mat_vec_q5_k<<>>(vx, y, dst, ncols); -} - -static void dequantize_mul_mat_vec_q6_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % QK_K == 0); - const int ny = 2 / K_QUANTS_PER_ITERATION; - const int block_num_y = (nrows + ny - 1) / ny; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(32, ny, 1); - dequantize_mul_mat_vec_q6_k<<>>(vx, y, dst, ncols, nrows); -} - -static void convert_mul_mat_vec_f16_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); - const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; - const dim3 block_nums(block_num_y, 1, 1); - const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); - dequantize_mul_mat_vec<1, 1, convert_f16> - <<>>(vx, y, dst, ncols, nrows); -} - -template -static void mul_mat_vec_q_cuda( - const void * vx, const void * vy, float * dst, - const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { - - GGML_ASSERT(ncols_x % qk == 0); - GGML_ASSERT(ncols_y <= MMVQ_MAX_BATCH_SIZE); - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - - int64_t nwarps = 1; - int64_t rows_per_cuda_block = 1; - - if (get_cuda_global_info().devices[id].cc < CC_RDNA2) { // NVIDIA and AMD older than RDNA2 - switch(ncols_y) { - case 1: - nwarps = 4; - rows_per_cuda_block = 1; - break; - case 2: - case 3: - case 4: - nwarps = 4; - rows_per_cuda_block = 2; - break; - case 5: - case 6: - case 7: - case 8: - nwarps = 2; - rows_per_cuda_block = 2; - break; - default: - GGML_ASSERT(false); - break; - } - } - const int64_t nblocks = (nrows_x + rows_per_cuda_block - 1) / rows_per_cuda_block; - const dim3 block_nums(nblocks, 1, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - switch (ncols_y) { - case 1: - mul_mat_vec_q<1, qk, qi, block_q_t, vdr, vec_dot> - <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); - break; - case 2: - mul_mat_vec_q<2, qk, qi, block_q_t, vdr, vec_dot> - <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); - break; - case 3: - mul_mat_vec_q<3, qk, qi, block_q_t, vdr, vec_dot> - <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); - break; - case 4: - mul_mat_vec_q<4, qk, qi, block_q_t, vdr, vec_dot> - <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); - break; - case 5: - mul_mat_vec_q<5, qk, qi, block_q_t, vdr, vec_dot> - <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); - break; - case 6: - mul_mat_vec_q<6, qk, qi, block_q_t, vdr, vec_dot> - <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); - break; - case 7: - mul_mat_vec_q<7, qk, qi, block_q_t, vdr, vec_dot> - <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); - break; - case 8: - mul_mat_vec_q<8, qk, qi, block_q_t, vdr, vec_dot> - <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); - break; - default: - GGML_ASSERT(false); - break; - } -} - -static void ggml_mul_mat_q4_0_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q4_0_RDNA2; - mmq_y = MMQ_Y_Q4_0_RDNA2; - nwarps = NWARPS_Q4_0_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q4_0_RDNA1; - mmq_y = MMQ_Y_Q4_0_RDNA1; - nwarps = NWARPS_Q4_0_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q4_0_AMPERE; - mmq_y = MMQ_Y_Q4_0_AMPERE; - nwarps = NWARPS_Q4_0_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q4_0_PASCAL; - mmq_y = MMQ_Y_Q4_0_PASCAL; - nwarps = NWARPS_Q4_0_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q4_0<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q4_0<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - -static void ggml_mul_mat_q4_1_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q4_1_RDNA2; - mmq_y = MMQ_Y_Q4_1_RDNA2; - nwarps = NWARPS_Q4_1_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q4_1_RDNA1; - mmq_y = MMQ_Y_Q4_1_RDNA1; - nwarps = NWARPS_Q4_1_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q4_1_AMPERE; - mmq_y = MMQ_Y_Q4_1_AMPERE; - nwarps = NWARPS_Q4_1_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q4_1_PASCAL; - mmq_y = MMQ_Y_Q4_1_PASCAL; - nwarps = NWARPS_Q4_1_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q4_1<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q4_1<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - -static void ggml_mul_mat_q5_0_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q5_0_RDNA2; - mmq_y = MMQ_Y_Q5_0_RDNA2; - nwarps = NWARPS_Q5_0_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q5_0_RDNA1; - mmq_y = MMQ_Y_Q5_0_RDNA1; - nwarps = NWARPS_Q5_0_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q5_0_AMPERE; - mmq_y = MMQ_Y_Q5_0_AMPERE; - nwarps = NWARPS_Q5_0_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q5_0_PASCAL; - mmq_y = MMQ_Y_Q5_0_PASCAL; - nwarps = NWARPS_Q5_0_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q5_0<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q5_0<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - -static void ggml_mul_mat_q5_1_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q5_1_RDNA2; - mmq_y = MMQ_Y_Q5_1_RDNA2; - nwarps = NWARPS_Q5_1_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q5_1_RDNA1; - mmq_y = MMQ_Y_Q5_1_RDNA1; - nwarps = NWARPS_Q5_1_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q5_1_AMPERE; - mmq_y = MMQ_Y_Q5_1_AMPERE; - nwarps = NWARPS_Q5_1_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q5_1_PASCAL; - mmq_y = MMQ_Y_Q5_1_PASCAL; - nwarps = NWARPS_Q5_1_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q5_1<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q5_1<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - -static void ggml_mul_mat_q8_0_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q8_0_RDNA2; - mmq_y = MMQ_Y_Q8_0_RDNA2; - nwarps = NWARPS_Q8_0_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q8_0_RDNA1; - mmq_y = MMQ_Y_Q8_0_RDNA1; - nwarps = NWARPS_Q8_0_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q8_0_AMPERE; - mmq_y = MMQ_Y_Q8_0_AMPERE; - nwarps = NWARPS_Q8_0_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q8_0_PASCAL; - mmq_y = MMQ_Y_Q8_0_PASCAL; - nwarps = NWARPS_Q8_0_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q8_0<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q8_0<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - -static void ggml_mul_mat_q2_K_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q2_K_RDNA2; - mmq_y = MMQ_Y_Q2_K_RDNA2; - nwarps = NWARPS_Q2_K_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q2_K_RDNA1; - mmq_y = MMQ_Y_Q2_K_RDNA1; - nwarps = NWARPS_Q2_K_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q2_K_AMPERE; - mmq_y = MMQ_Y_Q2_K_AMPERE; - nwarps = NWARPS_Q2_K_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q2_K_PASCAL; - mmq_y = MMQ_Y_Q2_K_PASCAL; - nwarps = NWARPS_Q2_K_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q2_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q2_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - -static void ggml_mul_mat_q3_K_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - -#if QK_K == 256 - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q3_K_RDNA2; - mmq_y = MMQ_Y_Q3_K_RDNA2; - nwarps = NWARPS_Q3_K_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q3_K_RDNA1; - mmq_y = MMQ_Y_Q3_K_RDNA1; - nwarps = NWARPS_Q3_K_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q3_K_AMPERE; - mmq_y = MMQ_Y_Q3_K_AMPERE; - nwarps = NWARPS_Q3_K_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q3_K_PASCAL; - mmq_y = MMQ_Y_Q3_K_PASCAL; - nwarps = NWARPS_Q3_K_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q3_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q3_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -#endif -} - -static void ggml_mul_mat_q4_K_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q4_K_RDNA2; - mmq_y = MMQ_Y_Q4_K_RDNA2; - nwarps = NWARPS_Q4_K_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q4_K_RDNA1; - mmq_y = MMQ_Y_Q4_K_RDNA1; - nwarps = NWARPS_Q4_K_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q4_K_AMPERE; - mmq_y = MMQ_Y_Q4_K_AMPERE; - nwarps = NWARPS_Q4_K_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q4_K_PASCAL; - mmq_y = MMQ_Y_Q4_K_PASCAL; - nwarps = NWARPS_Q4_K_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q4_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q4_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - -static void ggml_mul_mat_q5_K_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q5_K_RDNA2; - mmq_y = MMQ_Y_Q5_K_RDNA2; - nwarps = NWARPS_Q5_K_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q5_K_RDNA1; - mmq_y = MMQ_Y_Q5_K_RDNA1; - nwarps = NWARPS_Q5_K_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q5_K_AMPERE; - mmq_y = MMQ_Y_Q5_K_AMPERE; - nwarps = NWARPS_Q5_K_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q5_K_PASCAL; - mmq_y = MMQ_Y_Q5_K_PASCAL; - nwarps = NWARPS_Q5_K_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q5_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q5_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - -static void ggml_mul_mat_q6_K_q8_1_cuda( - const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, - const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - const int compute_capability = get_cuda_global_info().devices[id].cc; - - int mmq_x, mmq_y, nwarps; - if (compute_capability >= CC_RDNA2) { - mmq_x = MMQ_X_Q6_K_RDNA2; - mmq_y = MMQ_Y_Q6_K_RDNA2; - nwarps = NWARPS_Q6_K_RDNA2; - } else if (compute_capability >= CC_OFFSET_AMD) { - mmq_x = MMQ_X_Q6_K_RDNA1; - mmq_y = MMQ_Y_Q6_K_RDNA1; - nwarps = NWARPS_Q6_K_RDNA1; - } else if (compute_capability >= CC_VOLTA) { - mmq_x = MMQ_X_Q6_K_AMPERE; - mmq_y = MMQ_Y_Q6_K_AMPERE; - nwarps = NWARPS_Q6_K_AMPERE; - } else if (compute_capability >= MIN_CC_DP4A) { - mmq_x = MMQ_X_Q6_K_PASCAL; - mmq_y = MMQ_Y_Q6_K_PASCAL; - nwarps = NWARPS_Q6_K_PASCAL; - } else { - GGML_ASSERT(false); - } - - const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; - const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; - const dim3 block_nums(block_num_x, block_num_y, 1); - const dim3 block_dims(WARP_SIZE, nwarps, 1); - - if (nrows_x % mmq_y == 0) { - const bool need_check = false; - mul_mat_q6_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } else { - const bool need_check = true; - mul_mat_q6_K<<>> - (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); - } -} - static void ggml_mul_mat_p021_f16_f32_cuda( const void * vx, const float * y, float * dst, const int ncols_x, const int nrows_x, const int nchannels_x, const int nchannels_y, cudaStream_t stream) { @@ -8547,280 +1168,6 @@ static void ggml_mul_mat_vec_nc_f16_f32_cuda( (vx, y, dst, ncols_x, nrows_x, row_stride_x, channel_stride_x, nchannels_y/nchannels_x); } - -static void ggml_cpy_f16_f32_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - const int num_blocks = (ne + CUDA_CPY_BLOCK_SIZE - 1) / CUDA_CPY_BLOCK_SIZE; - cpy_f32_f16<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f32_f32_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - const int num_blocks = (ne + CUDA_CPY_BLOCK_SIZE - 1) / CUDA_CPY_BLOCK_SIZE; - cpy_f32_f16<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f32_f16_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - const int num_blocks = (ne + CUDA_CPY_BLOCK_SIZE - 1) / CUDA_CPY_BLOCK_SIZE; - cpy_f32_f16<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f32_q8_0_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - GGML_ASSERT(ne % QK8_0 == 0); - const int num_blocks = ne / QK8_0; - cpy_f32_q<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f32_q4_0_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - GGML_ASSERT(ne % QK4_0 == 0); - const int num_blocks = ne / QK4_0; - cpy_f32_q<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f32_q4_1_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - GGML_ASSERT(ne % QK4_1 == 0); - const int num_blocks = ne / QK4_1; - cpy_f32_q<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f32_q5_0_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - GGML_ASSERT(ne % QK5_0 == 0); - const int num_blocks = ne / QK5_0; - cpy_f32_q<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f32_q5_1_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - GGML_ASSERT(ne % QK5_1 == 0); - const int num_blocks = ne / QK5_1; - cpy_f32_q<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f32_iq4_nl_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - GGML_ASSERT(ne % QK4_NL == 0); - const int num_blocks = ne / QK4_NL; - cpy_f32_q<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - -static void ggml_cpy_f16_f16_cuda( - const char * cx, char * cdst, const int ne, - const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, - const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { - - const int num_blocks = (ne + CUDA_CPY_BLOCK_SIZE - 1) / CUDA_CPY_BLOCK_SIZE; - cpy_f32_f16<<>> - (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); -} - - - -static void scale_f32_cuda(const float * x, float * dst, const float scale, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_SCALE_BLOCK_SIZE - 1) / CUDA_SCALE_BLOCK_SIZE; - scale_f32<<>>(x, dst, scale, k); -} - -static void clamp_f32_cuda(const float * x, float * dst, const float min, const float max, const int k, cudaStream_t stream) { - const int num_blocks = (k + CUDA_CLAMP_BLOCK_SIZE - 1) / CUDA_CLAMP_BLOCK_SIZE; - clamp_f32<<>>(x, dst, min, max, k); -} - -template -static void rope_cuda( - const T * x, T * dst, int ncols, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, - float freq_base, float ext_factor, float attn_factor, rope_corr_dims corr_dims, cudaStream_t stream -) { - GGML_ASSERT(ncols % 2 == 0); - const dim3 block_dims(1, CUDA_ROPE_BLOCK_SIZE, 1); - const int num_blocks_x = (ncols + 2*CUDA_ROPE_BLOCK_SIZE - 1) / (2*CUDA_ROPE_BLOCK_SIZE); - const dim3 block_nums(nrows, num_blocks_x, 1); - if (pos == nullptr) { - rope<<>>( - x, dst, ncols, pos, freq_scale, p_delta_rows, freq_base, ext_factor, attn_factor, corr_dims - ); - } else { - rope<<>>( - x, dst, ncols, pos, freq_scale, p_delta_rows, freq_base, ext_factor, attn_factor, corr_dims - ); - } -} - -template -static void rope_neox_cuda( - const T * x, T * dst, int ncols, int n_dims, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, - float freq_base, float ext_factor, float attn_factor, rope_corr_dims corr_dims, cudaStream_t stream -) { - GGML_ASSERT(ncols % 2 == 0); - const dim3 block_dims(1, CUDA_ROPE_BLOCK_SIZE, 1); - const int num_blocks_x = (ncols + 2*CUDA_ROPE_BLOCK_SIZE - 1) / (2*CUDA_ROPE_BLOCK_SIZE); - const dim3 block_nums(nrows, num_blocks_x, 1); - - const float theta_scale = powf(freq_base, -2.0f/n_dims); - const float inv_ndims = -1.0f / n_dims; - - if (pos == nullptr) { - rope_neox<<>>( - x, dst, ncols, n_dims, pos, freq_scale, p_delta_rows, ext_factor, attn_factor, corr_dims, - theta_scale, inv_ndims - ); - } else { - rope_neox<<>>( - x, dst, ncols, n_dims, pos, freq_scale, p_delta_rows, ext_factor, attn_factor, corr_dims, - theta_scale, inv_ndims - ); - } -} - -static void rope_glm_f32_cuda( - const float * x, float * dst, int ncols, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, - float freq_base, int n_ctx, cudaStream_t stream -) { - GGML_ASSERT(ncols % 4 == 0); - const dim3 block_dims(CUDA_ROPE_BLOCK_SIZE/4, 1, 1); - const int num_blocks_x = (ncols + CUDA_ROPE_BLOCK_SIZE - 1) / CUDA_ROPE_BLOCK_SIZE; - const dim3 block_nums(num_blocks_x, nrows, 1); - rope_glm_f32<<>>(x, dst, ncols, pos, freq_scale, p_delta_rows, freq_base, n_ctx); -} - -static void alibi_f32_cuda(const float * x, float * dst, const int ncols, const int nrows, - const int k_rows, const int n_heads_log2_floor, const float m0, - const float m1, cudaStream_t stream) { - const dim3 block_dims(CUDA_ALIBI_BLOCK_SIZE, 1, 1); - const int num_blocks_x = (ncols + CUDA_ALIBI_BLOCK_SIZE - 1) / (CUDA_ALIBI_BLOCK_SIZE); - const dim3 block_nums(num_blocks_x, nrows, 1); - alibi_f32<<>>(x, dst, ncols, k_rows, n_heads_log2_floor, m0, m1); -} - -static void sum_rows_f32_cuda(const float * x, float * dst, const int ncols, const int nrows, cudaStream_t stream) { - const dim3 block_dims(WARP_SIZE, 1, 1); - const dim3 block_nums(nrows, 1, 1); - k_sum_rows_f32<<>>(x, dst, ncols); -} - -static void argsort_f32_i32_cuda(const float * x, int * dst, const int ncols, const int nrows, ggml_sort_order order, cudaStream_t stream) { - // bitonic sort requires ncols to be power of 2 - GGML_ASSERT((ncols & (ncols - 1)) == 0); - - const dim3 block_dims(ncols, 1, 1); - const dim3 block_nums(1, nrows, 1); - if (order == GGML_SORT_ORDER_ASC) { - k_argsort_f32_i32<<>>(x, dst, ncols); - } else if (order == GGML_SORT_ORDER_DESC) { - k_argsort_f32_i32<<>>(x, dst, ncols); - } else { - GGML_ASSERT(false); - } -} - -static void diag_mask_inf_f32_cuda(const float * x, float * dst, const int ncols_x, const int nrows_x, const int rows_per_channel, const int n_past, cudaStream_t stream) { - const dim3 block_dims(1, CUDA_DIAG_MASK_INF_BLOCK_SIZE, 1); - const int block_num_x = (ncols_x + CUDA_DIAG_MASK_INF_BLOCK_SIZE - 1) / CUDA_DIAG_MASK_INF_BLOCK_SIZE; - const dim3 block_nums(nrows_x, block_num_x, 1); - diag_mask_inf_f32<<>>(x, dst, ncols_x, rows_per_channel, n_past); -} - -static void soft_max_f32_cuda(const float * x, const float * mask, const float * pos, float * dst, const int ncols_x, const int nrows_x, const int nrows_y, const float scale, const float max_bias, cudaStream_t stream) { - int nth = WARP_SIZE; - while (nth < ncols_x && nth < CUDA_SOFT_MAX_BLOCK_SIZE) nth *= 2; - const dim3 block_dims(nth, 1, 1); - const dim3 block_nums(nrows_x, 1, 1); - const size_t shmem = (GGML_PAD(ncols_x, WARP_SIZE) + WARP_SIZE)*sizeof(float); - static_assert(CUDA_SOFT_MAX_BLOCK_SIZE == 1024, "These values need to be adjusted."); - - const uint32_t n_head_kv = nrows_x/nrows_y; - const uint32_t n_head_log2 = 1u << (uint32_t) floorf(log2f((float) n_head_kv)); - - const float m0 = powf(2.0f, -(max_bias ) / n_head_log2); - const float m1 = powf(2.0f, -(max_bias / 2.0f) / n_head_log2); - - if (shmem < get_cuda_global_info().devices[ggml_cuda_get_device()].smpb) { - switch (ncols_x) { - case 32: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - case 64: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - case 128: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - case 256: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - case 512: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - case 1024: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - case 2048: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - case 4096: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - default: - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - break; - } - } else { - const size_t shmem_low = WARP_SIZE*sizeof(float); - soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); - } -} - -template -static void im2col_cuda(const float * x, T* dst, - int64_t IW, int64_t IH, int64_t OW, int64_t OH, int64_t KW, int64_t KH, int64_t IC, - int64_t batch, int64_t batch_offset, int64_t offset_delta, - int s0,int s1,int p0,int p1,int d0,int d1, cudaStream_t stream) { - const int parallel_elements = OW * KW * KH; - const int num_blocks = (parallel_elements + CUDA_IM2COL_BLOCK_SIZE - 1) / CUDA_IM2COL_BLOCK_SIZE; - dim3 block_nums(num_blocks, OH, batch * IC); - im2col_kernel<<>>(x, dst, batch_offset, offset_delta, IC, IW, IH, OH, OW, KW, KH, parallel_elements, (IC * KH * KW), s0, s1, p0, p1, d0, d1); -} - static cudaError_t ggml_cuda_cpy_tensor_2d( void * dst, const struct ggml_tensor * src, int64_t i3, int64_t i2, int64_t i1_low, int64_t i1_high, cudaStream_t stream) { @@ -8857,670 +1204,6 @@ static cudaError_t ggml_cuda_cpy_tensor_2d( } } -static void ggml_cuda_op_get_rows( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_d, const float * src1_d, float * dst_d, cudaStream_t stream) { - - GGML_UNUSED(ctx); - - GGML_ASSERT(src1->type == GGML_TYPE_I32); - GGML_ASSERT(dst->type == GGML_TYPE_F32); - - GGML_ASSERT(src0->nb[0] == ggml_type_size(src0->type)); - GGML_ASSERT(src1->nb[0] == ggml_type_size(src1->type)); - GGML_ASSERT(dst->nb[0] == ggml_type_size(dst->type)); - - const int32_t * src1_i32 = (const int32_t *) src1_d; - - switch (src0->type) { - case GGML_TYPE_F16: - get_rows_cuda_float(src0, src1, dst, (const half *)src0_d, src1_i32, dst_d, stream); - break; - case GGML_TYPE_F32: - get_rows_cuda_float(src0, src1, dst, src0_d, src1_i32, dst_d, stream); - break; - case GGML_TYPE_Q4_0: - get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); - break; - case GGML_TYPE_Q4_1: - get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); - break; - case GGML_TYPE_Q5_0: - get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); - break; - case GGML_TYPE_Q5_1: - get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); - break; - case GGML_TYPE_Q8_0: - get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); - break; - default: - // TODO: k-quants - fprintf(stderr, "%s: unsupported type: %s\n", __func__, ggml_type_name(src0->type)); - GGML_ASSERT(false); - break; - } -} - -template -static void ggml_cuda_op_bin_bcast( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - - GGML_UNUSED(ctx); - - GGML_ASSERT(src1->type == GGML_TYPE_F32); - - if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) { - op()(src0, src1, dst, src0_dd, src1_dd, dst_dd, main_stream); - } else if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16) { - op()(src0, src1, dst, (const half *) src0_dd, src1_dd, (half *) dst_dd, main_stream); - } else if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F32) { - op()(src0, src1, dst, (const half *) src0_dd, src1_dd, dst_dd, main_stream); - } else { - fprintf(stderr, "%s: unsupported types: dst: %s, src0: %s, src1: %s\n", __func__, - ggml_type_name(dst->type), ggml_type_name(src0->type), ggml_type_name(src1->type)); - GGML_ASSERT(false); - } -} - -static void ggml_cuda_op_repeat( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_d, const float * src1_d, float * dst_d, cudaStream_t main_stream) { - - ggml_cuda_op_bin_bcast>(ctx, dst, src0, dst, nullptr, src0_d, dst_d, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(src1_d); -} - -static void ggml_cuda_op_add( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - - ggml_cuda_op_bin_bcast>(ctx, src0, src1, dst, src0_dd, src1_dd, dst_dd, main_stream); -} - -static void ggml_cuda_op_acc( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - - GGML_UNUSED(ctx); - - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT(src1->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - GGML_ASSERT(dst->ne[3] == 1); // just 3D tensors supported - - int nb1 = dst->op_params[0] / 4; // 4 bytes of float32 - int nb2 = dst->op_params[1] / 4; // 4 bytes of float32 - // int nb3 = dst->op_params[2] / 4; // 4 bytes of float32 - unused - int offset = dst->op_params[3] / 4; // offset in bytes - - acc_f32_cuda(src0_dd, src1_dd, dst_dd, ggml_nelements(dst), src1->ne[0], src1->ne[1], src1->ne[2], nb1, nb2, offset, main_stream); - - GGML_UNUSED(dst); -} - -static void ggml_cuda_op_mul( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - - ggml_cuda_op_bin_bcast>(ctx, src0, src1, dst, src0_dd, src1_dd, dst_dd, main_stream); -} - -static void ggml_cuda_op_div( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - - ggml_cuda_op_bin_bcast>(ctx, src0, src1, dst, src0_dd, src1_dd, dst_dd, main_stream); -} - -static void ggml_cuda_op_gelu( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - gelu_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_silu( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - silu_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_gelu_quick( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - gelu_quick_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_tanh( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - tanh_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_relu( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - relu_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_hardsigmoid( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - hardsigmoid_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_hardswish( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - hardswish_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_leaky_relu( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - float negative_slope; - memcpy(&negative_slope, dst->op_params, sizeof(float)); - - leaky_relu_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), negative_slope, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_sqr( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - sqr_f32_cuda(src0_dd, dst_dd, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_norm( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - const int64_t ne00 = src0->ne[0]; - const int64_t nrows = ggml_nrows(src0); - - float eps; - memcpy(&eps, dst->op_params, sizeof(float)); - - norm_f32_cuda(src0_dd, dst_dd, ne00, nrows, eps, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_group_norm( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - int num_groups = dst->op_params[0]; - int group_size = src0->ne[0] * src0->ne[1] * ((src0->ne[2] + num_groups - 1) / num_groups); - group_norm_f32_cuda(src0_dd, dst_dd, num_groups * src0->ne[3], group_size, ggml_nelements(src0), main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_concat( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT(src1->type == GGML_TYPE_F32); - GGML_ASSERT(dst->type == GGML_TYPE_F32); - - for (int i3 = 0; i3 < dst->ne[3]; i3++) { - concat_f32_cuda(src0_dd + i3 * (src0->nb[3] / 4), src1_dd + i3 * (src1->nb[3] / 4), dst_dd + i3 * (dst->nb[3] / 4), dst->ne[0], dst->ne[1], dst->ne[2], src0->ne[2], main_stream); - } - - GGML_UNUSED(src1); - GGML_UNUSED(dst); -} - -static void ggml_cuda_op_upscale( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT(dst->type == GGML_TYPE_F32); - GGML_ASSERT(src0->ne[3] == 1 && dst->ne[3] == 1); // just 3D tensors - - const int scale_factor = dst->op_params[0]; - - upscale_f32_cuda(src0_dd, dst_dd, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], scale_factor, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_pad( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT(dst->type == GGML_TYPE_F32); - GGML_ASSERT(src0->ne[3] == 1 && dst->ne[3] == 1); // just 3D tensors - - pad_f32_cuda(src0_dd, dst_dd, - src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], - dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3], main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_arange( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(dst->type == GGML_TYPE_F32); - - float start; - float stop; - float step; - memcpy(&start, (float *)dst->op_params + 0, sizeof(float)); - memcpy(&stop, (float *)dst->op_params + 1, sizeof(float)); - memcpy(&step, (float *)dst->op_params + 2, sizeof(float)); - - int64_t steps = (int64_t)ceil((stop - start) / step); - GGML_ASSERT(ggml_nelements(dst) == steps); - - arange_f32_cuda(dst_dd, dst->ne[0], start, step, main_stream); - - GGML_UNUSED(src0); - GGML_UNUSED(src1); - GGML_UNUSED(src0_dd); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_timestep_embedding( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT(dst->type == GGML_TYPE_F32); - - const int dim = dst->op_params[0]; - const int max_period = dst->op_params[1]; - - timestep_embedding_f32_cuda(src0_dd, dst_dd, src0->ne[0], dst->nb[1], dim, max_period, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_rms_norm( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - const int64_t ne00 = src0->ne[0]; - const int64_t nrows = ggml_nrows(src0); - - float eps; - memcpy(&eps, dst->op_params, sizeof(float)); - - rms_norm_f32_cuda(src0_dd, dst_dd, ne00, nrows, eps, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_mul_mat_q( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, - const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, - const int64_t src1_padded_row_size, cudaStream_t stream) { - - const int64_t ne00 = src0->ne[0]; - - const int64_t ne10 = src1->ne[0]; - GGML_ASSERT(ne10 % QK8_1 == 0); - - const int64_t ne0 = dst->ne[0]; - - const int64_t row_diff = row_high - row_low; - - int id = ggml_cuda_get_device(); - - // the main device has a larger memory buffer to hold the results from all GPUs - // nrows_dst == nrows of the matrix that the kernel writes into - const int64_t nrows_dst = id == ctx.device ? ne0 : row_diff; - - switch (src0->type) { - case GGML_TYPE_Q4_0: - ggml_mul_mat_q4_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q4_1: - ggml_mul_mat_q4_1_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q5_0: - ggml_mul_mat_q5_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q5_1: - ggml_mul_mat_q5_1_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q8_0: - ggml_mul_mat_q8_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q2_K: - ggml_mul_mat_q2_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q3_K: - ggml_mul_mat_q3_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q4_K: - ggml_mul_mat_q4_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q5_K: - ggml_mul_mat_q5_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - case GGML_TYPE_Q6_K: - ggml_mul_mat_q6_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); - break; - default: - GGML_ASSERT(false); - break; - } - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_ddf_i); -} - -static void ggml_cuda_op_mul_mat_vec_q( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, - const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, - const int64_t src1_padded_row_size, cudaStream_t stream) { - - const int64_t ne00 = src0->ne[0]; - const int64_t row_diff = row_high - row_low; - - const int64_t ne10 = src1->ne[0]; - GGML_ASSERT(ne10 % QK8_1 == 0); - - const int64_t ne0 = dst->ne[0]; - - int id; - CUDA_CHECK(cudaGetDevice(&id)); - - // the main device has a larger memory buffer to hold the results from all GPUs - // nrows_dst == nrows of the matrix that the kernel writes into - const int64_t nrows_dst = id == ctx.device ? ne0 : row_diff; - - switch (src0->type) { - case GGML_TYPE_Q4_0: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q4_1: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q5_0: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q5_1: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q8_0: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q2_K: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q3_K: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q4_K: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q5_K: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_Q6_K: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_IQ2_XXS: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_IQ2_XS: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_IQ2_S: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_IQ3_XXS: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_IQ1_S: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_IQ4_NL: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_IQ4_XS: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - case GGML_TYPE_IQ3_S: - mul_mat_vec_q_cuda - (src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); - break; - default: - GGML_ASSERT(false); - break; - } - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_ddf_i); - GGML_UNUSED(src1_ncols); - GGML_UNUSED(src1_padded_row_size); -} - -static void ggml_cuda_op_dequantize_mul_mat_vec( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, - const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, - const int64_t src1_padded_row_size, cudaStream_t stream) { - GGML_UNUSED(ctx); - const int64_t ne00 = src0->ne[0]; - const int64_t row_diff = row_high - row_low; - - GGML_ASSERT(src1->type == GGML_TYPE_F32); - - // on some GPUs it is faster to convert src1 to half and to use half precision intrinsics -#ifdef GGML_CUDA_F16 - ggml_cuda_pool_alloc src1_dfloat_a(ctx.pool()); - half * src1_dfloat = nullptr; // dfloat == half - - bool src1_convert_f16 = - src0->type == GGML_TYPE_Q4_0 || src0->type == GGML_TYPE_Q4_1 || - src0->type == GGML_TYPE_Q5_0 || src0->type == GGML_TYPE_Q5_1 || - src0->type == GGML_TYPE_Q8_0 || src0->type == GGML_TYPE_F16; - - if (src1_convert_f16) { - src1_dfloat = src1_dfloat_a.alloc(ne00); - const to_fp16_cuda_t to_fp16_cuda = ggml_get_to_fp16_cuda(src1->type); - GGML_ASSERT(to_fp16_cuda != nullptr); - to_fp16_cuda(src1_ddf_i, src1_dfloat, ne00, stream); - } -#else - const dfloat * src1_dfloat = (const dfloat *) src1_ddf_i; // dfloat == float, no conversion -#endif // GGML_CUDA_F16 - - switch (src0->type) { - case GGML_TYPE_Q4_0: - dequantize_mul_mat_vec_q4_0_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q4_1: - dequantize_mul_mat_vec_q4_1_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q5_0: - dequantize_mul_mat_vec_q5_0_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q5_1: - dequantize_mul_mat_vec_q5_1_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q8_0: - dequantize_mul_mat_vec_q8_0_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q2_K: - dequantize_mul_mat_vec_q2_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q3_K: - dequantize_mul_mat_vec_q3_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q4_K: - dequantize_mul_mat_vec_q4_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q5_K: - dequantize_mul_mat_vec_q5_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_Q6_K: - dequantize_mul_mat_vec_q6_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); - break; - case GGML_TYPE_F16: - convert_mul_mat_vec_f16_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); - break; - default: - GGML_ASSERT(false); - break; - } - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_ddq_i); - GGML_UNUSED(src1_ncols); - GGML_UNUSED(src1_padded_row_size); -} - static void ggml_cuda_op_mul_mat_cublas( ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, @@ -9544,7 +1227,7 @@ static void ggml_cuda_op_mul_mat_cublas( // ldc == nrows of the matrix that cuBLAS writes into int ldc = id == ctx.device ? ne0 : row_diff; - const int compute_capability = get_cuda_global_info().devices[id].cc; + const int compute_capability = ggml_cuda_info().devices[id].cc; if (compute_capability >= CC_VOLTA && (src0->type == GGML_TYPE_F16 || ggml_is_quantized(src0->type)) && ggml_is_contiguous(src0) && row_diff == src0->ne[1] && dst->op_params[0] == GGML_PREC_DEFAULT) { // convert src0 and src1 to fp16, multiply as fp16, convert dst to fp32 @@ -9621,345 +1304,6 @@ static void ggml_cuda_op_mul_mat_cublas( GGML_UNUSED(src1_padded_row_size); } -static void ggml_cuda_op_rope( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_F16); - GGML_ASSERT( dst->type == GGML_TYPE_F32 || dst->type == GGML_TYPE_F16); - GGML_ASSERT(src0->type == dst->type); - - const int64_t ne00 = src0->ne[0]; - const int64_t ne01 = src0->ne[1]; - const int64_t ne2 = dst->ne[2]; - const int64_t nrows = ggml_nrows(src0); - - //const int n_past = ((int32_t *) dst->op_params)[0]; - const int n_dims = ((int32_t *) dst->op_params)[1]; - const int mode = ((int32_t *) dst->op_params)[2]; - const int n_ctx = ((int32_t *) dst->op_params)[3]; - const int n_orig_ctx = ((int32_t *) dst->op_params)[4]; - - // RoPE alteration for extended context - float freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow; - memcpy(&freq_base, (int32_t *) dst->op_params + 5, sizeof(float)); - memcpy(&freq_scale, (int32_t *) dst->op_params + 6, sizeof(float)); - memcpy(&ext_factor, (int32_t *) dst->op_params + 7, sizeof(float)); - memcpy(&attn_factor, (int32_t *) dst->op_params + 8, sizeof(float)); - memcpy(&beta_fast, (int32_t *) dst->op_params + 9, sizeof(float)); - memcpy(&beta_slow, (int32_t *) dst->op_params + 10, sizeof(float)); - - const int32_t * pos = nullptr; - if ((mode & 1) == 0) { - GGML_ASSERT(src1->type == GGML_TYPE_I32); - GGML_ASSERT(src1->ne[0] == ne2); - pos = (const int32_t *) src1_dd; - } - - const bool is_neox = mode & 2; - const bool is_glm = mode & 4; - - rope_corr_dims corr_dims; - ggml_rope_yarn_corr_dims(n_dims, n_orig_ctx, freq_base, beta_fast, beta_slow, corr_dims.v); - - // compute - if (is_glm) { - GGML_ASSERT(false); - rope_glm_f32_cuda(src0_dd, dst_dd, ne00, nrows, pos, freq_scale, ne01, freq_base, n_ctx, main_stream); - } else if (is_neox) { - if (src0->type == GGML_TYPE_F32) { - rope_neox_cuda( - (const float *)src0_dd, (float *)dst_dd, ne00, n_dims, nrows, pos, freq_scale, ne01, freq_base, ext_factor, - attn_factor, corr_dims, main_stream - ); - } else if (src0->type == GGML_TYPE_F16) { - rope_neox_cuda( - (const half *)src0_dd, (half *)dst_dd, ne00, n_dims, nrows, pos, freq_scale, ne01, freq_base, ext_factor, - attn_factor, corr_dims, main_stream - ); - } else { - GGML_ASSERT(false); - } - } else { - if (src0->type == GGML_TYPE_F32) { - rope_cuda( - (const float *)src0_dd, (float *)dst_dd, ne00, nrows, pos, freq_scale, ne01, freq_base, ext_factor, - attn_factor, corr_dims, main_stream - ); - } else if (src0->type == GGML_TYPE_F16) { - rope_cuda( - (const half *)src0_dd, (half *)dst_dd, ne00, nrows, pos, freq_scale, ne01, freq_base, ext_factor, - attn_factor, corr_dims, main_stream - ); - } else { - GGML_ASSERT(false); - } - } - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_alibi( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - const int64_t ne00 = src0->ne[0]; - const int64_t ne01 = src0->ne[1]; - const int64_t ne02 = src0->ne[2]; - const int64_t nrows = ggml_nrows(src0); - - //const int n_past = ((int32_t *) dst->op_params)[0]; - const int n_head = ((int32_t *) dst->op_params)[1]; - float max_bias; - memcpy(&max_bias, (int32_t *) dst->op_params + 2, sizeof(float)); - - //GGML_ASSERT(ne01 + n_past == ne00); - GGML_ASSERT(n_head == ne02); - - const int n_heads_log2_floor = 1 << (int) floor(log2(n_head)); - - const float m0 = powf(2.0f, -(max_bias) / n_heads_log2_floor); - const float m1 = powf(2.0f, -(max_bias / 2.0f) / n_heads_log2_floor); - - alibi_f32_cuda(src0_dd, dst_dd, ne00, nrows, ne01, n_heads_log2_floor, m0, m1, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_pool2d( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - const int32_t * opts = (const int32_t *)dst->op_params; - enum ggml_op_pool op = static_cast(opts[0]); - const int k0 = opts[1]; - const int k1 = opts[2]; - const int s0 = opts[3]; - const int s1 = opts[4]; - const int p0 = opts[5]; - const int p1 = opts[6]; - - const int64_t IH = src0->ne[1]; - const int64_t IW = src0->ne[0]; - - const int64_t N = dst->ne[3]; - const int64_t OC = dst->ne[2]; - const int64_t OH = dst->ne[1]; - const int64_t OW = dst->ne[0]; - - const int parallel_elements = N * OC * OH * OW; - const int num_blocks = (parallel_elements + CUDA_POOL2D_BLOCK_SIZE - 1) / CUDA_POOL2D_BLOCK_SIZE; - dim3 block_nums(num_blocks); - pool2d_nchw_kernel<<>>(IH, IW, OH, OW, k1, k0, s1, s0, p1, p0, parallel_elements, src0_dd, dst_dd, op); - - GGML_UNUSED(src1); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_im2col( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F16); - GGML_ASSERT(src1->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F16 || dst->type == GGML_TYPE_F32); - - const int32_t s0 = ((const int32_t*)(dst->op_params))[0]; - const int32_t s1 = ((const int32_t*)(dst->op_params))[1]; - const int32_t p0 = ((const int32_t*)(dst->op_params))[2]; - const int32_t p1 = ((const int32_t*)(dst->op_params))[3]; - const int32_t d0 = ((const int32_t*)(dst->op_params))[4]; - const int32_t d1 = ((const int32_t*)(dst->op_params))[5]; - - const bool is_2D = ((const int32_t*)(dst->op_params))[6] == 1; - - const int64_t IC = src1->ne[is_2D ? 2 : 1]; - const int64_t IH = is_2D ? src1->ne[1] : 1; - const int64_t IW = src1->ne[0]; - - const int64_t KH = is_2D ? src0->ne[1] : 1; - const int64_t KW = src0->ne[0]; - - const int64_t OH = is_2D ? dst->ne[2] : 1; - const int64_t OW = dst->ne[1]; - - const size_t delta_offset = src1->nb[is_2D ? 2 : 1] / 4; // nb is byte offset, src is type float32 - const int64_t batch = src1->ne[3]; - const size_t batch_offset = src1->nb[3] / 4; // nb is byte offset, src is type float32 - - if(dst->type == GGML_TYPE_F16) { - im2col_cuda(src1_dd, (half*) dst_dd, IW, IH, OW, OH, KW, KH, IC, batch, batch_offset, delta_offset, s0, s1, p0, p1, d0, d1, main_stream); - } else { - im2col_cuda(src1_dd, (float*) dst_dd, IW, IH, OW, OH, KW, KH, IC, batch, batch_offset, delta_offset, s0, s1, p0, p1, d0, d1, main_stream); - } - - GGML_UNUSED(src0); - GGML_UNUSED(src0_dd); -} - -static void ggml_cuda_op_sum_rows( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - const int64_t ncols = src0->ne[0]; - const int64_t nrows = ggml_nrows(src0); - - sum_rows_f32_cuda(src0_dd, dst_dd, ncols, nrows, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_argsort( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_I32); - - const int64_t ncols = src0->ne[0]; - const int64_t nrows = ggml_nrows(src0); - - enum ggml_sort_order order = (enum ggml_sort_order) dst->op_params[0]; - - argsort_f32_i32_cuda(src0_dd, (int *)dst_dd, ncols, nrows, order, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_diag_mask_inf( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - const int64_t ne00 = src0->ne[0]; - const int64_t ne01 = src0->ne[1]; - const int nrows0 = ggml_nrows(src0); - - const int n_past = ((int32_t *) dst->op_params)[0]; - - diag_mask_inf_f32_cuda(src0_dd, dst_dd, ne00, nrows0, ne01, n_past, main_stream); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_soft_max( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - GGML_ASSERT(!src1 || src1->type == GGML_TYPE_F32); // src1 contains mask and it is optional - - const int64_t ne00 = src0->ne[0]; - const int64_t nrows_x = ggml_nrows(src0); - const int64_t nrows_y = src0->ne[1]; - - float scale = 1.0f; - float max_bias = 0.0f; - - memcpy(&scale, (float *) dst->op_params + 0, sizeof(float)); - memcpy(&max_bias, (float *) dst->op_params + 1, sizeof(float)); - - // positions tensor - float * src2_dd = nullptr; - - ggml_tensor * src2 = dst->src[2]; - const bool use_src2 = src2 != nullptr; - - if (use_src2) { - src2_dd = (float *)src2->data; - } - - soft_max_f32_cuda(src0_dd, src1 ? src1_dd : nullptr, src2_dd, dst_dd, ne00, nrows_x, nrows_y, scale, max_bias, main_stream); -} - -static void ggml_cuda_op_scale( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - float scale; - memcpy(&scale, dst->op_params, sizeof(float)); - - scale_f32_cuda(src0_dd, dst_dd, scale, ggml_nelements(src0), main_stream); - CUDA_CHECK(cudaGetLastError()); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -static void ggml_cuda_op_clamp( - ggml_backend_cuda_context & ctx, - const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, - const float * src0_dd, const float * src1_dd, float * dst_dd, cudaStream_t main_stream) { - GGML_UNUSED(ctx); - GGML_ASSERT(src0->type == GGML_TYPE_F32); - GGML_ASSERT( dst->type == GGML_TYPE_F32); - - float min; - float max; - memcpy(&min, dst->op_params, sizeof(float)); - memcpy(&max, (float *) dst->op_params + 1, sizeof(float)); - - clamp_f32_cuda(src0_dd, dst_dd, min, max, ggml_nelements(src0), main_stream); - CUDA_CHECK(cudaGetLastError()); - - GGML_UNUSED(src1); - GGML_UNUSED(dst); - GGML_UNUSED(src1_dd); -} - -// TODO: remove this function -static void ggml_cuda_op_flatten(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const ggml_cuda_op_flatten_t op) { - GGML_ASSERT(!src0 || ggml_backend_buffer_is_cuda(src0->buffer)); - GGML_ASSERT(!src1 || ggml_backend_buffer_is_cuda(src1->buffer)); - GGML_ASSERT( ggml_backend_buffer_is_cuda(dst->buffer)); - - // dd = data device - float * src0_ddf = src0 ? (float *) src0->data : nullptr; - float * src1_ddf = src1 ? (float *) src1->data : nullptr; - float * dst_ddf = (float *) dst->data; - - ggml_cuda_set_device(ctx.device); - - // do the computation - op(ctx, src0, src1, dst, src0_ddf, src1_ddf, dst_ddf, ctx.stream()); - CUDA_CHECK(cudaGetLastError()); -} - static void ggml_cuda_set_peer_access(const int n_tokens, int main_device) { static bool peer_access_enabled = false; @@ -10003,6 +1347,8 @@ static void ggml_cuda_set_peer_access(const int n_tokens, int main_device) { } } } + + ggml_cuda_set_device(main_device); #endif // NDEBUG peer_access_enabled = enable_peer_access; @@ -10299,97 +1645,6 @@ static void ggml_cuda_op_mul_mat( } } -static void ggml_cuda_repeat(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_repeat); -} - -static void ggml_cuda_get_rows(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_get_rows); -} - -static void ggml_cuda_add(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_add); -} - -static void ggml_cuda_acc(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_acc); -} - -static void ggml_cuda_mul(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_mul); -} - -static void ggml_cuda_div(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_div); -} - -static void ggml_cuda_gelu(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_gelu); -} - -static void ggml_cuda_silu(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_silu); -} - -static void ggml_cuda_gelu_quick(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_gelu_quick); -} - -static void ggml_cuda_tanh(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_tanh); -} - -static void ggml_cuda_relu(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_relu); -} - -static void ggml_cuda_hardsigmoid(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_hardsigmoid); -} - -static void ggml_cuda_hardswish(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_hardswish); -} -static void ggml_cuda_leaky_relu(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_leaky_relu); -} - -static void ggml_cuda_sqr(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_sqr); -} - -static void ggml_cuda_norm(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_norm); -} - -static void ggml_cuda_group_norm(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_group_norm); -} - -static void ggml_cuda_concat(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_concat); -} - -static void ggml_cuda_upscale(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_upscale); -} - -static void ggml_cuda_pad(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_pad); -} - -static void ggml_cuda_arange(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_arange); -} - -static void ggml_cuda_timestep_embedding(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_timestep_embedding); -} - -static void ggml_cuda_rms_norm(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_rms_norm); -} - static void ggml_cuda_mul_mat_vec_p021(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst){ GGML_ASSERT(ggml_is_permuted(src0) && ggml_is_permuted(src1)); GGML_ASSERT(ggml_backend_buffer_is_cuda(src0->buffer)); @@ -10404,7 +1659,6 @@ static void ggml_cuda_mul_mat_vec_p021(ggml_backend_cuda_context & ctx, const gg const int64_t ne12 = src1->ne[2]; - ggml_cuda_set_device(ctx.device); cudaStream_t main_stream = ctx.stream(); void * src0_ddq = src0->data; @@ -10431,7 +1685,6 @@ static void ggml_cuda_mul_mat_vec_nc(ggml_backend_cuda_context & ctx, const ggml const int64_t ne12 = src1->ne[2]; - ggml_cuda_set_device(ctx.device); cudaStream_t main_stream = ctx.stream(); void * src0_ddq = src0->data; @@ -10479,7 +1732,6 @@ static void ggml_cuda_mul_mat_batched_cublas(ggml_backend_cuda_context & ctx, co const int64_t ne_dst = ggml_nelements(dst); - ggml_cuda_set_device(ctx.device); cudaStream_t main_stream = ctx.stream(); CUBLAS_CHECK(cublasSetStream(ctx.cublas_handle(), main_stream)); @@ -10626,16 +1878,16 @@ static void ggml_cuda_mul_mat(ggml_backend_cuda_context & ctx, const ggml_tensor continue; } - if (min_compute_capability > get_cuda_global_info().devices[id].cc) { - min_compute_capability = get_cuda_global_info().devices[id].cc; + if (min_compute_capability > ggml_cuda_info().devices[id].cc) { + min_compute_capability = ggml_cuda_info().devices[id].cc; } - if (get_cuda_global_info().devices[id].cc == 610) { + if (ggml_cuda_info().devices[id].cc == 610) { any_pascal_with_slow_fp16 = true; } } } else { - min_compute_capability = get_cuda_global_info().devices[ctx.device].cc; - any_pascal_with_slow_fp16 = get_cuda_global_info().devices[ctx.device].cc == 610; + min_compute_capability = ggml_cuda_info().devices[ctx.device].cc; + any_pascal_with_slow_fp16 = ggml_cuda_info().devices[ctx.device].cc == 610; } // check data types and tensor shapes for custom matrix multiplication kernels: @@ -10889,11 +2141,14 @@ static void ggml_cuda_mul_mat_id_cublas(ggml_tensor * dst) { } #endif -static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { +static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { #if 0 ggml_cuda_mul_mat_id_cublas(dst); // TODO: mmq/mmv support #endif + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + cudaStream_t stream = ctx.stream(); const size_t nb11 = src1->nb[1]; @@ -10987,281 +2242,159 @@ static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, const ggml_ten } } -static void ggml_cuda_scale(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_scale); -} - -static void ggml_cuda_clamp(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_clamp); -} - -static void ggml_cuda_cpy(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - const int64_t ne = ggml_nelements(src0); - GGML_ASSERT(ne == ggml_nelements(src1)); - - GGML_ASSERT(ggml_backend_buffer_is_cuda(src0->buffer)); - - GGML_ASSERT(ggml_nbytes(src0) <= INT_MAX); - GGML_ASSERT(ggml_nbytes(src1) <= INT_MAX); - - const int64_t ne00 = src0->ne[0]; - const int64_t ne01 = src0->ne[1]; - const int64_t ne02 = src0->ne[2]; - - //GGML_ASSERT(src0->ne[3] == 1); - - const int64_t nb00 = src0->nb[0]; - const int64_t nb01 = src0->nb[1]; - const int64_t nb02 = src0->nb[2]; - const int64_t nb03 = src0->nb[3]; - - const int64_t ne10 = src1->ne[0]; - const int64_t ne11 = src1->ne[1]; - const int64_t ne12 = src1->ne[2]; - - //GGML_ASSERT(src1->ne[3] == 1); - - const int64_t nb10 = src1->nb[0]; - const int64_t nb11 = src1->nb[1]; - const int64_t nb12 = src1->nb[2]; - const int64_t nb13 = src1->nb[3]; - - ggml_cuda_set_device(ctx.device); - cudaStream_t main_stream = ctx.stream(); - - char * src0_ddc = (char *) src0->data; - char * src1_ddc = (char *) src1->data; - - if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_F32) { - ggml_cpy_f32_f32_cuda (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_F16) { - ggml_cpy_f32_f16_cuda (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q8_0) { - ggml_cpy_f32_q8_0_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q4_0) { - ggml_cpy_f32_q4_0_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q4_1) { - ggml_cpy_f32_q4_1_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q5_0) { - ggml_cpy_f32_q5_0_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_IQ4_NL) { - ggml_cpy_f32_iq4_nl_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q5_1) { - ggml_cpy_f32_q5_1_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F16) { - ggml_cpy_f16_f16_cuda (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F32) { - ggml_cpy_f16_f32_cuda (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); - } else { - fprintf(stderr, "%s: unsupported type combination (%s to %s)\n", __func__, - ggml_type_name(src0->type), ggml_type_name(src1->type)); - GGML_ASSERT(false); +static bool ggml_cuda_compute_forward(ggml_backend_cuda_context & ctx, struct ggml_tensor * dst) { + // why is this here instead of mul_mat? + if (dst->src[0] != nullptr && ggml_backend_buffer_is_cuda_split(dst->src[0]->buffer)) { + ggml_cuda_set_peer_access(dst->src[1]->ne[1], ctx.device); } - GGML_UNUSED(dst); -} - -static void ggml_cuda_dup(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - // TODO: why do we pass dst as src1 here? - ggml_cuda_cpy(ctx, src0, dst, nullptr); - GGML_UNUSED(src1); -} - -static void ggml_cuda_diag_mask_inf(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_diag_mask_inf); -} - -static void ggml_cuda_soft_max(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_soft_max); -} - -static void ggml_cuda_rope(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - GGML_ASSERT(ggml_is_contiguous(src0)); // TODO: this restriction is temporary until non-cont support is implemented - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_rope); -} - -static void ggml_cuda_alibi(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_alibi); -} - -static void ggml_cuda_pool2d(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_pool2d); -} - -static void ggml_cuda_im2col(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_im2col); -} - -static void ggml_cuda_sum_rows(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - GGML_ASSERT(ggml_is_contiguous(src0)); - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_sum_rows); -} - -static void ggml_cuda_argsort(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - GGML_ASSERT(ggml_is_contiguous(src0)); - ggml_cuda_op_flatten(ctx, src0, src1, dst, ggml_cuda_op_argsort); -} - -static void ggml_cuda_nop(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { - GGML_UNUSED(ctx); - GGML_UNUSED(src0); - GGML_UNUSED(src1); - GGML_UNUSED(dst); -} - -static bool ggml_cuda_compute_forward(ggml_backend_cuda_context & ctx, struct ggml_tensor * tensor) { - // FIXME: where should this be? - if (tensor->src[0] != nullptr && ggml_backend_buffer_is_cuda_split(tensor->src[0]->buffer)) { - ggml_cuda_set_peer_access(tensor->src[1]->ne[1], ctx.device); - } - - ggml_cuda_func_t func; - - switch (tensor->op) { + switch (dst->op) { case GGML_OP_REPEAT: - func = ggml_cuda_repeat; + ggml_cuda_op_repeat(ctx, dst); break; case GGML_OP_GET_ROWS: - func = ggml_cuda_get_rows; + ggml_cuda_op_get_rows(ctx, dst); break; case GGML_OP_DUP: - func = ggml_cuda_dup; + ggml_cuda_dup(ctx, dst); + break; + case GGML_OP_CPY: + ggml_cuda_cpy(ctx, dst->src[0], dst->src[1]); + break; + case GGML_OP_CONT: + ggml_cuda_dup(ctx, dst); break; case GGML_OP_ADD: - func = ggml_cuda_add; + ggml_cuda_op_add(ctx, dst); break; case GGML_OP_ACC: - func = ggml_cuda_acc; + ggml_cuda_op_acc(ctx, dst); break; case GGML_OP_MUL: - func = ggml_cuda_mul; + ggml_cuda_op_mul(ctx, dst); break; case GGML_OP_DIV: - func = ggml_cuda_div; + ggml_cuda_op_div(ctx, dst); break; case GGML_OP_UNARY: - switch (ggml_get_unary_op(tensor)) { + switch (ggml_get_unary_op(dst)) { case GGML_UNARY_OP_GELU: - func = ggml_cuda_gelu; + ggml_cuda_op_gelu(ctx, dst); break; case GGML_UNARY_OP_SILU: - func = ggml_cuda_silu; + ggml_cuda_op_silu(ctx, dst); break; case GGML_UNARY_OP_GELU_QUICK: - func = ggml_cuda_gelu_quick; + ggml_cuda_op_gelu_quick(ctx, dst); break; case GGML_UNARY_OP_TANH: - func = ggml_cuda_tanh; + ggml_cuda_op_tanh(ctx, dst); break; case GGML_UNARY_OP_RELU: - func = ggml_cuda_relu; + ggml_cuda_op_relu(ctx, dst); break; case GGML_UNARY_OP_HARDSIGMOID: - func = ggml_cuda_hardsigmoid; + ggml_cuda_op_hardsigmoid(ctx, dst); break; case GGML_UNARY_OP_HARDSWISH: - func = ggml_cuda_hardswish; + ggml_cuda_op_hardswish(ctx, dst); break; default: return false; } break; case GGML_OP_NORM: - func = ggml_cuda_norm; + ggml_cuda_op_norm(ctx, dst); break; case GGML_OP_GROUP_NORM: - func = ggml_cuda_group_norm; + ggml_cuda_op_group_norm(ctx, dst); break; case GGML_OP_CONCAT: - func = ggml_cuda_concat; + ggml_cuda_op_concat(ctx, dst); break; case GGML_OP_UPSCALE: - func = ggml_cuda_upscale; + ggml_cuda_op_upscale(ctx, dst); break; case GGML_OP_PAD: - func = ggml_cuda_pad; + ggml_cuda_op_pad(ctx, dst); break; case GGML_OP_ARANGE: - func = ggml_cuda_arange; + ggml_cuda_op_arange(ctx, dst); break; case GGML_OP_TIMESTEP_EMBEDDING: - func = ggml_cuda_timestep_embedding; + ggml_cuda_op_timestep_embedding(ctx, dst); break; case GGML_OP_LEAKY_RELU: - func = ggml_cuda_leaky_relu; + ggml_cuda_op_leaky_relu(ctx, dst); break; case GGML_OP_RMS_NORM: - func = ggml_cuda_rms_norm; + ggml_cuda_op_rms_norm(ctx, dst); break; case GGML_OP_MUL_MAT: - if (tensor->src[0]->ne[3] != tensor->src[1]->ne[3]) { - fprintf(stderr, "%s: cannot compute %s: src0->ne[3] = %" PRId64 ", src1->ne[3] = %" PRId64 " - fallback to CPU\n", __func__, tensor->name, tensor->src[0]->ne[3], tensor->src[1]->ne[3]); + if (dst->src[0]->ne[3] != dst->src[1]->ne[3]) { + fprintf(stderr, "%s: cannot compute %s: src0->ne[3] = %" PRId64 ", src1->ne[3] = %" PRId64 " - fallback to CPU\n", __func__, dst->name, dst->src[0]->ne[3], dst->src[1]->ne[3]); return false; } else { - func = ggml_cuda_mul_mat; + ggml_cuda_mul_mat(ctx, dst->src[0], dst->src[1], dst); } break; case GGML_OP_MUL_MAT_ID: - func = ggml_cuda_mul_mat_id; + ggml_cuda_mul_mat_id(ctx, dst); break; case GGML_OP_SCALE: - func = ggml_cuda_scale; + ggml_cuda_op_scale(ctx, dst); break; case GGML_OP_SQR: - func = ggml_cuda_sqr; + ggml_cuda_op_sqr(ctx, dst); break; case GGML_OP_CLAMP: - func = ggml_cuda_clamp; - break; - case GGML_OP_CPY: - func = ggml_cuda_cpy; - break; - case GGML_OP_CONT: - func = ggml_cuda_dup; + ggml_cuda_op_clamp(ctx, dst); break; case GGML_OP_NONE: case GGML_OP_RESHAPE: case GGML_OP_VIEW: case GGML_OP_PERMUTE: case GGML_OP_TRANSPOSE: - func = ggml_cuda_nop; - break; + break; case GGML_OP_DIAG_MASK_INF: - func = ggml_cuda_diag_mask_inf; + ggml_cuda_op_diag_mask_inf(ctx, dst); break; case GGML_OP_SOFT_MAX: - func = ggml_cuda_soft_max; + ggml_cuda_op_soft_max(ctx, dst); break; case GGML_OP_ROPE: - func = ggml_cuda_rope; + ggml_cuda_op_rope(ctx, dst); break; case GGML_OP_ALIBI: - func = ggml_cuda_alibi; + ggml_cuda_op_alibi(ctx, dst); break; case GGML_OP_IM2COL: - func = ggml_cuda_im2col; + ggml_cuda_op_im2col(ctx, dst); break; case GGML_OP_POOL_2D: - func = ggml_cuda_pool2d; + ggml_cuda_op_pool2d(ctx, dst); break; case GGML_OP_SUM_ROWS: - func = ggml_cuda_sum_rows; + ggml_cuda_op_sum_rows(ctx, dst); break; case GGML_OP_ARGSORT: - func = ggml_cuda_argsort; + ggml_cuda_op_argsort(ctx, dst); break; default: return false; } - func(ctx, tensor->src[0], tensor->src[1], tensor); + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) { + fprintf(stderr, "%s: %s failed\n", __func__, ggml_op_desc(dst)); + GGML_ASSERT(false); + } + return true; } - //////////////////////////////////////////////////////////////////////////////// - // backend GGML_CALL static const char * ggml_backend_cuda_name(ggml_backend_t backend) { @@ -11642,7 +2775,7 @@ GGML_CALL bool ggml_backend_is_cuda(ggml_backend_t backend) { } GGML_CALL int ggml_backend_cuda_get_device_count() { - return get_cuda_global_info().device_count; + return ggml_cuda_info().device_count; } GGML_CALL void ggml_backend_cuda_get_device_description(int device, char * description, size_t description_size) { diff --git a/ggml-cuda/acc.cu b/ggml-cuda/acc.cu new file mode 100644 index 000000000..96bfe1c9d --- /dev/null +++ b/ggml-cuda/acc.cu @@ -0,0 +1,47 @@ +#include "acc.cuh" + +static __global__ void acc_f32(const float * x, const float * y, float * dst, const int ne, + const int ne10, const int ne11, const int ne12, + const int nb1, const int nb2, int offset) { + const int i = blockDim.x * blockIdx.x + threadIdx.x; + if (i >= ne) { + return; + } + int src1_idx = i - offset; + int oz = src1_idx / nb2; + int oy = (src1_idx - (oz * nb2)) / nb1; + int ox = src1_idx % nb1; + if (src1_idx >= 0 && ox < ne10 && oy < ne11 && oz < ne12) { + dst[i] = x[i] + y[ox + oy * ne10 + oz * ne10 * ne11]; + } else { + dst[i] = x[i]; + } +} + +static void acc_f32_cuda(const float * x, const float * y, float * dst, const int n_elements, + const int ne10, const int ne11, const int ne12, + const int nb1, const int nb2, const int offset, cudaStream_t stream) { + int num_blocks = (n_elements + CUDA_ACC_BLOCK_SIZE - 1) / CUDA_ACC_BLOCK_SIZE; + acc_f32<<>>(x, y, dst, n_elements, ne10, ne11, ne12, nb1, nb2, offset); +} + +void ggml_cuda_op_acc(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + const float * src0_d = (const float *)src0->data; + const float * src1_d = (const float *)src1->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT(src1->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + GGML_ASSERT(dst->ne[3] == 1); // just 3D tensors supported + + int nb1 = dst->op_params[0] / 4; // 4 bytes of float32 + int nb2 = dst->op_params[1] / 4; // 4 bytes of float32 + // int nb3 = dst->op_params[2] / 4; // 4 bytes of float32 - unused + int offset = dst->op_params[3] / 4; // offset in bytes + + acc_f32_cuda(src0_d, src1_d, dst_d, ggml_nelements(dst), src1->ne[0], src1->ne[1], src1->ne[2], nb1, nb2, offset, stream); +} diff --git a/ggml-cuda/acc.cuh b/ggml-cuda/acc.cuh new file mode 100644 index 000000000..1168ea1b2 --- /dev/null +++ b/ggml-cuda/acc.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_ACC_BLOCK_SIZE 256 + +void ggml_cuda_op_acc(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/alibi.cu b/ggml-cuda/alibi.cu new file mode 100644 index 000000000..6c7f1fd95 --- /dev/null +++ b/ggml-cuda/alibi.cu @@ -0,0 +1,63 @@ +#include "alibi.cuh" + +static __global__ void alibi_f32(const float * x, float * dst, const int ncols, const int k_rows, + const int n_heads_log2_floor, const float m0, const float m1) { + const int col = blockDim.x*blockIdx.x + threadIdx.x; + + if (col >= ncols) { + return; + } + + const int row = blockDim.y*blockIdx.y + threadIdx.y; + const int i = row*ncols + col; + + const int k = row/k_rows; + + float m_k; + if (k < n_heads_log2_floor) { + m_k = powf(m0, k + 1); + } else { + m_k = powf(m1, 2 * (k - n_heads_log2_floor) + 1); + } + + dst[i] = col * m_k + x[i]; +} + +static void alibi_f32_cuda(const float * x, float * dst, const int ncols, const int nrows, + const int k_rows, const int n_heads_log2_floor, const float m0, + const float m1, cudaStream_t stream) { + const dim3 block_dims(CUDA_ALIBI_BLOCK_SIZE, 1, 1); + const int num_blocks_x = (ncols + CUDA_ALIBI_BLOCK_SIZE - 1) / (CUDA_ALIBI_BLOCK_SIZE); + const dim3 block_nums(num_blocks_x, nrows, 1); + alibi_f32<<>>(x, dst, ncols, k_rows, n_heads_log2_floor, m0, m1); +} + +void ggml_cuda_op_alibi(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + const int64_t ne00 = src0->ne[0]; + const int64_t ne01 = src0->ne[1]; + const int64_t ne02 = src0->ne[2]; + const int64_t nrows = ggml_nrows(src0); + + //const int n_past = ((int32_t *) dst->op_params)[0]; + const int n_head = ((int32_t *) dst->op_params)[1]; + float max_bias; + memcpy(&max_bias, (int32_t *) dst->op_params + 2, sizeof(float)); + + //GGML_ASSERT(ne01 + n_past == ne00); + GGML_ASSERT(n_head == ne02); + + const int n_heads_log2_floor = 1 << (int) floor(log2(n_head)); + + const float m0 = powf(2.0f, -(max_bias) / n_heads_log2_floor); + const float m1 = powf(2.0f, -(max_bias / 2.0f) / n_heads_log2_floor); + + alibi_f32_cuda(src0_d, dst_d, ne00, nrows, ne01, n_heads_log2_floor, m0, m1, stream); +} diff --git a/ggml-cuda/alibi.cuh b/ggml-cuda/alibi.cuh new file mode 100644 index 000000000..630adfc7f --- /dev/null +++ b/ggml-cuda/alibi.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_ALIBI_BLOCK_SIZE 32 + +void ggml_cuda_op_alibi(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/arange.cu b/ggml-cuda/arange.cu new file mode 100644 index 000000000..b5e495a24 --- /dev/null +++ b/ggml-cuda/arange.cu @@ -0,0 +1,34 @@ +#include "arange.cuh" + +static __global__ void arange_f32(float * dst, const int ne0, const float start, const float step) { + // blockIDx.x: idx of ne0 / BLOCK_SIZE + int nidx = threadIdx.x + blockIdx.x * blockDim.x; + if (nidx >= ne0) { + return; + } + dst[nidx] = start + step * nidx; +} + +static void arange_f32_cuda(float * dst, const int ne0, const float start, const float step, cudaStream_t stream) { + int num_blocks = (ne0 + CUDA_ARANGE_BLOCK_SIZE - 1) / CUDA_ARANGE_BLOCK_SIZE; + arange_f32<<>>(dst, ne0, start, step); +} + +void ggml_cuda_op_arange(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(dst->type == GGML_TYPE_F32); + + float start; + float stop; + float step; + memcpy(&start, (float *)dst->op_params + 0, sizeof(float)); + memcpy(&stop, (float *)dst->op_params + 1, sizeof(float)); + memcpy(&step, (float *)dst->op_params + 2, sizeof(float)); + + int64_t steps = (int64_t)ceil((stop - start) / step); + GGML_ASSERT(ggml_nelements(dst) == steps); + + arange_f32_cuda(dst_d, dst->ne[0], start, step, stream); +} diff --git a/ggml-cuda/arange.cuh b/ggml-cuda/arange.cuh new file mode 100644 index 000000000..41e74fdfc --- /dev/null +++ b/ggml-cuda/arange.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_ARANGE_BLOCK_SIZE 256 + +void ggml_cuda_op_arange(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/argsort.cu b/ggml-cuda/argsort.cu new file mode 100644 index 000000000..1333287e4 --- /dev/null +++ b/ggml-cuda/argsort.cu @@ -0,0 +1,77 @@ +#include "argsort.cuh" + +template +static inline __device__ void ggml_cuda_swap(T & a, T & b) { + T tmp = a; + a = b; + b = tmp; +} + +template +static __global__ void k_argsort_f32_i32(const float * x, int * dst, const int ncols) { + // bitonic sort + int col = threadIdx.x; + int row = blockIdx.y; + + if (col >= ncols) return; + + const float * x_row = x + row * ncols; + int * dst_row = dst + row * ncols; + + // initialize indices + if (col < ncols) { + dst_row[col] = col; + } + __syncthreads(); + + for (int k = 2; k <= ncols; k *= 2) { + for (int j = k / 2; j > 0; j /= 2) { + int ixj = col ^ j; + if (ixj > col) { + if ((col & k) == 0) { + if (order == GGML_SORT_ORDER_ASC ? x_row[dst_row[col]] > x_row[dst_row[ixj]] : x_row[dst_row[col]] < x_row[dst_row[ixj]]) { + ggml_cuda_swap(dst_row[col], dst_row[ixj]); + } + } else { + if (order == GGML_SORT_ORDER_ASC ? x_row[dst_row[col]] < x_row[dst_row[ixj]] : x_row[dst_row[col]] > x_row[dst_row[ixj]]) { + ggml_cuda_swap(dst_row[col], dst_row[ixj]); + } + } + } + __syncthreads(); + } + } +} + +static void argsort_f32_i32_cuda(const float * x, int * dst, const int ncols, const int nrows, ggml_sort_order order, cudaStream_t stream) { + // bitonic sort requires ncols to be power of 2 + GGML_ASSERT((ncols & (ncols - 1)) == 0); + + const dim3 block_dims(ncols, 1, 1); + const dim3 block_nums(1, nrows, 1); + if (order == GGML_SORT_ORDER_ASC) { + k_argsort_f32_i32<<>>(x, dst, ncols); + } else if (order == GGML_SORT_ORDER_DESC) { + k_argsort_f32_i32<<>>(x, dst, ncols); + } else { + GGML_ASSERT(false); + } +} + +void ggml_cuda_op_argsort(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_I32); + GGML_ASSERT(ggml_is_contiguous(src0)); + + const int64_t ncols = src0->ne[0]; + const int64_t nrows = ggml_nrows(src0); + + enum ggml_sort_order order = (enum ggml_sort_order) dst->op_params[0]; + + argsort_f32_i32_cuda(src0_d, (int *)dst_d, ncols, nrows, order, stream); +} diff --git a/ggml-cuda/argsort.cuh b/ggml-cuda/argsort.cuh new file mode 100644 index 000000000..68a001547 --- /dev/null +++ b/ggml-cuda/argsort.cuh @@ -0,0 +1,3 @@ +#include "common.cuh" + +void ggml_cuda_op_argsort(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/binbcast.cu b/ggml-cuda/binbcast.cu new file mode 100644 index 000000000..959eaed95 --- /dev/null +++ b/ggml-cuda/binbcast.cu @@ -0,0 +1,236 @@ +#include "binbcast.cuh" + +static __device__ __forceinline__ float op_repeat(const float a, const float b) { + return b; + GGML_UNUSED(a); +} + +static __device__ __forceinline__ float op_add(const float a, const float b) { + return a + b; +} + +static __device__ __forceinline__ float op_mul(const float a, const float b) { + return a * b; +} + +static __device__ __forceinline__ float op_div(const float a, const float b) { + return a / b; +} + +template +static __global__ void k_bin_bcast(const src0_t * src0, const src1_t * src1, dst_t * dst, + int ne0, int ne1, int ne2, int ne3, + int ne10, int ne11, int ne12, int ne13, + /*int s0, */ int s1, int s2, int s3, + /*int s10,*/ int s11, int s12, int s13) { + const int i0s = blockDim.x*blockIdx.x + threadIdx.x; + const int i1 = (blockDim.y*blockIdx.y + threadIdx.y); + const int i2 = (blockDim.z*blockIdx.z + threadIdx.z) / ne3; + const int i3 = (blockDim.z*blockIdx.z + threadIdx.z) % ne3; + + if (i0s >= ne0 || i1 >= ne1 || i2 >= ne2 || i3 >= ne3) { + return; + } + + const int i11 = i1 % ne11; + const int i12 = i2 % ne12; + const int i13 = i3 % ne13; + + const size_t i_src0 = i3*s3 + i2*s2 + i1*s1; + const size_t i_src1 = i13*s13 + i12*s12 + i11*s11; + const size_t i_dst = i_src0; + + const src0_t * src0_row = src0 + i_src0; + const src1_t * src1_row = src1 + i_src1; + dst_t * dst_row = dst + i_dst; + + for (int i0 = i0s; i0 < ne0; i0 += blockDim.x*gridDim.x) { + const int i10 = i0 % ne10; + dst_row[i0] = (dst_t)bin_op(src0 ? (float)src0_row[i0] : 0.0f, (float)src1_row[i10]); + } +} + +template +static __global__ void k_bin_bcast_unravel(const src0_t * src0, const src1_t * src1, dst_t * dst, + int ne0, int ne1, int ne2, int ne3, + int ne10, int ne11, int ne12, int ne13, + /*int s0, */ int s1, int s2, int s3, + /*int s10,*/ int s11, int s12, int s13) { + + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + const int i3 = i/(ne2*ne1*ne0); + const int i2 = (i/(ne1*ne0)) % ne2; + const int i1 = (i/ne0) % ne1; + const int i0 = i % ne0; + + if (i0 >= ne0 || i1 >= ne1 || i2 >= ne2 || i3 >= ne3) { + return; + } + + const int i11 = i1 % ne11; + const int i12 = i2 % ne12; + const int i13 = i3 % ne13; + + const size_t i_src0 = i3*s3 + i2*s2 + i1*s1; + const size_t i_src1 = i13*s13 + i12*s12 + i11*s11; + const size_t i_dst = i_src0; + + const src0_t * src0_row = src0 + i_src0; + const src1_t * src1_row = src1 + i_src1; + dst_t * dst_row = dst + i_dst; + + const int i10 = i0 % ne10; + dst_row[i0] = (dst_t)bin_op(src0 ? (float)src0_row[i0] : 0.0f, (float)src1_row[i10]); +} + +template +struct bin_bcast_cuda { + template + void operator()(const struct ggml_tensor * src0, const struct ggml_tensor * src1, struct ggml_tensor * dst, + const src0_t * src0_dd, const src1_t * src1_dd, dst_t * dst_dd, + cudaStream_t stream) { + + GGML_TENSOR_BINARY_OP_LOCALS + + int nr0 = ne10/ne0; + int nr1 = ne11/ne1; + int nr2 = ne12/ne2; + int nr3 = ne13/ne3; + + int nr[4] = { nr0, nr1, nr2, nr3 }; + + // collapse dimensions until first broadcast dimension + int64_t cne0[] = {ne0, ne1, ne2, ne3}; + int64_t cne1[] = {ne10, ne11, ne12, ne13}; + size_t cnb0[] = {nb0, nb1, nb2, nb3}; + size_t cnb1[] = {nb10, nb11, nb12, nb13}; + auto collapse = [](int64_t cne[]) { + cne[0] *= cne[1]; + cne[1] = cne[2]; + cne[2] = cne[3]; + cne[3] = 1; + }; + + auto collapse_nb = [](size_t cnb[], const int64_t cne[]) { + cnb[1] *= cne[1]; + cnb[2] *= cne[2]; + cnb[3] *= cne[3]; + }; + + for (int i = 0; i < 4; i++) { + if (nr[i] != 1) { + break; + } + if (i > 0) { + collapse_nb(cnb0, cne0); + collapse_nb(cnb1, cne1); + collapse(cne0); + collapse(cne1); + } + } + { + int64_t ne0 = cne0[0]; + int64_t ne1 = cne0[1]; + int64_t ne2 = cne0[2]; + int64_t ne3 = cne0[3]; + + int64_t ne10 = cne1[0]; + int64_t ne11 = cne1[1]; + int64_t ne12 = cne1[2]; + int64_t ne13 = cne1[3]; + + size_t nb0 = cnb0[0]; + size_t nb1 = cnb0[1]; + size_t nb2 = cnb0[2]; + size_t nb3 = cnb0[3]; + + size_t nb10 = cnb1[0]; + size_t nb11 = cnb1[1]; + size_t nb12 = cnb1[2]; + size_t nb13 = cnb1[3]; + + size_t s0 = nb0 / sizeof(dst_t); + size_t s1 = nb1 / sizeof(dst_t); + size_t s2 = nb2 / sizeof(dst_t); + size_t s3 = nb3 / sizeof(dst_t); + + size_t s10 = nb10 / sizeof(src1_t); + size_t s11 = nb11 / sizeof(src1_t); + size_t s12 = nb12 / sizeof(src1_t); + size_t s13 = nb13 / sizeof(src1_t); + + GGML_ASSERT(s0 == 1); + GGML_ASSERT(s10 == 1); + + const int block_size = 128; + + int64_t hne0 = std::max(ne0/2LL, 1LL); + + dim3 block_dims; + block_dims.x = std::min(hne0, block_size); + block_dims.y = std::min(ne1, block_size / block_dims.x); + block_dims.z = std::min(std::min(ne2*ne3, block_size / block_dims.x / block_dims.y), 64U); + + dim3 block_nums( + (hne0 + block_dims.x - 1) / block_dims.x, + (ne1 + block_dims.y - 1) / block_dims.y, + (ne2*ne3 + block_dims.z - 1) / block_dims.z + ); + + if (block_nums.z > 65535) { + // this is the maximum number of blocks in z direction, fallback to 1D grid kernel + int block_num = (ne0*ne1*ne2*ne3 + block_size - 1) / block_size; + k_bin_bcast_unravel<<>>( + src0_dd, src1_dd, dst_dd, + ne0, ne1, ne2, ne3, + ne10, ne11, ne12, ne13, + /* s0, */ s1, s2, s3, + /* s10, */ s11, s12, s13); + } else { + k_bin_bcast<<>>( + src0_dd, src1_dd, dst_dd, + ne0, ne1, ne2, ne3, + ne10, ne11, ne12, ne13, + /* s0, */ s1, s2, s3, + /* s10, */ s11, s12, s13); + } + } + } +}; + +template +static void ggml_cuda_op_bin_bcast( + const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, + const void * src0_dd, const void * src1_dd, void * dst_dd, cudaStream_t stream) { + + GGML_ASSERT(src1->type == GGML_TYPE_F32); + + if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) { + op()(src0, src1, dst, (const float *)src0_dd, (const float *)src1_dd, (float *)dst_dd, stream); + } else if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16) { + op()(src0, src1, dst, (const half *) src0_dd, (const float *)src1_dd, (half *) dst_dd, stream); + } else if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F32) { + op()(src0, src1, dst, (const half *) src0_dd, (const float *)src1_dd, (float *)dst_dd, stream); + } else { + fprintf(stderr, "%s: unsupported types: dst: %s, src0: %s, src1: %s\n", __func__, + ggml_type_name(dst->type), ggml_type_name(src0->type), ggml_type_name(src1->type)); + GGML_ASSERT(false); + } +} + +void ggml_cuda_op_repeat(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + ggml_cuda_op_bin_bcast>(dst, dst->src[0], dst, nullptr, dst->src[0]->data, dst->data, ctx.stream()); +} + +void ggml_cuda_op_add(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + ggml_cuda_op_bin_bcast>(dst->src[0], dst->src[1], dst, dst->src[0]->data, dst->src[1]->data, dst->data, ctx.stream()); +} + +void ggml_cuda_op_mul(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + ggml_cuda_op_bin_bcast>(dst->src[0], dst->src[1], dst, dst->src[0]->data, dst->src[1]->data, dst->data, ctx.stream()); +} + +void ggml_cuda_op_div(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + ggml_cuda_op_bin_bcast>(dst->src[0], dst->src[1], dst, dst->src[0]->data, dst->src[1]->data, dst->data, ctx.stream()); +} diff --git a/ggml-cuda/binbcast.cuh b/ggml-cuda/binbcast.cuh new file mode 100644 index 000000000..4f63d6372 --- /dev/null +++ b/ggml-cuda/binbcast.cuh @@ -0,0 +1,6 @@ +#include "common.cuh" + +void ggml_cuda_op_repeat(ggml_backend_cuda_context & ctx, ggml_tensor * dst); +void ggml_cuda_op_add(ggml_backend_cuda_context & ctx, ggml_tensor * dst); +void ggml_cuda_op_mul(ggml_backend_cuda_context & ctx, ggml_tensor * dst); +void ggml_cuda_op_div(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/clamp.cu b/ggml-cuda/clamp.cu new file mode 100644 index 000000000..379ded042 --- /dev/null +++ b/ggml-cuda/clamp.cu @@ -0,0 +1,35 @@ +#include "clamp.cuh" + +static __global__ void clamp_f32(const float * x, float * dst, const float min, const float max, const int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + + dst[i] = x[i] < min ? min : (x[i] > max ? max : x[i]); +} + +static void clamp_f32_cuda(const float * x, float * dst, const float min, const float max, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_CLAMP_BLOCK_SIZE - 1) / CUDA_CLAMP_BLOCK_SIZE; + clamp_f32<<>>(x, dst, min, max, k); +} + + +void ggml_cuda_op_clamp(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + float min; + float max; + memcpy(&min, dst->op_params, sizeof(float)); + memcpy(&max, (float *) dst->op_params + 1, sizeof(float)); + + clamp_f32_cuda(src0_d, dst_d, min, max, ggml_nelements(src0), stream); + CUDA_CHECK(cudaGetLastError()); +} diff --git a/ggml-cuda/clamp.cuh b/ggml-cuda/clamp.cuh new file mode 100644 index 000000000..7f9559dd1 --- /dev/null +++ b/ggml-cuda/clamp.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_CLAMP_BLOCK_SIZE 256 + +void ggml_cuda_op_clamp(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/common.cuh b/ggml-cuda/common.cuh new file mode 100644 index 000000000..33c8ed1da --- /dev/null +++ b/ggml-cuda/common.cuh @@ -0,0 +1,550 @@ +#pragma once + +#include "../ggml.h" +#include "../ggml-cuda.h" +#include + +#if defined(GGML_USE_HIPBLAS) +#define GGML_COMMON_DECL_HIP +#define GGML_COMMON_IMPL_HIP +#else +#define GGML_COMMON_DECL_CUDA +#define GGML_COMMON_IMPL_CUDA +#endif +#include "../ggml-common.h" + +#include +#include +#include +#include +#include + +#if defined(GGML_USE_HIPBLAS) +#include +#include +#include +#ifdef __HIP_PLATFORM_AMD__ +// for rocblas_initialize() +#include "rocblas/rocblas.h" +#endif // __HIP_PLATFORM_AMD__ +#define CUBLAS_COMPUTE_16F HIPBLAS_R_16F +#define CUBLAS_COMPUTE_32F HIPBLAS_R_32F +#define CUBLAS_COMPUTE_32F_FAST_16F HIPBLAS_R_32F +#define CUBLAS_GEMM_DEFAULT HIPBLAS_GEMM_DEFAULT +#define CUBLAS_GEMM_DEFAULT_TENSOR_OP HIPBLAS_GEMM_DEFAULT +#define CUBLAS_OP_N HIPBLAS_OP_N +#define CUBLAS_OP_T HIPBLAS_OP_T +#define CUBLAS_STATUS_SUCCESS HIPBLAS_STATUS_SUCCESS +#define CUBLAS_TF32_TENSOR_OP_MATH 0 +#define CUDA_R_16F HIPBLAS_R_16F +#define CUDA_R_32F HIPBLAS_R_32F +#define __shfl_xor_sync(mask, var, laneMask, width) __shfl_xor(var, laneMask, width) +#define cublasComputeType_t hipblasDatatype_t //deprecated, new hipblasComputeType_t not in 5.6 +#define cublasCreate hipblasCreate +#define cublasDestroy hipblasDestroy +#define cublasGemmEx hipblasGemmEx +#define cublasGemmBatchedEx hipblasGemmBatchedEx +#define cublasGemmStridedBatchedEx hipblasGemmStridedBatchedEx +#define cublasHandle_t hipblasHandle_t +#define cublasSetMathMode(handle, mode) CUBLAS_STATUS_SUCCESS +#define cublasSetStream hipblasSetStream +#define cublasSgemm hipblasSgemm +#define cublasStatus_t hipblasStatus_t +#define cudaDataType_t hipblasDatatype_t //deprecated, new hipblasDatatype not in 5.6 +#define cudaDeviceCanAccessPeer hipDeviceCanAccessPeer +#define cudaDeviceDisablePeerAccess hipDeviceDisablePeerAccess +#define cudaDeviceEnablePeerAccess hipDeviceEnablePeerAccess +#define cudaDeviceProp hipDeviceProp_t +#define cudaDeviceSynchronize hipDeviceSynchronize +#define cudaError_t hipError_t +#define cudaErrorPeerAccessAlreadyEnabled hipErrorPeerAccessAlreadyEnabled +#define cudaErrorPeerAccessNotEnabled hipErrorPeerAccessNotEnabled +#define cudaEventCreateWithFlags hipEventCreateWithFlags +#define cudaEventDisableTiming hipEventDisableTiming +#define cudaEventRecord hipEventRecord +#define cudaEventSynchronize hipEventSynchronize +#define cudaEvent_t hipEvent_t +#define cudaEventDestroy hipEventDestroy +#define cudaFree hipFree +#define cudaFreeHost hipHostFree +#define cudaGetDevice hipGetDevice +#define cudaGetDeviceCount hipGetDeviceCount +#define cudaGetDeviceProperties hipGetDeviceProperties +#define cudaGetErrorString hipGetErrorString +#define cudaGetLastError hipGetLastError +#define cudaHostRegister hipHostRegister +#define cudaHostRegisterPortable hipHostRegisterPortable +#define cudaHostRegisterReadOnly hipHostRegisterReadOnly +#define cudaHostUnregister hipHostUnregister +#define cudaLaunchHostFunc hipLaunchHostFunc +#ifdef GGML_HIP_UMA +#define cudaMalloc hipMallocManaged +#define cudaMallocHost(ptr, size) hipHostMalloc(ptr, size) +#else +#define cudaMalloc hipMalloc +#define cudaMallocHost(ptr, size) hipHostMalloc(ptr, size, hipHostMallocDefault) +#endif +#define cudaMemcpy hipMemcpy +#define cudaMemcpyAsync hipMemcpyAsync +#define cudaMemcpyPeerAsync hipMemcpyPeerAsync +#define cudaMemcpy2DAsync hipMemcpy2DAsync +#define cudaMemcpyDeviceToDevice hipMemcpyDeviceToDevice +#define cudaMemcpyDeviceToHost hipMemcpyDeviceToHost +#define cudaMemcpyHostToDevice hipMemcpyHostToDevice +#define cudaMemcpyKind hipMemcpyKind +#define cudaMemset hipMemset +#define cudaMemsetAsync hipMemsetAsync +#define cudaMemGetInfo hipMemGetInfo +#define cudaOccupancyMaxPotentialBlockSize hipOccupancyMaxPotentialBlockSize +#define cudaSetDevice hipSetDevice +#define cudaStreamCreateWithFlags hipStreamCreateWithFlags +#define cudaStreamDestroy hipStreamDestroy +#define cudaStreamFireAndForget hipStreamFireAndForget +#define cudaStreamNonBlocking hipStreamNonBlocking +#define cudaStreamPerThread hipStreamPerThread +#define cudaStreamSynchronize hipStreamSynchronize +#define cudaStreamWaitEvent(stream, event, flags) hipStreamWaitEvent(stream, event, flags) +#define cudaStream_t hipStream_t +#define cudaSuccess hipSuccess +#define __trap abort +#define CUBLAS_STATUS_SUCCESS HIPBLAS_STATUS_SUCCESS +#define CUBLAS_STATUS_NOT_INITIALIZED HIPBLAS_STATUS_NOT_INITIALIZED +#define CUBLAS_STATUS_ALLOC_FAILED HIPBLAS_STATUS_ALLOC_FAILED +#define CUBLAS_STATUS_INVALID_VALUE HIPBLAS_STATUS_INVALID_VALUE +#define CUBLAS_STATUS_ARCH_MISMATCH HIPBLAS_STATUS_ARCH_MISMATCH +#define CUBLAS_STATUS_MAPPING_ERROR HIPBLAS_STATUS_MAPPING_ERROR +#define CUBLAS_STATUS_EXECUTION_FAILED HIPBLAS_STATUS_EXECUTION_FAILED +#define CUBLAS_STATUS_INTERNAL_ERROR HIPBLAS_STATUS_INTERNAL_ERROR +#define CUBLAS_STATUS_NOT_SUPPORTED HIPBLAS_STATUS_NOT_SUPPORTED +#else +#include +#include +#include +#include + +#if CUDART_VERSION < 11020 +#define CU_DEVICE_ATTRIBUTE_VIRTUAL_MEMORY_MANAGEMENT_SUPPORTED CU_DEVICE_ATTRIBUTE_VIRTUAL_ADDRESS_MANAGEMENT_SUPPORTED +#define CUBLAS_TF32_TENSOR_OP_MATH CUBLAS_TENSOR_OP_MATH +#define CUBLAS_COMPUTE_16F CUDA_R_16F +#define CUBLAS_COMPUTE_32F CUDA_R_32F +#define cublasComputeType_t cudaDataType_t +#endif // CUDART_VERSION < 11020 + +#endif // defined(GGML_USE_HIPBLAS) + +#define STRINGIZE_IMPL(...) #__VA_ARGS__ +#define STRINGIZE(...) STRINGIZE_IMPL(__VA_ARGS__) + +#define WARP_SIZE 32 +#define CUDART_HMAX 11070 // CUDA 11.7, min. ver. for which __hmax and __hmax2 are known to work (may be higher than needed) + +#define CC_PASCAL 600 +#define MIN_CC_DP4A 610 // minimum compute capability for __dp4a, an intrinsic for byte-wise dot products +#define CC_VOLTA 700 +#define CC_OFFSET_AMD 1000000 +#define CC_RDNA1 (CC_OFFSET_AMD + 1010) +#define CC_RDNA2 (CC_OFFSET_AMD + 1030) +#define CC_RDNA3 (CC_OFFSET_AMD + 1100) + +// define this if you want to always fallback to MMQ kernels and not use cuBLAS for matrix multiplication +// on modern hardware, using cuBLAS is recommended as it utilizes F16 tensor cores which are very performant +// for large computational tasks. the drawback is that this requires some extra amount of VRAM: +// - 7B quantum model: +100-200 MB +// - 13B quantum model: +200-400 MB +// +//#define GGML_CUDA_FORCE_MMQ + +// TODO: improve this to be correct for more hardware +// for example, currently fails for GeForce GTX 1660 which is TURING arch (> VOLTA) but does not have tensor cores +#if !defined(GGML_CUDA_FORCE_MMQ) +#define CUDA_USE_TENSOR_CORES +#endif + +#define MMVQ_MAX_BATCH_SIZE 8 // max batch size to use MMVQ kernels +#define MMQ_MAX_BATCH_SIZE 32 // max batch size to use MMQ kernels when tensor cores are available + +#define MATRIX_ROW_PADDING 512 // last row of quant. matrices is a multiple of this to avoid out-of-bounds memory accesses + +#if defined(_MSC_VER) +#pragma warning(disable: 4244 4267) // possible loss of data +#endif + +#define GGML_CUDA_MAX_STREAMS 8 + +[[noreturn]] +void ggml_cuda_error(const char * stmt, const char * func, const char * file, int line, const char * msg); + +#define CUDA_CHECK_GEN(err, success, error_fn) \ + do { \ + auto err_ = (err); \ + if (err_ != (success)) { \ + ggml_cuda_error(#err, __func__, __FILE__, __LINE__, error_fn(err_)); \ + } \ + } while (0) + +#define CUDA_CHECK(err) CUDA_CHECK_GEN(err, cudaSuccess, cudaGetErrorString) + +#if CUDART_VERSION >= 12000 + static const char * cublas_get_error_str(const cublasStatus_t err) { + return cublasGetStatusString(err); + } +#else + static const char * cublas_get_error_str(const cublasStatus_t err) { + switch (err) { + case CUBLAS_STATUS_SUCCESS: return "CUBLAS_STATUS_SUCCESS"; + case CUBLAS_STATUS_NOT_INITIALIZED: return "CUBLAS_STATUS_NOT_INITIALIZED"; + case CUBLAS_STATUS_ALLOC_FAILED: return "CUBLAS_STATUS_ALLOC_FAILED"; + case CUBLAS_STATUS_INVALID_VALUE: return "CUBLAS_STATUS_INVALID_VALUE"; + case CUBLAS_STATUS_ARCH_MISMATCH: return "CUBLAS_STATUS_ARCH_MISMATCH"; + case CUBLAS_STATUS_MAPPING_ERROR: return "CUBLAS_STATUS_MAPPING_ERROR"; + case CUBLAS_STATUS_EXECUTION_FAILED: return "CUBLAS_STATUS_EXECUTION_FAILED"; + case CUBLAS_STATUS_INTERNAL_ERROR: return "CUBLAS_STATUS_INTERNAL_ERROR"; + case CUBLAS_STATUS_NOT_SUPPORTED: return "CUBLAS_STATUS_NOT_SUPPORTED"; + default: return "unknown error"; + } + } +#endif // CUDART_VERSION >= 12000 + +#define CUBLAS_CHECK(err) CUDA_CHECK_GEN(err, CUBLAS_STATUS_SUCCESS, cublas_get_error_str) + +#if !defined(GGML_USE_HIPBLAS) +static const char * cu_get_error_str(CUresult err) { + const char * err_str; + cuGetErrorString(err, &err_str); + return err_str; +} +#define CU_CHECK(err) CUDA_CHECK_GEN(err, CUDA_SUCCESS, cu_get_error_str) +#endif + +#if CUDART_VERSION >= 11100 +#define GGML_CUDA_ASSUME(x) __builtin_assume(x) +#else +#define GGML_CUDA_ASSUME(x) +#endif // CUDART_VERSION >= 11100 + +#ifdef GGML_CUDA_F16 +typedef half dfloat; // dequantize float +typedef half2 dfloat2; +#else +typedef float dfloat; // dequantize float +typedef float2 dfloat2; +#endif //GGML_CUDA_F16 + +[[noreturn]] +static __device__ void no_device_code( + const char * file_name, const int line, const char * function_name, const int arch, const char * arch_list) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) + printf("%s:%d: ERROR: HIP kernel %s has no device code compatible with HIP arch %d.\n", + file_name, line, function_name, arch); + GGML_UNUSED(arch_list); +#else + printf("%s:%d: ERROR: CUDA kernel %s has no device code compatible with CUDA arch %d. ggml-cuda.cu was compiled for: %s\n", + file_name, line, function_name, arch, arch_list); +#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) + __trap(); + + GGML_UNUSED(no_device_code); // suppress unused function warning +} + +#ifdef __CUDA_ARCH__ +#define NO_DEVICE_CODE no_device_code(__FILE__, __LINE__, __FUNCTION__, __CUDA_ARCH__, STRINGIZE(__CUDA_ARCH_LIST__)) +#else +#define NO_DEVICE_CODE //GGML_ASSERT(false && "NO_DEVICE_CODE not valid in host code.") +#endif // __CUDA_ARCH__ + +static __device__ __forceinline__ float warp_reduce_sum(float x) { +#pragma unroll + for (int mask = 16; mask > 0; mask >>= 1) { + x += __shfl_xor_sync(0xffffffff, x, mask, 32); + } + return x; +} + +static __device__ __forceinline__ float2 warp_reduce_sum(float2 a) { +#pragma unroll + for (int mask = 16; mask > 0; mask >>= 1) { + a.x += __shfl_xor_sync(0xffffffff, a.x, mask, 32); + a.y += __shfl_xor_sync(0xffffffff, a.y, mask, 32); + } + return a; +} + +#ifdef GGML_CUDA_F16 +static __device__ __forceinline__ half2 warp_reduce_sum(half2 a) { +#if !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) && __CUDA_ARCH__ >= CC_PASCAL +#pragma unroll + for (int mask = 16; mask > 0; mask >>= 1) { + a = __hadd2(a, __shfl_xor_sync(0xffffffff, a, mask, 32)); + } + return a; +#else + GGML_UNUSED(a); + NO_DEVICE_CODE; +#endif // !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) && __CUDA_ARCH__ >= CC_PASCAL +} +#endif // GGML_CUDA_F16 + +static __device__ __forceinline__ float warp_reduce_max(float x) { +#pragma unroll + for (int mask = 16; mask > 0; mask >>= 1) { + x = fmaxf(x, __shfl_xor_sync(0xffffffff, x, mask, 32)); + } + return x; +} + +//static __device__ __forceinline__ half2 warp_reduce_max(half2 x) { +//#if !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) && __CUDA_ARCH__ >= CC_PASCAL && CUDART_VERSION >= CUDART_HMAX +//#pragma unroll +// for (int mask = 16; mask > 0; mask >>= 1) { +// x = __hmax2(x, __shfl_xor_sync(0xffffffff, x, mask, 32)); +// } +// return x; +//#else +// GGML_UNUSED(x); +// NO_DEVICE_CODE; +//#endif // !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) && __CUDA_ARCH__ >= CC_PASCAL && CUDART_VERSION >= CUDART_HMAX +//} + + +#if defined(GGML_USE_HIPBLAS) +#define __CUDA_ARCH__ 1300 + +#if defined(__gfx1100__) || defined(__gfx1101__) || defined(__gfx1102__) || defined(__gfx1103__) || \ + defined(__gfx1150__) || defined(__gfx1151__) +#define RDNA3 +#endif + +#if defined(__gfx1030__) || defined(__gfx1031__) || defined(__gfx1032__) || defined(__gfx1033__) || \ + defined(__gfx1034__) || defined(__gfx1035__) || defined(__gfx1036__) || defined(__gfx1037__) +#define RDNA2 +#endif + +#ifndef __has_builtin + #define __has_builtin(x) 0 +#endif + +typedef int8_t int8x4_t __attribute__((ext_vector_type(4))); +typedef uint8_t uint8x4_t __attribute__((ext_vector_type(4))); +static __device__ __forceinline__ int __vsubss4(const int a, const int b) { + const int8x4_t va = reinterpret_cast(a); + const int8x4_t vb = reinterpret_cast(b); +#if __has_builtin(__builtin_elementwise_sub_sat) + const int8x4_t c = __builtin_elementwise_sub_sat(va, vb); + return reinterpret_cast(c); +#else + int8x4_t c; + int16_t tmp; +#pragma unroll + for (int i = 0; i < 4; i++) { + tmp = va[i] - vb[i]; + if(tmp > std::numeric_limits::max()) tmp = std::numeric_limits::max(); + if(tmp < std::numeric_limits::min()) tmp = std::numeric_limits::min(); + c[i] = tmp; + } + return reinterpret_cast(c); +#endif // __has_builtin(__builtin_elementwise_sub_sat) +} + +static __device__ __forceinline__ int __vsub4(const int a, const int b) { + return __vsubss4(a, b); +} + +static __device__ __forceinline__ unsigned int __vcmpeq4(unsigned int a, unsigned int b) { + const uint8x4_t& va = reinterpret_cast(a); + const uint8x4_t& vb = reinterpret_cast(b); + unsigned int c; + uint8x4_t& vc = reinterpret_cast(c); +#pragma unroll + for (int i = 0; i < 4; ++i) { + vc[i] = va[i] == vb[i] ? 0xff : 0x00; + } + return c; +} + +static __device__ __forceinline__ int __dp4a(const int a, const int b, int c) { +#if defined(__gfx906__) || defined(__gfx908__) || defined(__gfx90a__) || defined(__gfx1030__) + c = __builtin_amdgcn_sdot4(a, b, c, false); +#elif defined(RDNA3) + c = __builtin_amdgcn_sudot4( true, a, true, b, c, false); +#elif defined(__gfx1010__) || defined(__gfx900__) + int tmp1; + int tmp2; + asm("\n \ + v_mul_i32_i24 %1, sext(%3), sext(%4) dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:BYTE_0 src1_sel:BYTE_0 \n \ + v_mul_i32_i24 %2, sext(%3), sext(%4) dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:BYTE_1 src1_sel:BYTE_1 \n \ + v_add3_u32 %0, %1, %2, %0 \n \ + v_mul_i32_i24 %1, sext(%3), sext(%4) dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:BYTE_2 src1_sel:BYTE_2 \n \ + v_mul_i32_i24 %2, sext(%3), sext(%4) dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:BYTE_3 src1_sel:BYTE_3 \n \ + v_add3_u32 %0, %1, %2, %0 \n \ + " + : "+v"(c), "=&v"(tmp1), "=&v"(tmp2) + : "v"(a), "v"(b) + ); +#else + const int8x4_t va = reinterpret_cast(a); + const int8x4_t vb = reinterpret_cast(b); + c += va[0] * vb[0] + va[1] * vb[1] + va[2] * vb[2] + va[3] * vb[3]; +#endif + return c; +} +#endif // defined(GGML_USE_HIPBLAS) + +// TODO: move to ggml-common.h +static const __device__ int8_t kvalues_iq4nl[16] = {-127, -104, -83, -65, -49, -35, -22, -10, 1, 13, 25, 38, 53, 69, 89, 113}; + +typedef void (*dequantize_kernel_t)(const void * vx, const int ib, const int iqs, dfloat2 & v); + + +////////////////////// + +struct ggml_cuda_device_info { + int device_count; + + struct cuda_device_info { + int cc; // compute capability + size_t smpb; // max. shared memory per block + bool vmm; // virtual memory support + size_t vmm_granularity; // granularity of virtual memory + size_t total_vram; + }; + + cuda_device_info devices[GGML_CUDA_MAX_DEVICES] = {}; + + std::array default_tensor_split = {}; +}; + +const ggml_cuda_device_info & ggml_cuda_info(); + +void ggml_cuda_set_device(int device); +int ggml_cuda_get_device(); + +struct ggml_cuda_pool { + virtual ~ggml_cuda_pool() = default; + + virtual void * alloc(size_t size, size_t * actual_size) = 0; + virtual void free(void * ptr, size_t size) = 0; +}; + +template +struct ggml_cuda_pool_alloc { + ggml_cuda_pool * pool = nullptr; + T * ptr = nullptr; + size_t actual_size = 0; + + ggml_cuda_pool_alloc() = default; + + explicit ggml_cuda_pool_alloc(ggml_cuda_pool & pool) : pool(&pool) { + } + + ggml_cuda_pool_alloc(ggml_cuda_pool & pool, size_t size) : pool(&pool) { + alloc(size); + } + + ~ggml_cuda_pool_alloc() { + if (ptr != nullptr) { + pool->free(ptr, actual_size); + } + } + + // size is in number of elements + T * alloc(size_t size) { + GGML_ASSERT(pool != nullptr); + GGML_ASSERT(ptr == nullptr); + ptr = (T *) pool->alloc(size * sizeof(T), &this->actual_size); + return ptr; + } + + T * alloc(ggml_cuda_pool & pool, size_t size) { + this->pool = &pool; + return alloc(size); + } + + T * get() { + return ptr; + } + + ggml_cuda_pool_alloc(const ggml_cuda_pool_alloc &) = delete; + ggml_cuda_pool_alloc(ggml_cuda_pool_alloc &&) = delete; + ggml_cuda_pool_alloc& operator=(const ggml_cuda_pool_alloc &) = delete; + ggml_cuda_pool_alloc& operator=(ggml_cuda_pool_alloc &&) = delete; +}; + + +// backend interface + +struct ggml_tensor_extra_gpu { + void * data_device[GGML_CUDA_MAX_DEVICES]; // 1 pointer for each device for split tensors + cudaEvent_t events[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS]; // events for synchronizing multiple GPUs +}; + +struct ggml_backend_cuda_context { + int device; + std::string name; + cudaEvent_t copy_event = nullptr; + + cudaStream_t streams[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS] = { { nullptr } }; + cublasHandle_t cublas_handles[GGML_CUDA_MAX_DEVICES] = {nullptr}; + + explicit ggml_backend_cuda_context(int device) : + device(device), + name(GGML_CUDA_NAME + std::to_string(device)) { + } + + ~ggml_backend_cuda_context() { + if (copy_event != nullptr) { + CUDA_CHECK(cudaEventDestroy(copy_event)); + } + for (int i = 0; i < GGML_CUDA_MAX_DEVICES; ++i) { + for (int j = 0; j < GGML_CUDA_MAX_STREAMS; ++j) { + if (streams[i][j] != nullptr) { + CUDA_CHECK(cudaStreamDestroy(streams[i][j])); + } + } + if (cublas_handles[i] != nullptr) { + CUBLAS_CHECK(cublasDestroy(cublas_handles[i])); + } + } + } + + cudaStream_t stream(int device, int stream) { + if (streams[device][stream] == nullptr) { + ggml_cuda_set_device(device); + CUDA_CHECK(cudaStreamCreateWithFlags(&streams[device][stream], cudaStreamNonBlocking)); + } + return streams[device][stream]; + } + + cudaStream_t stream() { + return stream(device, 0); + } + + cublasHandle_t cublas_handle(int device) { + if (cublas_handles[device] == nullptr) { + ggml_cuda_set_device(device); + CUBLAS_CHECK(cublasCreate(&cublas_handles[device])); + CUBLAS_CHECK(cublasSetMathMode(cublas_handles[device], CUBLAS_TF32_TENSOR_OP_MATH)); + } + return cublas_handles[device]; + } + + cublasHandle_t cublas_handle() { + return cublas_handle(device); + } + + // pool + std::unique_ptr pools[GGML_CUDA_MAX_DEVICES]; + + static std::unique_ptr new_pool_for_device(int device); + + ggml_cuda_pool & pool(int device) { + if (pools[device] == nullptr) { + pools[device] = new_pool_for_device(device); + } + return *pools[device]; + } + + ggml_cuda_pool & pool() { + return pool(device); + } +}; diff --git a/ggml-cuda/concat.cu b/ggml-cuda/concat.cu new file mode 100644 index 000000000..2941d2f17 --- /dev/null +++ b/ggml-cuda/concat.cu @@ -0,0 +1,49 @@ +#include "concat.cuh" + +static __global__ void concat_f32(const float * x,const float * y, float * dst, const int ne0, const int ne02) { + int nidx = threadIdx.x + blockIdx.x * blockDim.x; + if (nidx >= ne0) { + return; + } + // operation + int offset_dst = + nidx + + blockIdx.y * ne0 + + blockIdx.z * ne0 * gridDim.y; + if (blockIdx.z < ne02) { // src0 + int offset_src = + nidx + + blockIdx.y * ne0 + + blockIdx.z * ne0 * gridDim.y; + dst[offset_dst] = x[offset_src]; + } else { + int offset_src = + nidx + + blockIdx.y * ne0 + + (blockIdx.z - ne02) * ne0 * gridDim.y; + dst[offset_dst] = y[offset_src]; + } +} + +static void concat_f32_cuda(const float * x, const float * y, float * dst, const int ne0, int ne1, int ne2, int ne02, cudaStream_t stream) { + int num_blocks = (ne0 + CUDA_CONCAT_BLOCK_SIZE - 1) / CUDA_CONCAT_BLOCK_SIZE; + dim3 gridDim(num_blocks, ne1, ne2); + concat_f32<<>>(x, y, dst, ne0, ne02); +} + +void ggml_cuda_op_concat(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + const float * src0_d = (const float *)src0->data; + const float * src1_d = (const float *)src1->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT(src1->type == GGML_TYPE_F32); + GGML_ASSERT(dst->type == GGML_TYPE_F32); + + for (int i3 = 0; i3 < dst->ne[3]; i3++) { + concat_f32_cuda(src0_d + i3 * (src0->nb[3] / 4), src1_d + i3 * (src1->nb[3] / 4), dst_d + i3 * (dst->nb[3] / 4), dst->ne[0], dst->ne[1], dst->ne[2], src0->ne[2], stream); + } +} diff --git a/ggml-cuda/concat.cuh b/ggml-cuda/concat.cuh new file mode 100644 index 000000000..aa506a05f --- /dev/null +++ b/ggml-cuda/concat.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_CONCAT_BLOCK_SIZE 256 + +void ggml_cuda_op_concat(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/convert.cu b/ggml-cuda/convert.cu new file mode 100644 index 000000000..2516ecddd --- /dev/null +++ b/ggml-cuda/convert.cu @@ -0,0 +1,783 @@ +#include "convert.cuh" +#include "dequantize.cuh" + +#define CUDA_Q8_0_NE_ALIGN 2048 + +template +static __global__ void dequantize_block(const void * __restrict__ vx, dst_t * __restrict__ y, const int k) { + const int i = 2*(blockDim.x*blockIdx.x + threadIdx.x); + + if (i >= k) { + return; + } + + const int ib = i/qk; // block index + const int iqs = (i%qk)/qr; // quant index + const int iybs = i - i%qk; // y block start index + const int y_offset = qr == 1 ? 1 : qk/2; + + // dequantize + dfloat2 v; + dequantize_kernel(vx, ib, iqs, v); + + y[iybs + iqs + 0] = v.x; + y[iybs + iqs + y_offset] = v.y; +} + +template +static __global__ void dequantize_block_q8_0_f16(const void * __restrict__ vx, half * __restrict__ y, const int k) { +#if __CUDA_ARCH__ >= CC_PASCAL + constexpr int nint = CUDA_Q8_0_NE_ALIGN/sizeof(int) + WARP_SIZE; + + const int i0 = CUDA_Q8_0_NE_ALIGN*blockIdx.x; + const int * x0 = ((int *) vx) + blockIdx.x * nint; + half2 * y2 = (half2 *) (y + i0); + + __shared__ int vals[nint]; + +#pragma unroll + for (int ix0 = 0; ix0 < nint; ix0 += WARP_SIZE) { + if (need_check && i0*sizeof(block_q8_0)/QK8_0 + sizeof(int)*(ix0 + threadIdx.x) >= k*sizeof(block_q8_0)/QK8_0) { + break; + } + + const int ix = ix0 + threadIdx.x; + vals[ix] = x0[ix]; + } + +#pragma unroll + for (int iy = 0; iy < CUDA_Q8_0_NE_ALIGN; iy += 2*WARP_SIZE) { + if (need_check && i0 + iy + 2*threadIdx.x >= k) { + return; + } + + const half * b0 = ((const half *) vals) + (sizeof(block_q8_0)/sizeof(half)) * ((iy + 2*threadIdx.x)/QK8_0); + const half d = *b0; + const char2 qs = ((const char2 *) (b0 + 1))[threadIdx.x % (QK8_0/2)]; + + y2[iy/2 + threadIdx.x] = __hmul2(make_half2(qs.x, qs.y), __half2half2(d)); + } +#else + GGML_UNUSED(vx); + GGML_UNUSED(y); + GGML_UNUSED(k); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_PASCAL +} + +template +static __global__ void dequantize_block_q4_0(const void * __restrict__ vx, dst_t * __restrict__ yy, int nb32) { + + const int i = blockIdx.x; + + // assume 32 threads + const int tid = threadIdx.x; + const int il = tid/8; + const int ir = tid%8; + const int ib = 8*i + ir; + if (ib >= nb32) { + return; + } + + dst_t * y = yy + 256*i + 32*ir + 4*il; + + const block_q4_0 * x = (const block_q4_0 *)vx + ib; + const float d = __half2float(x->d); + const float dm = -8*d; + + const uint8_t * q = x->qs + 4*il; + + for (int l = 0; l < 4; ++l) { + y[l+ 0] = d * (q[l] & 0xF) + dm; + y[l+16] = d * (q[l] >> 4) + dm; + } +} + +template +static __global__ void dequantize_block_q4_1(const void * __restrict__ vx, dst_t * __restrict__ yy, int nb32) { + + const int i = blockIdx.x; + + // assume 32 threads + const int tid = threadIdx.x; + const int il = tid/8; + const int ir = tid%8; + const int ib = 8*i + ir; + if (ib >= nb32) { + return; + } + + dst_t * y = yy + 256*i + 32*ir + 4*il; + + const block_q4_1 * x = (const block_q4_1 *)vx + ib; + const float2 d = __half22float2(x->dm); + + const uint8_t * q = x->qs + 4*il; + + for (int l = 0; l < 4; ++l) { + y[l+ 0] = d.x * (q[l] & 0xF) + d.y; + y[l+16] = d.x * (q[l] >> 4) + d.y; + } +} + +//================================== k-quants + +template +static __global__ void dequantize_block_q2_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_q2_K * x = (const block_q2_K *) vx; + + const int tid = threadIdx.x; +#if QK_K == 256 + const int n = tid/32; + const int l = tid - 32*n; + const int is = 8*n + l/16; + + const uint8_t q = x[i].qs[32*n + l]; + dst_t * y = yy + i*QK_K + 128*n; + + float dall = __low2half(x[i].dm); + float dmin = __high2half(x[i].dm); + y[l+ 0] = dall * (x[i].scales[is+0] & 0xF) * ((q >> 0) & 3) - dmin * (x[i].scales[is+0] >> 4); + y[l+32] = dall * (x[i].scales[is+2] & 0xF) * ((q >> 2) & 3) - dmin * (x[i].scales[is+2] >> 4); + y[l+64] = dall * (x[i].scales[is+4] & 0xF) * ((q >> 4) & 3) - dmin * (x[i].scales[is+4] >> 4); + y[l+96] = dall * (x[i].scales[is+6] & 0xF) * ((q >> 6) & 3) - dmin * (x[i].scales[is+6] >> 4); +#else + const int is = tid/16; // 0 or 1 + const int il = tid%16; // 0...15 + const uint8_t q = x[i].qs[il] >> (2*is); + dst_t * y = yy + i*QK_K + 16*is + il; + float dall = __low2half(x[i].dm); + float dmin = __high2half(x[i].dm); + y[ 0] = dall * (x[i].scales[is+0] & 0xF) * ((q >> 0) & 3) - dmin * (x[i].scales[is+0] >> 4); + y[32] = dall * (x[i].scales[is+2] & 0xF) * ((q >> 4) & 3) - dmin * (x[i].scales[is+2] >> 4); +#endif + +} + +template +static __global__ void dequantize_block_q3_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_q3_K * x = (const block_q3_K *) vx; + +#if QK_K == 256 + const int r = threadIdx.x/4; + const int tid = r/2; + const int is0 = r%2; + const int l0 = 16*is0 + 4*(threadIdx.x%4); + const int n = tid / 4; + const int j = tid - 4*n; + + uint8_t m = 1 << (4*n + j); + int is = 8*n + 2*j + is0; + int shift = 2*j; + + int8_t us = is < 4 ? (x[i].scales[is-0] & 0xF) | (((x[i].scales[is+8] >> 0) & 3) << 4) : + is < 8 ? (x[i].scales[is-0] & 0xF) | (((x[i].scales[is+4] >> 2) & 3) << 4) : + is < 12 ? (x[i].scales[is-8] >> 4) | (((x[i].scales[is+0] >> 4) & 3) << 4) : + (x[i].scales[is-8] >> 4) | (((x[i].scales[is-4] >> 6) & 3) << 4); + float d_all = x[i].d; + float dl = d_all * (us - 32); + + dst_t * y = yy + i*QK_K + 128*n + 32*j; + const uint8_t * q = x[i].qs + 32*n; + const uint8_t * hm = x[i].hmask; + + for (int l = l0; l < l0+4; ++l) y[l] = dl * ((int8_t)((q[l] >> shift) & 3) - ((hm[l] & m) ? 0 : 4)); +#else + const int tid = threadIdx.x; + const int is = tid/16; // 0 or 1 + const int il = tid%16; // 0...15 + const int im = il/8; // 0...1 + const int in = il%8; // 0...7 + + dst_t * y = yy + i*QK_K + 16*is + il; + + const uint8_t q = x[i].qs[il] >> (2*is); + const uint8_t h = x[i].hmask[in] >> (2*is + im); + const float d = (float)x[i].d; + + if (is == 0) { + y[ 0] = d * ((x[i].scales[0] & 0xF) - 8) * ((int8_t)((q >> 0) & 3) - ((h >> 0) & 1 ? 0 : 4)); + y[32] = d * ((x[i].scales[1] & 0xF) - 8) * ((int8_t)((q >> 4) & 3) - ((h >> 4) & 1 ? 0 : 4)); + } else { + y[ 0] = d * ((x[i].scales[0] >> 4) - 8) * ((int8_t)((q >> 0) & 3) - ((h >> 0) & 1 ? 0 : 4)); + y[32] = d * ((x[i].scales[1] >> 4) - 8) * ((int8_t)((q >> 4) & 3) - ((h >> 4) & 1 ? 0 : 4)); + } +#endif + +} + +#if QK_K == 256 +static inline __device__ void get_scale_min_k4(int j, const uint8_t * q, uint8_t & d, uint8_t & m) { + if (j < 4) { + d = q[j] & 63; m = q[j + 4] & 63; + } else { + d = (q[j+4] & 0xF) | ((q[j-4] >> 6) << 4); + m = (q[j+4] >> 4) | ((q[j-0] >> 6) << 4); + } +} +#endif + +template +static __global__ void dequantize_block_q4_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { + const block_q4_K * x = (const block_q4_K *) vx; + + const int i = blockIdx.x; + +#if QK_K == 256 + // assume 32 threads + const int tid = threadIdx.x; + const int il = tid/8; + const int ir = tid%8; + const int is = 2*il; + const int n = 4; + + dst_t * y = yy + i*QK_K + 64*il + n*ir; + + const float dall = __low2half(x[i].dm); + const float dmin = __high2half(x[i].dm); + + const uint8_t * q = x[i].qs + 32*il + n*ir; + + uint8_t sc, m; + get_scale_min_k4(is + 0, x[i].scales, sc, m); + const float d1 = dall * sc; const float m1 = dmin * m; + get_scale_min_k4(is + 1, x[i].scales, sc, m); + const float d2 = dall * sc; const float m2 = dmin * m; + for (int l = 0; l < n; ++l) { + y[l + 0] = d1 * (q[l] & 0xF) - m1; + y[l +32] = d2 * (q[l] >> 4) - m2; + } +#else + const int tid = threadIdx.x; + const uint8_t * q = x[i].qs; + dst_t * y = yy + i*QK_K; + const float d = (float)x[i].dm[0]; + const float m = (float)x[i].dm[1]; + y[tid+ 0] = d * (x[i].scales[0] & 0xF) * (q[tid] & 0xF) - m * (x[i].scales[0] >> 4); + y[tid+32] = d * (x[i].scales[1] & 0xF) * (q[tid] >> 4) - m * (x[i].scales[1] >> 4); +#endif +} + +template +static __global__ void dequantize_block_q5_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { + const block_q5_K * x = (const block_q5_K *) vx; + + const int i = blockIdx.x; + +#if QK_K == 256 + // assume 64 threads - this is very slightly better than the one below + const int tid = threadIdx.x; + const int il = tid/16; // il is in 0...3 + const int ir = tid%16; // ir is in 0...15 + const int is = 2*il; // is is in 0...6 + + dst_t * y = yy + i*QK_K + 64*il + 2*ir; + + const float dall = __low2half(x[i].dm); + const float dmin = __high2half(x[i].dm); + + const uint8_t * ql = x[i].qs + 32*il + 2*ir; + const uint8_t * qh = x[i].qh + 2*ir; + + uint8_t sc, m; + get_scale_min_k4(is + 0, x[i].scales, sc, m); + const float d1 = dall * sc; const float m1 = dmin * m; + get_scale_min_k4(is + 1, x[i].scales, sc, m); + const float d2 = dall * sc; const float m2 = dmin * m; + + uint8_t hm = 1 << (2*il); + y[ 0] = d1 * ((ql[ 0] & 0xF) + (qh[ 0] & hm ? 16 : 0)) - m1; + y[ 1] = d1 * ((ql[ 1] & 0xF) + (qh[ 1] & hm ? 16 : 0)) - m1; + hm <<= 1; + y[32] = d2 * ((ql[ 0] >> 4) + (qh[ 0] & hm ? 16 : 0)) - m2; + y[33] = d2 * ((ql[ 1] >> 4) + (qh[ 1] & hm ? 16 : 0)) - m2; +#else + const int tid = threadIdx.x; + const uint8_t q = x[i].qs[tid]; + const int im = tid/8; // 0...3 + const int in = tid%8; // 0...7 + const int is = tid/16; // 0 or 1 + const uint8_t h = x[i].qh[in] >> im; + const float d = x[i].d; + dst_t * y = yy + i*QK_K + tid; + y[ 0] = d * x[i].scales[is+0] * ((q & 0xF) - ((h >> 0) & 1 ? 0 : 16)); + y[32] = d * x[i].scales[is+2] * ((q >> 4) - ((h >> 4) & 1 ? 0 : 16)); +#endif +} + +template +static __global__ void dequantize_block_q6_K(const void * __restrict__ vx, dst_t * __restrict__ yy) { + const block_q6_K * x = (const block_q6_K *) vx; + + const int i = blockIdx.x; +#if QK_K == 256 + + // assume 64 threads - this is very slightly better than the one below + const int tid = threadIdx.x; + const int ip = tid/32; // ip is 0 or 1 + const int il = tid - 32*ip; // 0...32 + const int is = 8*ip + il/16; + + dst_t * y = yy + i*QK_K + 128*ip + il; + + const float d = x[i].d; + + const uint8_t * ql = x[i].ql + 64*ip + il; + const uint8_t qh = x[i].qh[32*ip + il]; + const int8_t * sc = x[i].scales + is; + + y[ 0] = d * sc[0] * ((int8_t)((ql[ 0] & 0xF) | (((qh >> 0) & 3) << 4)) - 32); + y[32] = d * sc[2] * ((int8_t)((ql[32] & 0xF) | (((qh >> 2) & 3) << 4)) - 32); + y[64] = d * sc[4] * ((int8_t)((ql[ 0] >> 4) | (((qh >> 4) & 3) << 4)) - 32); + y[96] = d * sc[6] * ((int8_t)((ql[32] >> 4) | (((qh >> 6) & 3) << 4)) - 32); +#else + + // assume 32 threads + const int tid = threadIdx.x; + const int ip = tid/16; // 0 or 1 + const int il = tid - 16*ip; // 0...15 + + dst_t * y = yy + i*QK_K + 16*ip + il; + + const float d = x[i].d; + + const uint8_t ql = x[i].ql[16*ip + il]; + const uint8_t qh = x[i].qh[il] >> (2*ip); + const int8_t * sc = x[i].scales; + + y[ 0] = d * sc[ip+0] * ((int8_t)((ql & 0xF) | (((qh >> 0) & 3) << 4)) - 32); + y[32] = d * sc[ip+2] * ((int8_t)((ql >> 4) | (((qh >> 4) & 3) << 4)) - 32); +#endif +} + +template +static __global__ void dequantize_block_iq2_xxs(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_iq2_xxs * x = (const block_iq2_xxs *) vx; + + const int tid = threadIdx.x; +#if QK_K == 256 + const int il = tid/8; // 0...3 + const int ib = tid%8; // 0...7 + dst_t * y = yy + i*QK_K + 32*ib + 8*il; + const uint16_t * q2 = x[i].qs + 4*ib; + const uint8_t * aux8 = (const uint8_t *)q2; + const uint8_t * grid = (const uint8_t *)(iq2xxs_grid + aux8[il]); + const uint32_t aux32 = q2[2] | (q2[3] << 16); + const float d = (float)x[i].d * (0.5f + (aux32 >> 28)) * 0.25f; + const uint8_t signs = ksigns_iq2xs[(aux32 >> 7*il) & 127]; + for (int j = 0; j < 8; ++j) y[j] = d * grid[j] * (signs & kmask_iq2xs[j] ? -1.f : 1.f); +#else + assert(false); +#endif + +} + +template +static __global__ void dequantize_block_iq2_xs(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_iq2_xs * x = (const block_iq2_xs *) vx; + + const int tid = threadIdx.x; +#if QK_K == 256 + const int il = tid/8; // 0...3 + const int ib = tid%8; // 0...7 + dst_t * y = yy + i*QK_K + 32*ib + 8*il; + const uint16_t * q2 = x[i].qs + 4*ib; + const uint8_t * grid = (const uint8_t *)(iq2xs_grid + (q2[il] & 511)); + const float d = (float)x[i].d * (0.5f + ((x[i].scales[ib] >> 4*(il/2)) & 0xf)) * 0.25f; + const uint8_t signs = ksigns_iq2xs[q2[il] >> 9]; + for (int j = 0; j < 8; ++j) y[j] = d * grid[j] * (signs & kmask_iq2xs[j] ? -1.f : 1.f); +#else + assert(false); +#endif + +} + +template +static __global__ void dequantize_block_iq2_s(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_iq2_s * x = (const block_iq2_s *) vx; + + const int tid = threadIdx.x; +#if QK_K == 256 + const int il = tid/8; // 0...3 + const int ib = tid%8; // 0...7 + dst_t * y = yy + i*QK_K + 32*ib + 8*il; + const uint8_t * grid = (const uint8_t *)(iq2s_grid + (x[i].qs[4*ib+il] | ((x[i].qh[ib] << (8-2*il)) & 0x300))); + const float d = (float)x[i].d * (0.5f + ((x[i].scales[ib] >> 4*(il/2)) & 0xf)) * 0.25f; + const uint8_t signs = x[i].qs[QK_K/8+4*ib+il]; + for (int j = 0; j < 8; ++j) y[j] = d * grid[j] * (signs & kmask_iq2xs[j] ? -1.f : 1.f); +#else + assert(false); +#endif + +} + +template +static __global__ void dequantize_block_iq3_xxs(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_iq3_xxs * x = (const block_iq3_xxs *) vx; + + const int tid = threadIdx.x; +#if QK_K == 256 + const int il = tid/8; // 0...3 + const int ib = tid%8; // 0...7 + dst_t * y = yy + i*QK_K + 32*ib + 8*il; + const uint8_t * q3 = x[i].qs + 8*ib; + const uint16_t * gas = (const uint16_t *)(x[i].qs + QK_K/4) + 2*ib; + const uint8_t * grid1 = (const uint8_t *)(iq3xxs_grid + q3[2*il+0]); + const uint8_t * grid2 = (const uint8_t *)(iq3xxs_grid + q3[2*il+1]); + const uint32_t aux32 = gas[0] | (gas[1] << 16); + const float d = (float)x[i].d * (0.5f + (aux32 >> 28)) * 0.5f; + const uint8_t signs = ksigns_iq2xs[(aux32 >> 7*il) & 127]; + for (int j = 0; j < 4; ++j) { + y[j+0] = d * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f); + y[j+4] = d * grid2[j] * (signs & kmask_iq2xs[j+4] ? -1.f : 1.f); + } +#else + assert(false); +#endif + +} + +template +static __global__ void dequantize_block_iq3_s(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_iq3_s * x = (const block_iq3_s *) vx; + + const int tid = threadIdx.x; +#if QK_K == 256 + const int il = tid/8; // 0...3 + const int ib = tid%8; // 0...7 + dst_t * y = yy + i*QK_K + 32*ib + 8*il; + const uint8_t * qs = x[i].qs + 8*ib; + const uint8_t * grid1 = (const uint8_t *)(iq3s_grid + (qs[2*il+0] | ((x[i].qh[ib] << (8-2*il)) & 256))); + const uint8_t * grid2 = (const uint8_t *)(iq3s_grid + (qs[2*il+1] | ((x[i].qh[ib] << (7-2*il)) & 256))); + const float d = (float)x[i].d * (1 + 2*((x[i].scales[ib/2] >> 4*(ib%2)) & 0xf)); + const uint8_t signs = x[i].signs[4*ib + il]; + for (int j = 0; j < 4; ++j) { + y[j+0] = d * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f); + y[j+4] = d * grid2[j] * (signs & kmask_iq2xs[j+4] ? -1.f : 1.f); + } +#else + assert(false); +#endif + +} + +template +static __global__ void dequantize_block_iq1_s(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_iq1_s * x = (const block_iq1_s *) vx; + + const int tid = threadIdx.x; +#if QK_K == 256 + const int il = tid/8; // 0...3 + const int ib = tid%8; // 0...7 + dst_t * y = yy + i*QK_K + 32*ib + 8*il; + const float delta = x[i].qh[ib] & 0x8000 ? -1 - IQ1S_DELTA : -1 + IQ1S_DELTA; + const float d = (float)x[i].d * (2*((x[i].qh[ib] >> 12) & 7) + 1); + uint32_t grid32[2]; const int8_t * q = (const int8_t *)grid32; + grid32[0] = iq1s_grid_gpu[x[i].qs[4*ib+il] | (((x[i].qh[ib] >> 3*il) & 7) << 8)]; + grid32[1] = (grid32[0] >> 4) & 0x0f0f0f0f; + grid32[0] &= 0x0f0f0f0f; + for (int j = 0; j < 8; ++j) { + y[j] = d * (q[j] + delta); + } +#else + assert(false); +#endif + +} + +template +static __global__ void dequantize_block_iq4_nl(const void * __restrict__ vx, dst_t * __restrict__ yy) { + + const int i = blockIdx.x; + const block_iq4_nl * x = (const block_iq4_nl *) vx + i*(QK_K/QK4_NL); + + const int tid = threadIdx.x; + const int il = tid/8; // 0...3 + const int ib = tid%8; // 0...7 + dst_t * y = yy + i*QK_K + 32*ib + 4*il; + const uint8_t * q4 = x[ib].qs + 4*il; + const float d = (float)x[ib].d; + for (int j = 0; j < 4; ++j) { + y[j+ 0] = d * kvalues_iq4nl[q4[j] & 0xf]; + y[j+16] = d * kvalues_iq4nl[q4[j] >> 4]; + } + +} + +#if QK_K != 64 +template +static __global__ void dequantize_block_iq4_xs(const void * __restrict__ vx, dst_t * __restrict__ yy) { + const int i = blockIdx.x; + const block_iq4_xs * x = (const block_iq4_xs *)vx; + + const int tid = threadIdx.x; + const int il = tid/8; // 0...3 + const int ib = tid%8; // 0...7 + dst_t * y = yy + i*QK_K + 32*ib + 4*il; + const uint8_t * q4 = x[i].qs + 16*ib + 4*il; + const float d = (float)x[i].d * ((((x[i].scales_l[ib/2] >> 4*(ib%2)) & 0xf) | (((x[i].scales_h >> 2*ib) & 3) << 4)) - 32); + for (int j = 0; j < 4; ++j) { + y[j+ 0] = d * kvalues_iq4nl[q4[j] & 0xf]; + y[j+16] = d * kvalues_iq4nl[q4[j] >> 4]; + } +} +#endif + +template +static void dequantize_block_cuda(const void * __restrict__ vx, dst_t * __restrict__ y, const int k, cudaStream_t stream) { + const int num_blocks = (k + 2*CUDA_DEQUANTIZE_BLOCK_SIZE - 1) / (2*CUDA_DEQUANTIZE_BLOCK_SIZE); + dequantize_block<<>>(vx, y, k); +} + +static void dequantize_block_q8_0_f16_cuda(const void * __restrict__ vx, half * __restrict__ y, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_Q8_0_NE_ALIGN - 1) / CUDA_Q8_0_NE_ALIGN; + if (k % CUDA_Q8_0_NE_ALIGN == 0) { + const bool need_check = false; + dequantize_block_q8_0_f16<<>>(vx, y, k); + } else { + const bool need_check = true; + dequantize_block_q8_0_f16<<>>(vx, y, k); + } +} + +template +static void dequantize_row_q2_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; +#if QK_K == 256 + dequantize_block_q2_K<<>>(vx, y); +#else + dequantize_block_q2_K<<>>(vx, y); +#endif +} + +template +static void dequantize_row_q3_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; +#if QK_K == 256 + dequantize_block_q3_K<<>>(vx, y); +#else + dequantize_block_q3_K<<>>(vx, y); +#endif +} + +template +static void dequantize_row_q4_0_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb32 = k / 32; + const int nb = (k + 255) / 256; + dequantize_block_q4_0<<>>(vx, y, nb32); +} + +template +static void dequantize_row_q4_1_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb32 = k / 32; + const int nb = (k + 255) / 256; + dequantize_block_q4_1<<>>(vx, y, nb32); +} + +template +static void dequantize_row_q4_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; + dequantize_block_q4_K<<>>(vx, y); +} + +template +static void dequantize_row_q5_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; +#if QK_K == 256 + dequantize_block_q5_K<<>>(vx, y); +#else + dequantize_block_q5_K<<>>(vx, y); +#endif +} + +template +static void dequantize_row_q6_K_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; +#if QK_K == 256 + dequantize_block_q6_K<<>>(vx, y); +#else + dequantize_block_q6_K<<>>(vx, y); +#endif +} + +template +static void dequantize_row_iq2_xxs_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; + dequantize_block_iq2_xxs<<>>(vx, y); +} + +template +static void dequantize_row_iq2_xs_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; + dequantize_block_iq2_xs<<>>(vx, y); +} + +template +static void dequantize_row_iq2_s_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; + dequantize_block_iq2_s<<>>(vx, y); +} + +template +static void dequantize_row_iq3_xxs_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; + dequantize_block_iq3_xxs<<>>(vx, y); +} + +template +static void dequantize_row_iq3_s_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; + dequantize_block_iq3_s<<>>(vx, y); +} + +template +static void dequantize_row_iq1_s_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = k / QK_K; + dequantize_block_iq1_s<<>>(vx, y); +} + +template +static void dequantize_row_iq4_nl_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = (k + QK_K - 1) / QK_K; + dequantize_block_iq4_nl<<>>(vx, y); +} + +template +static void dequantize_row_iq4_xs_cuda(const void * vx, dst_t * y, const int k, cudaStream_t stream) { + const int nb = (k + QK_K - 1) / QK_K; +#if QK_K == 64 + dequantize_block_iq4_nl<<>>(vx, y); +#else + dequantize_block_iq4_xs<<>>(vx, y); +#endif +} + +template +static __global__ void convert_unary(const void * __restrict__ vx, dst_t * __restrict__ y, const int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + + const src_t * x = (src_t *) vx; + + y[i] = x[i]; +} + +template +static void convert_unary_cuda(const void * __restrict__ vx, dst_t * __restrict__ y, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_DEQUANTIZE_BLOCK_SIZE - 1) / CUDA_DEQUANTIZE_BLOCK_SIZE; + convert_unary<<>>(vx, y, k); +} + +to_fp16_cuda_t ggml_get_to_fp16_cuda(ggml_type type) { + int id; + switch (type) { + case GGML_TYPE_Q4_0: + return dequantize_row_q4_0_cuda; + case GGML_TYPE_Q4_1: + return dequantize_row_q4_1_cuda; + case GGML_TYPE_Q5_0: + return dequantize_block_cuda; + case GGML_TYPE_Q5_1: + return dequantize_block_cuda; + case GGML_TYPE_Q8_0: + CUDA_CHECK(cudaGetDevice(&id)); + if (ggml_cuda_info().devices[id].cc >= CC_PASCAL) { + return dequantize_block_q8_0_f16_cuda; + } + return dequantize_block_cuda; + case GGML_TYPE_Q2_K: + return dequantize_row_q2_K_cuda; + case GGML_TYPE_Q3_K: + return dequantize_row_q3_K_cuda; + case GGML_TYPE_Q4_K: + return dequantize_row_q4_K_cuda; + case GGML_TYPE_Q5_K: + return dequantize_row_q5_K_cuda; + case GGML_TYPE_Q6_K: + return dequantize_row_q6_K_cuda; + case GGML_TYPE_IQ2_XXS: + return dequantize_row_iq2_xxs_cuda; + case GGML_TYPE_IQ2_XS: + return dequantize_row_iq2_xs_cuda; + case GGML_TYPE_IQ2_S: + return dequantize_row_iq2_s_cuda; + case GGML_TYPE_IQ3_XXS: + return dequantize_row_iq3_xxs_cuda; + case GGML_TYPE_IQ1_S: + return dequantize_row_iq1_s_cuda; + case GGML_TYPE_IQ4_NL: + return dequantize_row_iq4_nl_cuda; + case GGML_TYPE_IQ4_XS: + return dequantize_row_iq4_xs_cuda; + case GGML_TYPE_IQ3_S: + return dequantize_row_iq3_s_cuda; + case GGML_TYPE_F32: + return convert_unary_cuda; + default: + return nullptr; + } +} + +to_fp32_cuda_t ggml_get_to_fp32_cuda(ggml_type type) { + switch (type) { + case GGML_TYPE_Q4_0: + return dequantize_row_q4_0_cuda; + case GGML_TYPE_Q4_1: + return dequantize_row_q4_1_cuda; + case GGML_TYPE_Q5_0: + return dequantize_block_cuda; + case GGML_TYPE_Q5_1: + return dequantize_block_cuda; + case GGML_TYPE_Q8_0: + return dequantize_block_cuda; + case GGML_TYPE_Q2_K: + return dequantize_row_q2_K_cuda; + case GGML_TYPE_Q3_K: + return dequantize_row_q3_K_cuda; + case GGML_TYPE_Q4_K: + return dequantize_row_q4_K_cuda; + case GGML_TYPE_Q5_K: + return dequantize_row_q5_K_cuda; + case GGML_TYPE_Q6_K: + return dequantize_row_q6_K_cuda; + case GGML_TYPE_IQ2_XXS: + return dequantize_row_iq2_xxs_cuda; + case GGML_TYPE_IQ2_XS: + return dequantize_row_iq2_xs_cuda; + case GGML_TYPE_IQ2_S: + return dequantize_row_iq2_s_cuda; + case GGML_TYPE_IQ3_XXS: + return dequantize_row_iq3_xxs_cuda; + case GGML_TYPE_IQ1_S: + return dequantize_row_iq1_s_cuda; + case GGML_TYPE_IQ4_NL: + return dequantize_row_iq4_nl_cuda; + case GGML_TYPE_IQ4_XS: + return dequantize_row_iq4_xs_cuda; + case GGML_TYPE_IQ3_S: + return dequantize_row_iq3_s_cuda; + case GGML_TYPE_F16: + return convert_unary_cuda; + default: + return nullptr; + } +} diff --git a/ggml-cuda/convert.cuh b/ggml-cuda/convert.cuh new file mode 100644 index 000000000..db34c0be9 --- /dev/null +++ b/ggml-cuda/convert.cuh @@ -0,0 +1,13 @@ +#include "common.cuh" + +#define CUDA_DEQUANTIZE_BLOCK_SIZE 256 + +template +using to_t_cuda_t = void (*)(const void * __restrict__ x, T * __restrict__ y, int k, cudaStream_t stream); + +typedef to_t_cuda_t to_fp32_cuda_t; +typedef to_t_cuda_t to_fp16_cuda_t; + +to_fp16_cuda_t ggml_get_to_fp16_cuda(ggml_type type); + +to_fp32_cuda_t ggml_get_to_fp32_cuda(ggml_type type); diff --git a/ggml-cuda/cpy.cu b/ggml-cuda/cpy.cu new file mode 100644 index 000000000..16d9c8fff --- /dev/null +++ b/ggml-cuda/cpy.cu @@ -0,0 +1,461 @@ +#include "cpy.cuh" + +typedef void (*cpy_kernel_t)(const char * cx, char * cdst); + +static __device__ void cpy_1_f32_f32(const char * cxi, char * cdsti) { + const float * xi = (const float *) cxi; + float * dsti = (float *) cdsti; + + *dsti = *xi; +} + +static __device__ void cpy_1_f32_f16(const char * cxi, char * cdsti) { + const float * xi = (const float *) cxi; + half * dsti = (half *) cdsti; + + *dsti = __float2half(*xi); +} + +static __device__ void cpy_1_f16_f16(const char * cxi, char * cdsti) { + const half * xi = (const half *) cxi; + half * dsti = (half *) cdsti; + + *dsti = *xi; +} + +static __device__ void cpy_1_f16_f32(const char * cxi, char * cdsti) { + const half * xi = (const half *) cxi; + float * dsti = (float *) cdsti; + + *dsti = *xi; +} + +template +static __global__ void cpy_f32_f16(const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, + const int nb12, const int nb13) { + const int64_t i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= ne) { + return; + } + + // determine indices i03/i13, i02/i12, i01/i11, i00/i10 as a function of index i of flattened tensor + // then combine those indices with the corresponding byte offsets to get the total offsets + const int64_t i03 = i/(ne00 * ne01 * ne02); + const int64_t i02 = (i - i03*ne00*ne01*ne02 )/ (ne00*ne01); + const int64_t i01 = (i - i03*ne00*ne01*ne02 - i02*ne01*ne00) / ne00; + const int64_t i00 = i - i03*ne00*ne01*ne02 - i02*ne01*ne00 - i01*ne00; + const int64_t x_offset = i00*nb00 + i01*nb01 + i02*nb02 + i03 * nb03; + + const int64_t i13 = i/(ne10 * ne11 * ne12); + const int64_t i12 = (i - i13*ne10*ne11*ne12) / (ne10*ne11); + const int64_t i11 = (i - i13*ne10*ne11*ne12 - i12*ne10*ne11) / ne10; + const int64_t i10 = i - i13*ne10*ne11*ne12 - i12*ne10*ne11 - i11*ne10; + const int64_t dst_offset = i10*nb10 + i11*nb11 + i12*nb12 + i13 * nb13; + + cpy_1(cx + x_offset, cdst + dst_offset); +} + +static __device__ void cpy_blck_f32_q8_0(const char * cxi, char * cdsti) { + const float * xi = (const float *) cxi; + block_q8_0 * dsti = (block_q8_0 *) cdsti; + + float amax = 0.0f; // absolute max + + for (int j = 0; j < QK8_0; j++) { + const float v = xi[j]; + amax = fmaxf(amax, fabsf(v)); + } + + const float d = amax / ((1 << 7) - 1); + const float id = d ? 1.0f/d : 0.0f; + + dsti->d = d; + + for (int j = 0; j < QK8_0; ++j) { + const float x0 = xi[j]*id; + + dsti->qs[j] = roundf(x0); + } +} + +static __device__ void cpy_blck_f32_q4_0(const char * cxi, char * cdsti) { + const float * xi = (const float *) cxi; + block_q4_0 * dsti = (block_q4_0 *) cdsti; + + float amax = 0.0f; + float vmax = 0.0f; + + for (int j = 0; j < QK4_0; ++j) { + const float v = xi[j]; + if (amax < fabsf(v)) { + amax = fabsf(v); + vmax = v; + } + } + + const float d = vmax / -8; + const float id = d ? 1.0f/d : 0.0f; + + dsti->d = d; + + for (int j = 0; j < QK4_0/2; ++j) { + const float x0 = xi[0 + j]*id; + const float x1 = xi[QK4_0/2 + j]*id; + + const uint8_t xi0 = min(15, (int8_t)(x0 + 8.5f)); + const uint8_t xi1 = min(15, (int8_t)(x1 + 8.5f)); + + dsti->qs[j] = xi0; + dsti->qs[j] |= xi1 << 4; + } +} + +static __device__ void cpy_blck_f32_q4_1(const char * cxi, char * cdsti) { + const float * xi = (const float *) cxi; + block_q4_1 * dsti = (block_q4_1 *) cdsti; + + float vmin = FLT_MAX; + float vmax = -FLT_MAX; + + for (int j = 0; j < QK4_1; ++j) { + const float v = xi[j]; + + if (v < vmin) vmin = v; + if (v > vmax) vmax = v; + } + + const float d = (vmax - vmin) / ((1 << 4) - 1); + const float id = d ? 1.0f/d : 0.0f; + + dsti->dm.x = d; + dsti->dm.y = vmin; + + for (int j = 0; j < QK4_1/2; ++j) { + const float x0 = (xi[0 + j] - vmin)*id; + const float x1 = (xi[QK4_1/2 + j] - vmin)*id; + + const uint8_t xi0 = min(15, (int8_t)(x0 + 0.5f)); + const uint8_t xi1 = min(15, (int8_t)(x1 + 0.5f)); + + dsti->qs[j] = xi0; + dsti->qs[j] |= xi1 << 4; + } +} + +static __device__ void cpy_blck_f32_q5_0(const char * cxi, char * cdsti) { + const float * xi = (const float *) cxi; + block_q5_0 * dsti = (block_q5_0 *) cdsti; + + float amax = 0.0f; + float vmax = 0.0f; + + for (int j = 0; j < QK5_0; ++j) { + const float v = xi[j]; + if (amax < fabsf(v)) { + amax = fabsf(v); + vmax = v; + } + } + + const float d = vmax / -16; + const float id = d ? 1.0f/d : 0.0f; + + dsti->d = d; + + uint32_t qh = 0; + for (int j = 0; j < QK5_0/2; ++j) { + const float x0 = xi[0 + j]*id; + const float x1 = xi[QK5_0/2 + j]*id; + + const uint8_t xi0 = min(31, (int8_t)(x0 + 16.5f)); + const uint8_t xi1 = min(31, (int8_t)(x1 + 16.5f)); + + dsti->qs[j] = (xi0 & 0xf) | ((xi1 & 0xf) << 4); + qh |= ((xi0 & 0x10u) >> 4) << (j + 0); + qh |= ((xi1 & 0x10u) >> 4) << (j + QK5_0/2); + } + memcpy(dsti->qh, &qh, sizeof(qh)); +} + +static __device__ void cpy_blck_f32_q5_1(const char * cxi, char * cdsti) { + const float * xi = (const float *) cxi; + block_q5_1 * dsti = (block_q5_1 *) cdsti; + + float min = xi[0]; + float max = xi[0]; + + for (int j = 1; j < QK5_1; ++j) { + const float v = xi[j]; + min = v < min ? v : min; + max = v > max ? v : max; + } + + const float d = (max - min) / 31; + const float id = d ? 1.0f/d : 0.0f; + + dsti->dm.x = d; + dsti->dm.y = min; + + uint32_t qh = 0; + for (int j = 0; j < QK5_1/2; ++j) { + const float x0 = (xi[0 + j] - min)*id; + const float x1 = (xi[QK5_1/2 + j] - min)*id; + + const uint8_t xi0 = (uint8_t)(x0 + 0.5f); + const uint8_t xi1 = (uint8_t)(x1 + 0.5f); + + dsti->qs[j] = (xi0 & 0xf) | ((xi1 & 0xf) << 4); + qh |= ((xi0 & 0x10u) >> 4) << (j + 0); + qh |= ((xi1 & 0x10u) >> 4) << (j + QK5_1/2); + } + memcpy(dsti->qh, &qh, sizeof(qh)); +} + + +static __device__ __forceinline__ int best_index_int8(int n, const int8_t * val, float x) { + if (x <= val[0]) return 0; + if (x >= val[n-1]) return n-1; + int ml = 0, mu = n-1; + while (mu-ml > 1) { + int mav = (ml+mu)/2; + if (x < val[mav]) mu = mav; else ml = mav; + } + return x - val[mu-1] < val[mu] - x ? mu-1 : mu; +} + +static __device__ void cpy_blck_f32_iq4_nl(const char * cxi, char * cdsti) { + const float * xi = (const float *) cxi; + block_iq4_nl * dsti = (block_iq4_nl *) cdsti; + + float amax = 0.0f; + float vmax = 0.0f; + + for (int j = 0; j < QK4_NL; ++j) { + const float v = xi[j]; + if (amax < fabsf(v)) { + amax = fabsf(v); + vmax = v; + } + } + + float d = vmax / kvalues_iq4nl[0]; + const float id = d ? 1.0f/d : 0.0f; + + float sumqx = 0, sumq2 = 0; + for (int j = 0; j < QK4_NL/2; ++j) { + const float x0 = xi[0 + j]*id; + const float x1 = xi[QK4_NL/2 + j]*id; + const uint8_t xi0 = best_index_int8(16, kvalues_iq4nl, x0); + const uint8_t xi1 = best_index_int8(16, kvalues_iq4nl, x1); + dsti->qs[j] = xi0 | (xi1 << 4); + const float v0 = kvalues_iq4nl[xi0]; + const float v1 = kvalues_iq4nl[xi1]; + const float w0 = xi[0 + j]*xi[0 + j]; + const float w1 = xi[QK4_NL/2 + j]*xi[QK4_NL/2 + j]; + sumqx += w0*v0*xi[j] + w1*v1*xi[QK4_NL/2 + j]; + sumq2 += w0*v0*v0 + w1*v1*v1; + } + + dsti->d = sumq2 > 0 ? sumqx/sumq2 : d; +} + +template +static __global__ void cpy_f32_q(const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, + const int nb12, const int nb13) { + const int i = (blockDim.x*blockIdx.x + threadIdx.x)*qk; + + if (i >= ne) { + return; + } + + const int i03 = i/(ne00 * ne01 * ne02); + const int i02 = (i - i03*ne00*ne01*ne02 )/ (ne00*ne01); + const int i01 = (i - i03*ne00*ne01*ne02 - i02*ne01*ne00) / ne00; + const int i00 = i - i03*ne00*ne01*ne02 - i02*ne01*ne00 - i01*ne00; + const int x_offset = i00*nb00 + i01*nb01 + i02*nb02 + i03 * nb03; + + const int i13 = i/(ne10 * ne11 * ne12); + const int i12 = (i - i13*ne10*ne11*ne12) / (ne10*ne11); + const int i11 = (i - i13*ne10*ne11*ne12 - i12*ne10*ne11) / ne10; + const int i10 = i - i13*ne10*ne11*ne12 - i12*ne10*ne11 - i11*ne10; + const int dst_offset = (i10/qk)*nb10 + i11*nb11 + i12*nb12 + i13*nb13; + + cpy_blck(cx + x_offset, cdst + dst_offset); +} + +static void ggml_cpy_f16_f32_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + const int num_blocks = (ne + CUDA_CPY_BLOCK_SIZE - 1) / CUDA_CPY_BLOCK_SIZE; + cpy_f32_f16<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f32_f32_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + const int num_blocks = (ne + CUDA_CPY_BLOCK_SIZE - 1) / CUDA_CPY_BLOCK_SIZE; + cpy_f32_f16<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f32_f16_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + const int num_blocks = (ne + CUDA_CPY_BLOCK_SIZE - 1) / CUDA_CPY_BLOCK_SIZE; + cpy_f32_f16<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f32_q8_0_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + GGML_ASSERT(ne % QK8_0 == 0); + const int num_blocks = ne / QK8_0; + cpy_f32_q<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f32_q4_0_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + GGML_ASSERT(ne % QK4_0 == 0); + const int num_blocks = ne / QK4_0; + cpy_f32_q<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f32_q4_1_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + GGML_ASSERT(ne % QK4_1 == 0); + const int num_blocks = ne / QK4_1; + cpy_f32_q<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f32_q5_0_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + GGML_ASSERT(ne % QK5_0 == 0); + const int num_blocks = ne / QK5_0; + cpy_f32_q<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f32_q5_1_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + GGML_ASSERT(ne % QK5_1 == 0); + const int num_blocks = ne / QK5_1; + cpy_f32_q<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f32_iq4_nl_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + GGML_ASSERT(ne % QK4_NL == 0); + const int num_blocks = ne / QK4_NL; + cpy_f32_q<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +static void ggml_cpy_f16_f16_cuda( + const char * cx, char * cdst, const int ne, + const int ne00, const int ne01, const int ne02, const int nb00, const int nb01, const int nb02, + const int nb03, const int ne10, const int ne11, const int ne12, const int nb10, const int nb11, const int nb12, const int nb13, cudaStream_t stream) { + + const int num_blocks = (ne + CUDA_CPY_BLOCK_SIZE - 1) / CUDA_CPY_BLOCK_SIZE; + cpy_f32_f16<<>> + (cx, cdst, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13); +} + +void ggml_cuda_cpy(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, ggml_tensor * src1) { + const int64_t ne = ggml_nelements(src0); + GGML_ASSERT(ne == ggml_nelements(src1)); + + GGML_ASSERT(ggml_nbytes(src0) <= INT_MAX); + GGML_ASSERT(ggml_nbytes(src1) <= INT_MAX); + + const int64_t ne00 = src0->ne[0]; + const int64_t ne01 = src0->ne[1]; + const int64_t ne02 = src0->ne[2]; + + //GGML_ASSERT(src0->ne[3] == 1); + + const int64_t nb00 = src0->nb[0]; + const int64_t nb01 = src0->nb[1]; + const int64_t nb02 = src0->nb[2]; + const int64_t nb03 = src0->nb[3]; + + const int64_t ne10 = src1->ne[0]; + const int64_t ne11 = src1->ne[1]; + const int64_t ne12 = src1->ne[2]; + + //GGML_ASSERT(src1->ne[3] == 1); + + const int64_t nb10 = src1->nb[0]; + const int64_t nb11 = src1->nb[1]; + const int64_t nb12 = src1->nb[2]; + const int64_t nb13 = src1->nb[3]; + + cudaStream_t main_stream = ctx.stream(); + + char * src0_ddc = (char *) src0->data; + char * src1_ddc = (char *) src1->data; + + if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_F32) { + ggml_cpy_f32_f32_cuda (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_F16) { + ggml_cpy_f32_f16_cuda (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q8_0) { + ggml_cpy_f32_q8_0_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q4_0) { + ggml_cpy_f32_q4_0_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q4_1) { + ggml_cpy_f32_q4_1_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q5_0) { + ggml_cpy_f32_q5_0_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_IQ4_NL) { + ggml_cpy_f32_iq4_nl_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_Q5_1) { + ggml_cpy_f32_q5_1_cuda(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F16) { + ggml_cpy_f16_f16_cuda (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F32) { + ggml_cpy_f16_f32_cuda (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream); + } else { + fprintf(stderr, "%s: unsupported type combination (%s to %s)\n", __func__, + ggml_type_name(src0->type), ggml_type_name(src1->type)); + GGML_ASSERT(false); + } +} + +void ggml_cuda_dup(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + ggml_cuda_cpy(ctx, src0, dst); +} diff --git a/ggml-cuda/cpy.cuh b/ggml-cuda/cpy.cuh new file mode 100644 index 000000000..f0b2c453b --- /dev/null +++ b/ggml-cuda/cpy.cuh @@ -0,0 +1,7 @@ +#include "common.cuh" + +#define CUDA_CPY_BLOCK_SIZE 32 + +void ggml_cuda_cpy(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, ggml_tensor * src1); + +void ggml_cuda_dup(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/dequantize.cuh b/ggml-cuda/dequantize.cuh new file mode 100644 index 000000000..b54400632 --- /dev/null +++ b/ggml-cuda/dequantize.cuh @@ -0,0 +1,103 @@ +#include "common.cuh" + +static __device__ __forceinline__ void dequantize_q4_0(const void * vx, const int ib, const int iqs, dfloat2 & v){ + const block_q4_0 * x = (const block_q4_0 *) vx; + + const dfloat d = x[ib].d; + + const int vui = x[ib].qs[iqs]; + + v.x = vui & 0xF; + v.y = vui >> 4; + +#ifdef GGML_CUDA_F16 + v = __hsub2(v, {8.0f, 8.0f}); + v = __hmul2(v, {d, d}); +#else + v.x = (v.x - 8.0f) * d; + v.y = (v.y - 8.0f) * d; +#endif // GGML_CUDA_F16 +} + +static __device__ __forceinline__ void dequantize_q4_1(const void * vx, const int ib, const int iqs, dfloat2 & v){ + const block_q4_1 * x = (const block_q4_1 *) vx; + + const dfloat d = __low2half(x[ib].dm); + const dfloat m = __high2half(x[ib].dm); + + const int vui = x[ib].qs[iqs]; + + v.x = vui & 0xF; + v.y = vui >> 4; + +#ifdef GGML_CUDA_F16 + v = __hmul2(v, {d, d}); + v = __hadd2(v, {m, m}); +#else + v.x = (v.x * d) + m; + v.y = (v.y * d) + m; +#endif // GGML_CUDA_F16 +} + +static __device__ __forceinline__ void dequantize_q5_0(const void * vx, const int ib, const int iqs, dfloat2 & v){ + const block_q5_0 * x = (const block_q5_0 *) vx; + + const dfloat d = x[ib].d; + + uint32_t qh; + memcpy(&qh, x[ib].qh, sizeof(qh)); + + const int xh_0 = ((qh >> (iqs + 0)) << 4) & 0x10; + const int xh_1 = ((qh >> (iqs + 12)) ) & 0x10; + + v.x = ((x[ib].qs[iqs] & 0xf) | xh_0); + v.y = ((x[ib].qs[iqs] >> 4) | xh_1); + +#ifdef GGML_CUDA_F16 + v = __hsub2(v, {16.0f, 16.0f}); + v = __hmul2(v, {d, d}); +#else + v.x = (v.x - 16.0f) * d; + v.y = (v.y - 16.0f) * d; +#endif // GGML_CUDA_F16 +} + +static __device__ __forceinline__ void dequantize_q5_1(const void * vx, const int ib, const int iqs, dfloat2 & v){ + const block_q5_1 * x = (const block_q5_1 *) vx; + + const dfloat d = __low2half(x[ib].dm); + const dfloat m = __high2half(x[ib].dm); + + uint32_t qh; + memcpy(&qh, x[ib].qh, sizeof(qh)); + + const int xh_0 = ((qh >> (iqs + 0)) << 4) & 0x10; + const int xh_1 = ((qh >> (iqs + 12)) ) & 0x10; + + v.x = ((x[ib].qs[iqs] & 0xf) | xh_0); + v.y = ((x[ib].qs[iqs] >> 4) | xh_1); + +#ifdef GGML_CUDA_F16 + v = __hmul2(v, {d, d}); + v = __hadd2(v, {m, m}); +#else + v.x = (v.x * d) + m; + v.y = (v.y * d) + m; +#endif // GGML_CUDA_F16 +} + +static __device__ __forceinline__ void dequantize_q8_0(const void * vx, const int ib, const int iqs, dfloat2 & v){ + const block_q8_0 * x = (const block_q8_0 *) vx; + + const dfloat d = x[ib].d; + + v.x = x[ib].qs[iqs + 0]; + v.y = x[ib].qs[iqs + 1]; + +#ifdef GGML_CUDA_F16 + v = __hmul2(v, {d, d}); +#else + v.x *= d; + v.y *= d; +#endif // GGML_CUDA_F16 +} diff --git a/ggml-cuda/diagmask.cu b/ggml-cuda/diagmask.cu new file mode 100644 index 000000000..4b713ba22 --- /dev/null +++ b/ggml-cuda/diagmask.cu @@ -0,0 +1,40 @@ +#include "diagmask.cuh" + +static __global__ void diag_mask_inf_f32(const float * x, float * dst, const int ncols, const int rows_per_channel, const int n_past) { + const int col = blockDim.y*blockIdx.y + threadIdx.y; + const int row = blockDim.x*blockIdx.x + threadIdx.x; + + if (col >= ncols) { + return; + } + + const int i = row*ncols + col; + //dst[i] = col > (n_past + row % rows_per_channel) ? -INFINITY : x[i]; + //dst[i] = x[i] - (col > n_past + row % rows_per_channel) * INT_MAX; // equivalent within rounding error but slightly faster on GPU + dst[i] = x[i] - (col > n_past + row % rows_per_channel) * FLT_MAX; +} + +static void diag_mask_inf_f32_cuda(const float * x, float * dst, const int ncols_x, const int nrows_x, const int rows_per_channel, const int n_past, cudaStream_t stream) { + const dim3 block_dims(1, CUDA_DIAG_MASK_INF_BLOCK_SIZE, 1); + const int block_num_x = (ncols_x + CUDA_DIAG_MASK_INF_BLOCK_SIZE - 1) / CUDA_DIAG_MASK_INF_BLOCK_SIZE; + const dim3 block_nums(nrows_x, block_num_x, 1); + diag_mask_inf_f32<<>>(x, dst, ncols_x, rows_per_channel, n_past); +} + +void ggml_cuda_op_diag_mask_inf(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + const int64_t ne00 = src0->ne[0]; + const int64_t ne01 = src0->ne[1]; + const int nrows0 = ggml_nrows(src0); + + const int n_past = ((int32_t *) dst->op_params)[0]; + + diag_mask_inf_f32_cuda(src0_d, dst_d, ne00, nrows0, ne01, n_past, stream); +} diff --git a/ggml-cuda/diagmask.cuh b/ggml-cuda/diagmask.cuh new file mode 100644 index 000000000..6cdbef17e --- /dev/null +++ b/ggml-cuda/diagmask.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_DIAG_MASK_INF_BLOCK_SIZE 32 + +void ggml_cuda_op_diag_mask_inf(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/dmmv.cu b/ggml-cuda/dmmv.cu new file mode 100644 index 000000000..f91732df5 --- /dev/null +++ b/ggml-cuda/dmmv.cu @@ -0,0 +1,820 @@ +#include "dmmv.cuh" +#include "dequantize.cuh" + +// dmmv = dequantize_mul_mat_vec +#ifndef GGML_CUDA_DMMV_X +#define GGML_CUDA_DMMV_X 32 +#endif +#ifndef GGML_CUDA_MMV_Y +#define GGML_CUDA_MMV_Y 1 +#endif + +#ifndef K_QUANTS_PER_ITERATION +#define K_QUANTS_PER_ITERATION 2 +#else +static_assert(K_QUANTS_PER_ITERATION == 1 || K_QUANTS_PER_ITERATION == 2, "K_QUANTS_PER_ITERATION must be 1 or 2"); +#endif + +static __global__ void dequantize_mul_mat_vec_q2_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols, int nrows) { + + static_assert(16%K_QUANTS_PER_ITERATION == 0, "16 must be divisible by K_QUANTS_PER_ITERATION"); + + const int row = blockIdx.x*blockDim.y + threadIdx.y; + if (row > nrows) return; + + const int num_blocks_per_row = ncols / QK_K; + const int ib0 = row*num_blocks_per_row; + + const block_q2_K * x = (const block_q2_K *)vx + ib0; + + float tmp = 0; // partial sum for thread in warp + +#if QK_K == 256 + const int tid = threadIdx.x/K_QUANTS_PER_ITERATION; // 0...31 or 0...15 + const int ix = threadIdx.x%K_QUANTS_PER_ITERATION; // 0 or 0,1 + + const int step = 16/K_QUANTS_PER_ITERATION; + + const int im = tid/step; // 0 or 1. 0 computes 0..., 1 computes 128... + const int in = tid - step*im; // 0...15 or 0...7 + + const int l0 = K_QUANTS_PER_ITERATION*in; // 0...15 or 0...14 in steps of 2 + const int q_offset = 32*im + l0; + const int s_offset = 8*im; + const int y_offset = 128*im + l0; + + uint32_t aux[4]; + const uint8_t * d = (const uint8_t *)aux; + const uint8_t * m = (const uint8_t *)(aux + 2); + + for (int i = ix; i < num_blocks_per_row; i += K_QUANTS_PER_ITERATION) { + + const float * y = yy + i * QK_K + y_offset; + const uint8_t * q = x[i].qs + q_offset; + + const float dall = __low2half(x[i].dm); + const float dmin = __high2half(x[i].dm); + + const uint32_t * a = (const uint32_t *)(x[i].scales + s_offset); + aux[0] = a[0] & 0x0f0f0f0f; + aux[1] = a[1] & 0x0f0f0f0f; + aux[2] = (a[0] >> 4) & 0x0f0f0f0f; + aux[3] = (a[1] >> 4) & 0x0f0f0f0f; + + float sum1 = 0, sum2 = 0; + for (int l = 0; l < K_QUANTS_PER_ITERATION; ++l) { + sum1 += y[l+ 0] * d[0] * ((q[l+ 0] >> 0) & 3) + + y[l+32] * d[2] * ((q[l+ 0] >> 2) & 3) + + y[l+64] * d[4] * ((q[l+ 0] >> 4) & 3) + + y[l+96] * d[6] * ((q[l+ 0] >> 6) & 3) + + y[l+16] * d[1] * ((q[l+16] >> 0) & 3) + + y[l+48] * d[3] * ((q[l+16] >> 2) & 3) + + y[l+80] * d[5] * ((q[l+16] >> 4) & 3) + +y[l+112] * d[7] * ((q[l+16] >> 6) & 3); + sum2 += y[l+ 0] * m[0] + y[l+32] * m[2] + y[l+64] * m[4] + y[ l+96] * m[6] + + y[l+16] * m[1] + y[l+48] * m[3] + y[l+80] * m[5] + y[l+112] * m[7]; + + } + tmp += dall * sum1 - dmin * sum2; + + } +#else + const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...15 or 0...7 + const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); // 0....1 or 0...3 + const int offset = tid * K_QUANTS_PER_ITERATION; + + uint32_t uaux[2]; + const uint8_t * d = (const uint8_t *)uaux; + + for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { + + const float * y = yy + i * QK_K + offset; + const uint8_t * q = x[i].qs + offset; + const uint32_t * s = (const uint32_t *)x[i].scales; + + uaux[0] = s[0] & 0x0f0f0f0f; + uaux[1] = (s[0] >> 4) & 0x0f0f0f0f; + + const float2 dall = __half22float2(x[i].dm); + + float sum1 = 0, sum2 = 0; + for (int l = 0; l < K_QUANTS_PER_ITERATION; ++l) { + const uint8_t ql = q[l]; + sum1 += y[l+ 0] * d[0] * ((ql >> 0) & 3) + + y[l+16] * d[1] * ((ql >> 2) & 3) + + y[l+32] * d[2] * ((ql >> 4) & 3) + + y[l+48] * d[3] * ((ql >> 6) & 3); + sum2 += y[l+0] * d[4] + y[l+16] * d[5] + y[l+32] * d[6] + y[l+48] * d[7]; + } + tmp += dall.x * sum1 - dall.y * sum2; + } +#endif + + // sum up partial sums and write back result + tmp = warp_reduce_sum(tmp); + + if (threadIdx.x == 0) { + dst[row] = tmp; + } +} + +static __global__ void dequantize_mul_mat_vec_q3_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols, int nrows) { + + const int row = blockIdx.x*blockDim.y + threadIdx.y; + if (row > nrows) return; + + const int num_blocks_per_row = ncols / QK_K; + const int ib0 = row*num_blocks_per_row; + + const block_q3_K * x = (const block_q3_K *)vx + ib0; + + float tmp = 0; // partial sum for thread in warp + +#if QK_K == 256 + + const uint16_t kmask1 = 0x0303; + const uint16_t kmask2 = 0x0f0f; + + const int tid = threadIdx.x/K_QUANTS_PER_ITERATION; // 0...31 or 0...16 + const int ix = threadIdx.x%K_QUANTS_PER_ITERATION; // 0 or 0,1 + + const int n = K_QUANTS_PER_ITERATION; // iterations in the inner loop + const int step = 16/K_QUANTS_PER_ITERATION; + const int im = tid/step; // 0 or 1. 0 computes 0..., 1 computes 128... + const int in = tid - step*im; // 0....15 or 0...7 + + const uint8_t m = 1 << (4*im); + + const int l0 = n*in; // 0...15 or 0...14 in steps of 2 + const int q_offset = 32*im + l0; + const int y_offset = 128*im + l0; + + uint16_t utmp[4]; + const int8_t * s = (const int8_t *)utmp; + + const uint16_t s_shift = 4*im; + + for (int i = ix; i < num_blocks_per_row; i += K_QUANTS_PER_ITERATION) { + + const float * y = yy + i * QK_K + y_offset; + const uint8_t * q = x[i].qs + q_offset; + const uint8_t * h = x[i].hmask + l0; + + const uint16_t * a = (const uint16_t *)x[i].scales; + utmp[0] = ((a[0] >> s_shift) & kmask2) | (((a[4] >> (s_shift + 0)) & kmask1) << 4); + utmp[1] = ((a[1] >> s_shift) & kmask2) | (((a[5] >> (s_shift + 0)) & kmask1) << 4); + utmp[2] = ((a[2] >> s_shift) & kmask2) | (((a[4] >> (s_shift + 2)) & kmask1) << 4); + utmp[3] = ((a[3] >> s_shift) & kmask2) | (((a[5] >> (s_shift + 2)) & kmask1) << 4); + + const float d = x[i].d; + + float sum = 0; + for (int l = 0; l < n; ++l) { + sum += y[l+ 0] * (s[0] - 32) * (((q[l] >> 0) & 3) - (h[l] & (m << 0) ? 0 : 4)) + + y[l+32] * (s[2] - 32) * (((q[l] >> 2) & 3) - (h[l] & (m << 1) ? 0 : 4)) + + y[l+64] * (s[4] - 32) * (((q[l] >> 4) & 3) - (h[l] & (m << 2) ? 0 : 4)) + + y[l+96] * (s[6] - 32) * (((q[l] >> 6) & 3) - (h[l] & (m << 3) ? 0 : 4)); + sum += y[l+16] * (s[1] - 32) * (((q[l+16] >> 0) & 3) - (h[l+16] & (m << 0) ? 0 : 4)) + + y[l+48] * (s[3] - 32) * (((q[l+16] >> 2) & 3) - (h[l+16] & (m << 1) ? 0 : 4)) + + y[l+80] * (s[5] - 32) * (((q[l+16] >> 4) & 3) - (h[l+16] & (m << 2) ? 0 : 4)) + + y[l+112] * (s[7] - 32) * (((q[l+16] >> 6) & 3) - (h[l+16] & (m << 3) ? 0 : 4)); + } + tmp += d * sum; + + } +#else + + const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...15 or 0...7 + const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); // 0....1 or 0...3 + const int offset = tid * K_QUANTS_PER_ITERATION; // 0...15 or 0...14 + const int in = offset/8; // 0 or 1 + const int im = offset%8; // 0...7 + + for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { + + const float * y = yy + i * QK_K + offset; + const uint8_t * q = x[i].qs + offset; + const uint8_t * s = x[i].scales; + + const float dall = (float)x[i].d; + + float sum = 0; + for (int l = 0; l < K_QUANTS_PER_ITERATION; ++l) { + const uint8_t hl = x[i].hmask[im+l] >> in; + const uint8_t ql = q[l]; + sum += y[l+ 0] * dall * ((s[0] & 0xF) - 8) * ((int8_t)((ql >> 0) & 3) - ((hl >> 0) & 1 ? 0 : 4)) + + y[l+16] * dall * ((s[0] >> 4) - 8) * ((int8_t)((ql >> 2) & 3) - ((hl >> 2) & 1 ? 0 : 4)) + + y[l+32] * dall * ((s[1] & 0xF) - 8) * ((int8_t)((ql >> 4) & 3) - ((hl >> 4) & 1 ? 0 : 4)) + + y[l+48] * dall * ((s[1] >> 4) - 8) * ((int8_t)((ql >> 6) & 3) - ((hl >> 6) & 1 ? 0 : 4)); + } + tmp += sum; + } +#endif + + // sum up partial sums and write back result + tmp = warp_reduce_sum(tmp); + + if (threadIdx.x == 0) { + dst[row] = tmp; + } +} + +static __global__ void dequantize_mul_mat_vec_q4_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols, int nrows) { + + const int row = blockIdx.x*blockDim.y + threadIdx.y; + if (row > nrows) return; + const int num_blocks_per_row = ncols / QK_K; + const int ib0 = row*num_blocks_per_row; + + const block_q4_K * x = (const block_q4_K *)vx + ib0; + +#if QK_K == 256 + const uint16_t kmask1 = 0x3f3f; + const uint16_t kmask2 = 0x0f0f; + const uint16_t kmask3 = 0xc0c0; + + const int tid = threadIdx.x/K_QUANTS_PER_ITERATION; // 0...31 or 0...16 + const int ix = threadIdx.x%K_QUANTS_PER_ITERATION; // 0 or 0,1 + + const int step = 8/K_QUANTS_PER_ITERATION; // 8 or 4 + + const int il = tid/step; // 0...3 + const int ir = tid - step*il; // 0...7 or 0...3 + const int n = 2 * K_QUANTS_PER_ITERATION; // 2 or 4 + + const int im = il/2; // 0 or 1. 0 computes 0,32 + 128,160, 1 computes 64,96 + 192,224 + const int in = il%2; + + const int l0 = n*(2*ir + in); + const int q_offset = 32*im + l0; + const int y_offset = 64*im + l0; + + uint16_t aux[4]; + const uint8_t * sc = (const uint8_t *)aux; + +#if K_QUANTS_PER_ITERATION == 2 + uint32_t q32[4]; + const uint8_t * q4 = (const uint8_t *)q32; +#else + uint16_t q16[4]; + const uint8_t * q4 = (const uint8_t *)q16; +#endif + + float tmp = 0; // partial sum for thread in warp + + for (int i = ix; i < num_blocks_per_row; i += K_QUANTS_PER_ITERATION) { + + const float * y1 = yy + i*QK_K + y_offset; + const float * y2 = y1 + 128; + + const float dall = __low2half(x[i].dm); + const float dmin = __high2half(x[i].dm); + + const uint16_t * a = (const uint16_t *)x[i].scales; + aux[0] = a[im+0] & kmask1; + aux[1] = a[im+2] & kmask1; + aux[2] = ((a[im+4] >> 0) & kmask2) | ((a[im+0] & kmask3) >> 2); + aux[3] = ((a[im+4] >> 4) & kmask2) | ((a[im+2] & kmask3) >> 2); + +#if K_QUANTS_PER_ITERATION == 2 + const uint32_t * q1 = (const uint32_t *)(x[i].qs + q_offset); + const uint32_t * q2 = q1 + 16; + + q32[0] = q1[0] & 0x0f0f0f0f; + q32[1] = q1[0] & 0xf0f0f0f0; + q32[2] = q2[0] & 0x0f0f0f0f; + q32[3] = q2[0] & 0xf0f0f0f0; + + float4 s = {0.f, 0.f, 0.f, 0.f}; + float smin = 0; + for (int l = 0; l < 4; ++l) { + s.x += y1[l] * q4[l+0]; s.y += y1[l+32] * q4[l+ 4]; + s.z += y2[l] * q4[l+8]; s.w += y2[l+32] * q4[l+12]; + smin += y1[l] * sc[2] + y1[l+32] * sc[3] + y2[l] * sc[6] + y2[l+32] * sc[7]; + } + tmp += dall * (s.x * sc[0] + s.y * sc[1] * 1.f/16.f + s.z * sc[4] + s.w * sc[5] * 1.f/16.f) - dmin * smin; +#else + const uint16_t * q1 = (const uint16_t *)(x[i].qs + q_offset); + const uint16_t * q2 = q1 + 32; + + q16[0] = q1[0] & 0x0f0f; + q16[1] = q1[0] & 0xf0f0; + q16[2] = q2[0] & 0x0f0f; + q16[3] = q2[0] & 0xf0f0; + + float4 s = {0.f, 0.f, 0.f, 0.f}; + float smin = 0; + for (int l = 0; l < 2; ++l) { + s.x += y1[l] * q4[l+0]; s.y += y1[l+32] * q4[l+2]; + s.z += y2[l] * q4[l+4]; s.w += y2[l+32] * q4[l+6]; + smin += y1[l] * sc[2] + y1[l+32] * sc[3] + y2[l] * sc[6] + y2[l+32] * sc[7]; + } + tmp += dall * (s.x * sc[0] + s.y * sc[1] * 1.f/16.f + s.z * sc[4] + s.w * sc[5] * 1.f/16.f) - dmin * smin; +#endif + + } +#else + const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...15 + const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); + + const int step = tid * K_QUANTS_PER_ITERATION; + + uint16_t aux16[2]; + const uint8_t * s = (const uint8_t *)aux16; + + float tmp = 0; + + for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { + const uint8_t * q = x[i].qs + step; + const float * y = yy + i*QK_K + step; + const uint16_t * a = (const uint16_t *)x[i].scales; + aux16[0] = a[0] & 0x0f0f; + aux16[1] = (a[0] >> 4) & 0x0f0f; + const float d = (float)x[i].dm[0]; + const float m = (float)x[i].dm[1]; + float sum = 0.f; + for (int j = 0; j < K_QUANTS_PER_ITERATION; ++j) { + sum += y[j+ 0] * (d * s[0] * (q[j+ 0] & 0xF) - m * s[2]) + + y[j+16] * (d * s[0] * (q[j+16] & 0xF) - m * s[2]) + + y[j+32] * (d * s[1] * (q[j+ 0] >> 4) - m * s[3]) + + y[j+48] * (d * s[1] * (q[j+16] >> 4) - m * s[3]); + } + tmp += sum; + } + +#endif + + // sum up partial sums and write back result + tmp = warp_reduce_sum(tmp); + + if (tid == 0) { + dst[row] = tmp; + } +} + +static __global__ void dequantize_mul_mat_vec_q5_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols) { + + const int row = blockIdx.x; + const int num_blocks_per_row = ncols / QK_K; + const int ib0 = row*num_blocks_per_row; + + const block_q5_K * x = (const block_q5_K *)vx + ib0; + + float tmp = 0; // partial sum for thread in warp + +#if QK_K == 256 + const uint16_t kmask1 = 0x3f3f; + const uint16_t kmask2 = 0x0f0f; + const uint16_t kmask3 = 0xc0c0; + + const int tid = threadIdx.x/2; // 0...15 + const int ix = threadIdx.x%2; + + const int il = tid/4; // 0...3 + const int ir = tid - 4*il;// 0...3 + const int n = 2; + + const int im = il/2; // 0 or 1. 0 computes 0,32 + 128,160, 1 computes 64,96 + 192,224 + const int in = il%2; + + const int l0 = n*(2*ir + in); + const int q_offset = 32*im + l0; + const int y_offset = 64*im + l0; + + const uint8_t hm1 = 1 << (2*im); + const uint8_t hm2 = hm1 << 4; + + uint16_t aux[4]; + const uint8_t * sc = (const uint8_t *)aux; + + uint16_t q16[8]; + const uint8_t * q4 = (const uint8_t *)q16; + + for (int i = ix; i < num_blocks_per_row; i += 2) { + + const uint8_t * ql1 = x[i].qs + q_offset; + const uint8_t * qh = x[i].qh + l0; + const float * y1 = yy + i*QK_K + y_offset; + const float * y2 = y1 + 128; + + const float dall = __low2half(x[i].dm); + const float dmin = __high2half(x[i].dm); + + const uint16_t * a = (const uint16_t *)x[i].scales; + aux[0] = a[im+0] & kmask1; + aux[1] = a[im+2] & kmask1; + aux[2] = ((a[im+4] >> 0) & kmask2) | ((a[im+0] & kmask3) >> 2); + aux[3] = ((a[im+4] >> 4) & kmask2) | ((a[im+2] & kmask3) >> 2); + + float4 sum = {0.f, 0.f, 0.f, 0.f}; + float smin = 0; + const uint16_t * q1 = (const uint16_t *)ql1; + const uint16_t * q2 = q1 + 32; + q16[0] = q1[0] & 0x0f0f; + q16[1] = q1[8] & 0x0f0f; + q16[2] = (q1[0] >> 4) & 0x0f0f; + q16[3] = (q1[8] >> 4) & 0x0f0f; + q16[4] = q2[0] & 0x0f0f; + q16[5] = q2[8] & 0x0f0f; + q16[6] = (q2[0] >> 4) & 0x0f0f; + q16[7] = (q2[8] >> 4) & 0x0f0f; + for (int l = 0; l < n; ++l) { + sum.x += y1[l+ 0] * (q4[l +0] + (qh[l+ 0] & (hm1 << 0) ? 16 : 0)) + + y1[l+16] * (q4[l +2] + (qh[l+16] & (hm1 << 0) ? 16 : 0)); + sum.y += y1[l+32] * (q4[l +4] + (qh[l+ 0] & (hm1 << 1) ? 16 : 0)) + + y1[l+48] * (q4[l +6] + (qh[l+16] & (hm1 << 1) ? 16 : 0)); + sum.z += y2[l+ 0] * (q4[l +8] + (qh[l+ 0] & (hm2 << 0) ? 16 : 0)) + + y2[l+16] * (q4[l+10] + (qh[l+16] & (hm2 << 0) ? 16 : 0)); + sum.w += y2[l+32] * (q4[l+12] + (qh[l+ 0] & (hm2 << 1) ? 16 : 0)) + + y2[l+48] * (q4[l+14] + (qh[l+16] & (hm2 << 1) ? 16 : 0)); + smin += (y1[l] + y1[l+16]) * sc[2] + (y1[l+32] + y1[l+48]) * sc[3] + + (y2[l] + y2[l+16]) * sc[6] + (y2[l+32] + y2[l+48]) * sc[7]; + } + tmp += dall * (sum.x * sc[0] + sum.y * sc[1] + sum.z * sc[4] + sum.w * sc[5]) - dmin * smin; + } + +#else + const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...15 + const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); + const int step = tid * K_QUANTS_PER_ITERATION; + const int im = step/8; + const int in = step%8; + + for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { + const uint8_t * q = x[i].qs + step; + const int8_t * s = x[i].scales; + const float * y = yy + i*QK_K + step; + const float d = x[i].d; + float sum = 0.f; + for (int j = 0; j < K_QUANTS_PER_ITERATION; ++j) { + const uint8_t h = x[i].qh[in+j] >> im; + sum += y[j+ 0] * d * s[0] * ((q[j+ 0] & 0xF) - ((h >> 0) & 1 ? 0 : 16)) + + y[j+16] * d * s[1] * ((q[j+16] & 0xF) - ((h >> 2) & 1 ? 0 : 16)) + + y[j+32] * d * s[2] * ((q[j+ 0] >> 4) - ((h >> 4) & 1 ? 0 : 16)) + + y[j+48] * d * s[3] * ((q[j+16] >> 4) - ((h >> 6) & 1 ? 0 : 16)); + } + tmp += sum; + } +#endif + + // sum up partial sums and write back result + tmp = warp_reduce_sum(tmp); + + if (threadIdx.x == 0) { + dst[row] = tmp; + } +} + +static __global__ void dequantize_mul_mat_vec_q6_k(const void * __restrict__ vx, const float * __restrict__ yy, float * __restrict__ dst, const int ncols, int nrows) { + + static_assert(16%K_QUANTS_PER_ITERATION == 0, "16 must be divisible by K_QUANTS_PER_ITERATION"); + + const int row = blockIdx.x*blockDim.y + threadIdx.y; + if (row > nrows) return; + + const int num_blocks_per_row = ncols / QK_K; + const int ib0 = row*num_blocks_per_row; + + const block_q6_K * x = (const block_q6_K *)vx + ib0; + +#if QK_K == 256 + + const int tid = threadIdx.x/K_QUANTS_PER_ITERATION; // 0...31 or 0...16 + const int ix = threadIdx.x%K_QUANTS_PER_ITERATION; // 0 or 0, 1 + + const int step = 16/K_QUANTS_PER_ITERATION; // 16 or 8 + + const int im = tid/step; // 0 or 1. 0 computes 0..., 1 computes 128... + const int in = tid - step*im; // 0...15 or 0...7 + +#if K_QUANTS_PER_ITERATION == 1 + const int l0 = K_QUANTS_PER_ITERATION*in; // 0...15 + const int is = 0; +#else + const int l0 = 4 * in; // 0, 4, 8, ..., 28 + const int is = in / 4; +#endif + const int ql_offset = 64*im + l0; + const int qh_offset = 32*im + l0; + const int s_offset = 8*im + is; + const int y_offset = 128*im + l0; + + float tmp = 0; // partial sum for thread in warp + + for (int i = ix; i < num_blocks_per_row; i += K_QUANTS_PER_ITERATION) { + + const float * y = yy + i * QK_K + y_offset; + const uint8_t * ql = x[i].ql + ql_offset; + const uint8_t * qh = x[i].qh + qh_offset; + const int8_t * s = x[i].scales + s_offset; + + const float d = x[i].d; + +#if K_QUANTS_PER_ITERATION == 1 + float sum = y[ 0] * s[0] * d * ((int8_t)((ql[ 0] & 0xF) | ((qh[ 0] & 0x03) << 4)) - 32) + + y[16] * s[1] * d * ((int8_t)((ql[16] & 0xF) | ((qh[16] & 0x03) << 4)) - 32) + + y[32] * s[2] * d * ((int8_t)((ql[32] & 0xF) | ((qh[ 0] & 0x0c) << 2)) - 32) + + y[48] * s[3] * d * ((int8_t)((ql[48] & 0xF) | ((qh[16] & 0x0c) << 2)) - 32) + + y[64] * s[4] * d * ((int8_t)((ql[ 0] >> 4) | ((qh[ 0] & 0x30) >> 0)) - 32) + + y[80] * s[5] * d * ((int8_t)((ql[16] >> 4) | ((qh[16] & 0x30) >> 0)) - 32) + + y[96] * s[6] * d * ((int8_t)((ql[32] >> 4) | ((qh[ 0] & 0xc0) >> 2)) - 32) + +y[112] * s[7] * d * ((int8_t)((ql[48] >> 4) | ((qh[16] & 0xc0) >> 2)) - 32); + tmp += sum; +#else + float sum = 0; + for (int l = 0; l < 4; ++l) { + sum += y[l+ 0] * s[0] * d * ((int8_t)((ql[l+ 0] & 0xF) | (((qh[l] >> 0) & 3) << 4)) - 32) + + y[l+32] * s[2] * d * ((int8_t)((ql[l+32] & 0xF) | (((qh[l] >> 2) & 3) << 4)) - 32) + + y[l+64] * s[4] * d * ((int8_t)((ql[l+ 0] >> 4) | (((qh[l] >> 4) & 3) << 4)) - 32) + + y[l+96] * s[6] * d * ((int8_t)((ql[l+32] >> 4) | (((qh[l] >> 6) & 3) << 4)) - 32); + } + tmp += sum; +#endif + + } + +#else + + const int tid = threadIdx.x/(2*K_QUANTS_PER_ITERATION); // 0...7 + const int ix = threadIdx.x%(2*K_QUANTS_PER_ITERATION); // 0...3 + + const int step = tid * K_QUANTS_PER_ITERATION; + + float tmp = 0; // partial sum for thread in warp + + for (int i = ix; i < num_blocks_per_row; i += 2*K_QUANTS_PER_ITERATION) { + + const float * y = yy + i * QK_K + step; + const uint8_t * ql = x[i].ql + step; + const uint8_t * qh = x[i].qh + step; + const int8_t * s = x[i].scales; + + const float d = x[i+0].d; + + float sum = 0; + for (int j = 0; j < K_QUANTS_PER_ITERATION; ++j) { + sum += y[j+ 0] * s[0] * d * ((int8_t)((ql[j+ 0] & 0xF) | ((qh[j] & 0x03) << 4)) - 32) + + y[j+16] * s[1] * d * ((int8_t)((ql[j+16] & 0xF) | ((qh[j] & 0x0c) << 2)) - 32) + + y[j+32] * s[2] * d * ((int8_t)((ql[j+ 0] >> 4) | ((qh[j] & 0x30) >> 0)) - 32) + + y[j+48] * s[3] * d * ((int8_t)((ql[j+16] >> 4) | ((qh[j] & 0xc0) >> 2)) - 32); + } + tmp += sum; + + } + +#endif + + // sum up partial sums and write back result + tmp = warp_reduce_sum(tmp); + + if (tid == 0) { + dst[row] = tmp; + } +} + +static __device__ void convert_f16(const void * vx, const int ib, const int iqs, dfloat2 & v){ + const half * x = (const half *) vx; + + // automatic half -> float type cast if dfloat == float + v.x = x[ib + iqs + 0]; + v.y = x[ib + iqs + 1]; +} + +template +static __global__ void dequantize_mul_mat_vec(const void * __restrict__ vx, const dfloat * __restrict__ y, float * __restrict__ dst, const int ncols, const int nrows) { + // qk = quantized weights per x block + // qr = number of quantized weights per data value in x block + const int row = blockIdx.x*blockDim.y + threadIdx.y; + + if (row >= nrows) { + return; + } + + const int tid = threadIdx.x; + + const int iter_stride = 2*GGML_CUDA_DMMV_X; + const int vals_per_iter = iter_stride / WARP_SIZE; // num quantized vals per thread and i iter + const int y_offset = qr == 1 ? 1 : qk/2; + +// partial sum for each thread +#ifdef GGML_CUDA_F16 + half2 tmp = {0.0f, 0.0f}; // two sums for f16 to take advantage of half2 intrinsics +#else + float tmp = 0.0f; +#endif // GGML_CUDA_F16 + + for (int i = 0; i < ncols; i += iter_stride) { + const int col = i + vals_per_iter*tid; + const int ib = (row*ncols + col)/qk; // x block index + const int iqs = (col%qk)/qr; // x quant index + const int iybs = col - col%qk; // y block start index + +// processing >2 values per i iter is faster for fast GPUs +#pragma unroll + for (int j = 0; j < vals_per_iter; j += 2) { + // process 2 vals per j iter + + // dequantize + // for qr = 2 the iqs needs to increase by 1 per j iter because 2 weights per data val + dfloat2 v; + dequantize_kernel(vx, ib, iqs + j/qr, v); + + // matrix multiplication + // for qr = 2 the y index needs to increase by 1 per j iter because of y_offset = qk/2 +#ifdef GGML_CUDA_F16 + tmp += __hmul2(v, { + y[iybs + iqs + j/qr + 0], + y[iybs + iqs + j/qr + y_offset] + }); +#else + tmp += v.x * y[iybs + iqs + j/qr + 0]; + tmp += v.y * y[iybs + iqs + j/qr + y_offset]; +#endif // GGML_CUDA_F16 + } + } + + // sum up partial sums and write back result + tmp = warp_reduce_sum(tmp); + + if (tid == 0) { +#ifdef GGML_CUDA_F16 + dst[row] = tmp.x + tmp.y; +#else + dst[row] = tmp; +#endif // GGML_CUDA_F16 + } +} + +static void dequantize_mul_mat_vec_q4_0_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); + const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; + // the number of rows may exceed maximum grid size in the y or z dimensions, use the x dimension instead + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); + dequantize_mul_mat_vec + <<>>(vx, y, dst, ncols, nrows); +} + +static void dequantize_mul_mat_vec_q4_1_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); + const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); + dequantize_mul_mat_vec + <<>>(vx, y, dst, ncols, nrows); +} + +static void dequantize_mul_mat_vec_q5_0_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); + const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); + dequantize_mul_mat_vec + <<>>(vx, y, dst, ncols, nrows); +} + +static void dequantize_mul_mat_vec_q5_1_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); + const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); + dequantize_mul_mat_vec + <<>>(vx, y, dst, ncols, nrows); +} + +static void dequantize_mul_mat_vec_q8_0_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); + const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); + dequantize_mul_mat_vec + <<>>(vx, y, dst, ncols, nrows); +} + +static void dequantize_mul_mat_vec_q2_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % QK_K == 0); + const int ny = 2; // very slightly faster than 1 even when K_QUANTS_PER_ITERATION = 2 + const int block_num_y = (nrows + ny - 1) / ny; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(32, ny, 1); + dequantize_mul_mat_vec_q2_k<<>>(vx, y, dst, ncols, nrows); +} + +static void dequantize_mul_mat_vec_q3_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % QK_K == 0); + const int ny = 2 / K_QUANTS_PER_ITERATION; + const int block_num_y = (nrows + ny - 1) / ny; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(32, ny, 1); + dequantize_mul_mat_vec_q3_k<<>>(vx, y, dst, ncols, nrows); +} + +static void dequantize_mul_mat_vec_q4_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % QK_K == 0); + const int ny = 2 / K_QUANTS_PER_ITERATION; + const int block_num_y = (nrows + ny - 1) / ny; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(32, ny, 1); + dequantize_mul_mat_vec_q4_k<<>>(vx, y, dst, ncols, nrows); +} + +static void dequantize_mul_mat_vec_q5_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % QK_K == 0); + const dim3 block_dims(32, 1, 1); + dequantize_mul_mat_vec_q5_k<<>>(vx, y, dst, ncols); +} + +static void dequantize_mul_mat_vec_q6_K_cuda(const void * vx, const float * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % QK_K == 0); + const int ny = 2 / K_QUANTS_PER_ITERATION; + const int block_num_y = (nrows + ny - 1) / ny; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(32, ny, 1); + dequantize_mul_mat_vec_q6_k<<>>(vx, y, dst, ncols, nrows); +} + +static void convert_mul_mat_vec_f16_cuda(const void * vx, const dfloat * y, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + GGML_ASSERT(ncols % GGML_CUDA_DMMV_X == 0); + const int block_num_y = (nrows + GGML_CUDA_MMV_Y - 1) / GGML_CUDA_MMV_Y; + const dim3 block_nums(block_num_y, 1, 1); + const dim3 block_dims(WARP_SIZE, GGML_CUDA_MMV_Y, 1); + dequantize_mul_mat_vec<1, 1, convert_f16> + <<>>(vx, y, dst, ncols, nrows); +} + +void ggml_cuda_op_dequantize_mul_mat_vec( + ggml_backend_cuda_context & ctx, + const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, + const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, + const int64_t src1_padded_row_size, cudaStream_t stream) { + GGML_UNUSED(ctx); + const int64_t ne00 = src0->ne[0]; + const int64_t row_diff = row_high - row_low; + + GGML_ASSERT(src1->type == GGML_TYPE_F32); + + // on some GPUs it is faster to convert src1 to half and to use half precision intrinsics +#ifdef GGML_CUDA_F16 + ggml_cuda_pool_alloc src1_dfloat_a(ctx.pool()); + half * src1_dfloat = nullptr; // dfloat == half + + bool src1_convert_f16 = + src0->type == GGML_TYPE_Q4_0 || src0->type == GGML_TYPE_Q4_1 || + src0->type == GGML_TYPE_Q5_0 || src0->type == GGML_TYPE_Q5_1 || + src0->type == GGML_TYPE_Q8_0 || src0->type == GGML_TYPE_F16; + + if (src1_convert_f16) { + src1_dfloat = src1_dfloat_a.alloc(ne00); + const to_fp16_cuda_t to_fp16_cuda = ggml_get_to_fp16_cuda(src1->type); + GGML_ASSERT(to_fp16_cuda != nullptr); + to_fp16_cuda(src1_ddf_i, src1_dfloat, ne00, stream); + } +#else + const dfloat * src1_dfloat = (const dfloat *) src1_ddf_i; // dfloat == float, no conversion +#endif // GGML_CUDA_F16 + + switch (src0->type) { + case GGML_TYPE_Q4_0: + dequantize_mul_mat_vec_q4_0_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q4_1: + dequantize_mul_mat_vec_q4_1_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q5_0: + dequantize_mul_mat_vec_q5_0_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q5_1: + dequantize_mul_mat_vec_q5_1_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q8_0: + dequantize_mul_mat_vec_q8_0_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q2_K: + dequantize_mul_mat_vec_q2_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q3_K: + dequantize_mul_mat_vec_q3_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q4_K: + dequantize_mul_mat_vec_q4_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q5_K: + dequantize_mul_mat_vec_q5_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_Q6_K: + dequantize_mul_mat_vec_q6_K_cuda(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream); + break; + case GGML_TYPE_F16: + convert_mul_mat_vec_f16_cuda(src0_dd_i, src1_dfloat, dst_dd_i, ne00, row_diff, stream); + break; + default: + GGML_ASSERT(false); + break; + } + + GGML_UNUSED(src1); + GGML_UNUSED(dst); + GGML_UNUSED(src1_ddq_i); + GGML_UNUSED(src1_ncols); + GGML_UNUSED(src1_padded_row_size); +} diff --git a/ggml-cuda/dmmv.cuh b/ggml-cuda/dmmv.cuh new file mode 100644 index 000000000..3802678ff --- /dev/null +++ b/ggml-cuda/dmmv.cuh @@ -0,0 +1,7 @@ +#include "common.cuh" + +void ggml_cuda_op_dequantize_mul_mat_vec( + ggml_backend_cuda_context & ctx, + const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, + const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, + const int64_t src1_padded_row_size, cudaStream_t stream); diff --git a/ggml-cuda/getrows.cu b/ggml-cuda/getrows.cu new file mode 100644 index 000000000..55af195fd --- /dev/null +++ b/ggml-cuda/getrows.cu @@ -0,0 +1,178 @@ +#include "getrows.cuh" +#include "dequantize.cuh" + +template +static __global__ void k_get_rows( + const void * src0, const int32_t * src1, dst_t * dst, + int64_t ne00, /*int64_t ne01, int64_t ne02, int64_t ne03,*/ + /*int64_t ne10, int64_t ne11,*/ int64_t ne12, /*int64_t ne13,*/ + /*size_t s0,*/ size_t s1, size_t s2, size_t s3, + /*size_t nb00,*/ size_t nb01, size_t nb02, size_t nb03, + size_t s10, size_t s11, size_t s12/*, size_t s13*/) { + + const int i00 = (blockIdx.x*blockDim.x + threadIdx.x)*2; + const int i10 = blockDim.y*blockIdx.y + threadIdx.y; + const int i11 = (blockIdx.z*blockDim.z + threadIdx.z)/ne12; + const int i12 = (blockIdx.z*blockDim.z + threadIdx.z)%ne12; + + if (i00 >= ne00) { + return; + } + + const int i01 = src1[i10*s10 + i11*s11 + i12*s12]; + + dst_t * dst_row = dst + i10*s1 + i11*s2 + i12*s3; + const void * src0_row = (const char *)src0 + i01*nb01 + i11*nb02 + i12*nb03; + + const int ib = i00/qk; // block index + const int iqs = (i00%qk)/qr; // quant index + const int iybs = i00 - i00%qk; // dst block start index + const int y_offset = qr == 1 ? 1 : qk/2; + + // dequantize + dfloat2 v; + dequantize_kernel(src0_row, ib, iqs, v); + + dst_row[iybs + iqs + 0] = v.x; + dst_row[iybs + iqs + y_offset] = v.y; +} + +template +static __global__ void k_get_rows_float( + const src0_t * src0, const int32_t * src1, dst_t * dst, + int64_t ne00, /*int64_t ne01, int64_t ne02, int64_t ne03,*/ + /*int64_t ne10, int64_t ne11,*/ int64_t ne12, /*int64_t ne13,*/ + /*size_t s0,*/ size_t s1, size_t s2, size_t s3, + /*size_t nb00,*/ size_t nb01, size_t nb02, size_t nb03, + size_t s10, size_t s11, size_t s12/*, size_t s13*/) { + + const int i00 = blockIdx.x*blockDim.x + threadIdx.x; + const int i10 = blockDim.y*blockIdx.y + threadIdx.y; + const int i11 = (blockIdx.z*blockDim.z + threadIdx.z)/ne12; + const int i12 = (blockIdx.z*blockDim.z + threadIdx.z)%ne12; + + if (i00 >= ne00) { + return; + } + + const int i01 = src1[i10*s10 + i11*s11 + i12*s12]; + + dst_t * dst_row = dst + i10*s1 + i11*s2 + i12*s3; + const src0_t * src0_row = (const src0_t *)((const char *)src0 + i01*nb01 + i11*nb02 + i12*nb03); + + dst_row[i00] = src0_row[i00]; +} + +template +static void get_rows_cuda(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, + const void * src0_dd, const int32_t * src1_dd, float * dst_dd, cudaStream_t stream) { + + GGML_TENSOR_BINARY_OP_LOCALS + + const dim3 block_dims(CUDA_GET_ROWS_BLOCK_SIZE, 1, 1); + const int block_num_x = (ne00 + 2*CUDA_GET_ROWS_BLOCK_SIZE - 1) / (2*CUDA_GET_ROWS_BLOCK_SIZE); + const dim3 block_nums(block_num_x, ne10, ne11*ne12); + + // strides in elements + //const size_t s0 = nb0 / ggml_element_size(dst); + const size_t s1 = nb1 / ggml_element_size(dst); + const size_t s2 = nb2 / ggml_element_size(dst); + const size_t s3 = nb3 / ggml_element_size(dst); + + const size_t s10 = nb10 / ggml_element_size(src1); + const size_t s11 = nb11 / ggml_element_size(src1); + const size_t s12 = nb12 / ggml_element_size(src1); + //const size_t s13 = nb13 / ggml_element_size(src1); + + GGML_ASSERT(ne00 % 2 == 0); + + k_get_rows<<>>( + src0_dd, src1_dd, dst_dd, + ne00, /*ne01, ne02, ne03,*/ + /*ne10, ne11,*/ ne12, /*ne13,*/ + /* s0,*/ s1, s2, s3, + /* nb00,*/ nb01, nb02, nb03, + s10, s11, s12/*, s13*/); + + GGML_UNUSED(dst); +} + +template +static void get_rows_cuda_float(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, + const src0_t * src0_dd, const int32_t * src1_dd, float * dst_dd, cudaStream_t stream) { + + GGML_TENSOR_BINARY_OP_LOCALS + + const dim3 block_dims(CUDA_GET_ROWS_BLOCK_SIZE, 1, 1); + const int block_num_x = (ne00 + CUDA_GET_ROWS_BLOCK_SIZE - 1) / CUDA_GET_ROWS_BLOCK_SIZE; + const dim3 block_nums(block_num_x, ne10, ne11*ne12); + + // strides in elements + //const size_t s0 = nb0 / ggml_element_size(dst); + const size_t s1 = nb1 / ggml_element_size(dst); + const size_t s2 = nb2 / ggml_element_size(dst); + const size_t s3 = nb3 / ggml_element_size(dst); + + const size_t s10 = nb10 / ggml_element_size(src1); + const size_t s11 = nb11 / ggml_element_size(src1); + const size_t s12 = nb12 / ggml_element_size(src1); + //const size_t s13 = nb13 / ggml_element_size(src1); + + k_get_rows_float<<>>( + src0_dd, src1_dd, dst_dd, + ne00, /*ne01, ne02, ne03,*/ + /*ne10, ne11,*/ ne12, /*ne13,*/ + /* s0,*/ s1, s2, s3, + /* nb00,*/ nb01, nb02, nb03, + s10, s11, s12/*, s13*/); + + GGML_UNUSED(dst); +} + +void ggml_cuda_op_get_rows(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + const float * src0_d = (const float *)src0->data; + const float * src1_d = (const float *)src1->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + + GGML_ASSERT(src1->type == GGML_TYPE_I32); + GGML_ASSERT(dst->type == GGML_TYPE_F32); + + GGML_ASSERT(src0->nb[0] == ggml_type_size(src0->type)); + GGML_ASSERT(src1->nb[0] == ggml_type_size(src1->type)); + GGML_ASSERT(dst->nb[0] == ggml_type_size(dst->type)); + + const int32_t * src1_i32 = (const int32_t *) src1_d; + + switch (src0->type) { + case GGML_TYPE_F16: + get_rows_cuda_float(src0, src1, dst, (const half *)src0_d, src1_i32, dst_d, stream); + break; + case GGML_TYPE_F32: + get_rows_cuda_float(src0, src1, dst, src0_d, src1_i32, dst_d, stream); + break; + case GGML_TYPE_Q4_0: + get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); + break; + case GGML_TYPE_Q4_1: + get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); + break; + case GGML_TYPE_Q5_0: + get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); + break; + case GGML_TYPE_Q5_1: + get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); + break; + case GGML_TYPE_Q8_0: + get_rows_cuda(src0, src1, dst, src0_d, src1_i32, dst_d, stream); + break; + default: + // TODO: k-quants + fprintf(stderr, "%s: unsupported type: %s\n", __func__, ggml_type_name(src0->type)); + GGML_ASSERT(false); + break; + } +} diff --git a/ggml-cuda/getrows.cuh b/ggml-cuda/getrows.cuh new file mode 100644 index 000000000..bbf130232 --- /dev/null +++ b/ggml-cuda/getrows.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_GET_ROWS_BLOCK_SIZE 256 + +void ggml_cuda_op_get_rows(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/im2col.cu b/ggml-cuda/im2col.cu new file mode 100644 index 000000000..3d0d8d4e6 --- /dev/null +++ b/ggml-cuda/im2col.cu @@ -0,0 +1,104 @@ +#include "im2col.cuh" + +template +static __global__ void im2col_kernel( + const float * x, T * dst, int64_t batch_offset, + int64_t offset_delta, int64_t IC, int64_t IW, int64_t IH, int64_t OH, int64_t OW, int64_t KW, int64_t KH, int64_t pelements, int64_t CHW, + int s0, int s1, int p0, int p1, int d0, int d1) { + const int64_t i = threadIdx.x + blockIdx.x * blockDim.x; + if (i >= pelements) { + return; + } + + const int64_t ksize = OW * (KH > 1 ? KW : 1); + const int64_t kx = i / ksize; + const int64_t kd = kx * ksize; + const int64_t ky = (i - kd) / OW; + const int64_t ix = i % OW; + + const int64_t oh = blockIdx.y; + const int64_t batch = blockIdx.z / IC; + const int64_t ic = blockIdx.z % IC; + + const int64_t iiw = ix * s0 + kx * d0 - p0; + const int64_t iih = oh * s1 + ky * d1 - p1; + + const int64_t offset_dst = + ((batch * OH + oh) * OW + ix) * CHW + + (ic * (KW * KH) + ky * KW + kx); + + if (iih < 0 || iih >= IH || iiw < 0 || iiw >= IW) { + dst[offset_dst] = 0.0f; + } else { + const int64_t offset_src = ic * offset_delta + batch * batch_offset; + dst[offset_dst] = x[offset_src + iih * IW + iiw]; + } +} + +template +static void im2col_cuda(const float * x, T* dst, + int64_t IW, int64_t IH, int64_t OW, int64_t OH, int64_t KW, int64_t KH, int64_t IC, + int64_t batch, int64_t batch_offset, int64_t offset_delta, + int s0,int s1,int p0,int p1,int d0,int d1, cudaStream_t stream) { + const int parallel_elements = OW * KW * KH; + const int num_blocks = (parallel_elements + CUDA_IM2COL_BLOCK_SIZE - 1) / CUDA_IM2COL_BLOCK_SIZE; + dim3 block_nums(num_blocks, OH, batch * IC); + im2col_kernel<<>>(x, dst, batch_offset, offset_delta, IC, IW, IH, OH, OW, KW, KH, parallel_elements, (IC * KH * KW), s0, s1, p0, p1, d0, d1); +} + +static void im2col_cuda_f16(const float * x, half * dst, + int64_t IW, int64_t IH, int64_t OW, int64_t OH, int64_t KW, int64_t KH, int64_t IC, + int64_t batch, int64_t batch_offset, int64_t offset_delta, + int s0,int s1,int p0,int p1,int d0,int d1, cudaStream_t stream) { + + im2col_cuda(x, dst, IW, IH, OW, OH, KW, KH, IC, batch, batch_offset, offset_delta, s0, s1, p0, p1, d0, d1, stream); +} + +static void im2col_cuda_f32(const float * x, float * dst, + int64_t IW, int64_t IH, int64_t OW, int64_t OH, int64_t KW, int64_t KH, int64_t IC, + int64_t batch, int64_t batch_offset, int64_t offset_delta, + int s0,int s1,int p0,int p1,int d0,int d1, cudaStream_t stream) { + + im2col_cuda(x, dst, IW, IH, OW, OH, KW, KH, IC, batch, batch_offset, offset_delta, s0, s1, p0, p1, d0, d1, stream); +} + +void ggml_cuda_op_im2col(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + const float * src1_d = (const float *)src1->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F16); + GGML_ASSERT(src1->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F16 || dst->type == GGML_TYPE_F32); + + const int32_t s0 = ((const int32_t*)(dst->op_params))[0]; + const int32_t s1 = ((const int32_t*)(dst->op_params))[1]; + const int32_t p0 = ((const int32_t*)(dst->op_params))[2]; + const int32_t p1 = ((const int32_t*)(dst->op_params))[3]; + const int32_t d0 = ((const int32_t*)(dst->op_params))[4]; + const int32_t d1 = ((const int32_t*)(dst->op_params))[5]; + + const bool is_2D = ((const int32_t*)(dst->op_params))[6] == 1; + + const int64_t IC = src1->ne[is_2D ? 2 : 1]; + const int64_t IH = is_2D ? src1->ne[1] : 1; + const int64_t IW = src1->ne[0]; + + const int64_t KH = is_2D ? src0->ne[1] : 1; + const int64_t KW = src0->ne[0]; + + const int64_t OH = is_2D ? dst->ne[2] : 1; + const int64_t OW = dst->ne[1]; + + const size_t delta_offset = src1->nb[is_2D ? 2 : 1] / 4; // nb is byte offset, src is type float32 + const int64_t batch = src1->ne[3]; + const size_t batch_offset = src1->nb[3] / 4; // nb is byte offset, src is type float32 + + if(dst->type == GGML_TYPE_F16) { + im2col_cuda_f16(src1_d, (half *) dst_d, IW, IH, OW, OH, KW, KH, IC, batch, batch_offset, delta_offset, s0, s1, p0, p1, d0, d1, stream); + } else { + im2col_cuda_f32(src1_d, (float *) dst_d, IW, IH, OW, OH, KW, KH, IC, batch, batch_offset, delta_offset, s0, s1, p0, p1, d0, d1, stream); + } +} diff --git a/ggml-cuda/im2col.cuh b/ggml-cuda/im2col.cuh new file mode 100644 index 000000000..1ce8fae4d --- /dev/null +++ b/ggml-cuda/im2col.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_IM2COL_BLOCK_SIZE 256 + +void ggml_cuda_op_im2col(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/mmq.cu b/ggml-cuda/mmq.cu new file mode 100644 index 000000000..60d6616a8 --- /dev/null +++ b/ggml-cuda/mmq.cu @@ -0,0 +1,2265 @@ +#include "mmq.cuh" +#include "vecdotq.cuh" + +typedef void (*allocate_tiles_cuda_t)(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc); +typedef void (*load_tiles_cuda_t)( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row); +typedef float (*vec_dot_q_mul_mat_cuda_t)( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ms, const int & i, const int & j, const int & k); +typedef void (*dot_kernel_k_t)(const void * __restrict__ vx, const int ib, const int iqs, const float * __restrict__ y, float & v); + +template static __device__ __forceinline__ void allocate_tiles_q4_0(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); + GGML_UNUSED(x_sc); + + __shared__ int tile_x_qs[mmq_y * (WARP_SIZE) + mmq_y]; + __shared__ float tile_x_d[mmq_y * (WARP_SIZE/QI4_0) + mmq_y/QI4_0]; + + *x_ql = tile_x_qs; + *x_dm = (half2 *) tile_x_d; +} + +template static __device__ __forceinline__ void load_tiles_q4_0( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI4_0; + const int kqsx = k % QI4_0; + + const block_q4_0 * bx0 = (const block_q4_0 *) vx; + + float * x_dmf = (float *) x_dm; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q4_0 * bxi = bx0 + i*blocks_per_row + kbx; + + x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8(bxi->qs, kqsx); + // x_dmf[i * (WARP_SIZE/QI4_0) + i / QI4_0 + kbx] = bxi->d; + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI4_0; + const int kbxd = k % blocks_per_tile_x_row; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI4_0) { + int i = i0 + i_offset * QI4_0 + k / blocks_per_tile_x_row; + + if (need_check) { + i = min(i, i_max); + } + + const block_q4_0 * bxi = bx0 + i*blocks_per_row + kbxd; + + x_dmf[i * (WARP_SIZE/QI4_0) + i / QI4_0 + kbxd] = bxi->d; + } +} + +static __device__ __forceinline__ float vec_dot_q4_0_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + const int kyqs = k % (QI8_1/2) + QI8_1 * (k / (QI8_1/2)); + const float * x_dmf = (const float *) x_dm; + + int u[2*VDR_Q4_0_Q8_1_MMQ]; + +#pragma unroll + for (int l = 0; l < VDR_Q4_0_Q8_1_MMQ; ++l) { + u[2*l+0] = y_qs[j * WARP_SIZE + (kyqs + l) % WARP_SIZE]; + u[2*l+1] = y_qs[j * WARP_SIZE + (kyqs + l + QI4_0) % WARP_SIZE]; + } + + return vec_dot_q4_0_q8_1_impl + (&x_ql[i * (WARP_SIZE + 1) + k], u, x_dmf[i * (WARP_SIZE/QI4_0) + i/QI4_0 + k/QI4_0], + y_ds[j * (WARP_SIZE/QI8_1) + (2*k/QI8_1) % (WARP_SIZE/QI8_1)]); +} + +template static __device__ __forceinline__ void allocate_tiles_q4_1(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + __shared__ int tile_x_qs[mmq_y * (WARP_SIZE) + + mmq_y]; + __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI4_1) + mmq_y/QI4_1]; + + *x_ql = tile_x_qs; + *x_dm = tile_x_dm; +} + +template static __device__ __forceinline__ void load_tiles_q4_1( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI4_1; + const int kqsx = k % QI4_1; + + const block_q4_1 * bx0 = (const block_q4_1 *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q4_1 * bxi = bx0 + i*blocks_per_row + kbx; + + x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8_aligned(bxi->qs, kqsx); + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI4_1; + const int kbxd = k % blocks_per_tile_x_row; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI4_1) { + int i = i0 + i_offset * QI4_1 + k / blocks_per_tile_x_row; + + if (need_check) { + i = min(i, i_max); + } + + const block_q4_1 * bxi = bx0 + i*blocks_per_row + kbxd; + + x_dm[i * (WARP_SIZE/QI4_1) + i / QI4_1 + kbxd] = bxi->dm; + } +} + +static __device__ __forceinline__ float vec_dot_q4_1_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + const int kyqs = k % (QI8_1/2) + QI8_1 * (k / (QI8_1/2)); + + int u[2*VDR_Q4_1_Q8_1_MMQ]; + +#pragma unroll + for (int l = 0; l < VDR_Q4_1_Q8_1_MMQ; ++l) { + u[2*l+0] = y_qs[j * WARP_SIZE + (kyqs + l) % WARP_SIZE]; + u[2*l+1] = y_qs[j * WARP_SIZE + (kyqs + l + QI4_1) % WARP_SIZE]; + } + + return vec_dot_q4_1_q8_1_impl + (&x_ql[i * (WARP_SIZE + 1) + k], u, x_dm[i * (WARP_SIZE/QI4_1) + i/QI4_1 + k/QI4_1], + y_ds[j * (WARP_SIZE/QI8_1) + (2*k/QI8_1) % (WARP_SIZE/QI8_1)]); +} + +template static __device__ __forceinline__ void allocate_tiles_q5_0(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + __shared__ int tile_x_ql[mmq_y * (2*WARP_SIZE) + mmq_y]; + __shared__ float tile_x_d[mmq_y * (WARP_SIZE/QI5_0) + mmq_y/QI5_0]; + + *x_ql = tile_x_ql; + *x_dm = (half2 *) tile_x_d; +} + +template static __device__ __forceinline__ void load_tiles_q5_0( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI5_0; + const int kqsx = k % QI5_0; + + const block_q5_0 * bx0 = (const block_q5_0 *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q5_0 * bxi = bx0 + i*blocks_per_row + kbx; + + const int ql = get_int_from_uint8(bxi->qs, kqsx); + const int qh = get_int_from_uint8(bxi->qh, 0) >> (4 * (k % QI5_0)); + + int qs0 = (ql >> 0) & 0x0F0F0F0F; + qs0 |= (qh << 4) & 0x00000010; // 0 -> 4 + qs0 |= (qh << 11) & 0x00001000; // 1 -> 12 + qs0 |= (qh << 18) & 0x00100000; // 2 -> 20 + qs0 |= (qh << 25) & 0x10000000; // 3 -> 28 + qs0 = __vsubss4(qs0, 0x10101010); // subtract 16 + + x_ql[i * (2*WARP_SIZE + 1) + 2*k+0] = qs0; + + int qs1 = (ql >> 4) & 0x0F0F0F0F; + qs1 |= (qh >> 12) & 0x00000010; // 16 -> 4 + qs1 |= (qh >> 5) & 0x00001000; // 17 -> 12 + qs1 |= (qh << 2) & 0x00100000; // 18 -> 20 + qs1 |= (qh << 9) & 0x10000000; // 19 -> 28 + qs1 = __vsubss4(qs1, 0x10101010); // subtract 16 + + x_ql[i * (2*WARP_SIZE + 1) + 2*k+1] = qs1; + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI5_0; + const int kbxd = k % blocks_per_tile_x_row; + float * x_dmf = (float *) x_dm; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI5_0) { + int i = i0 + i_offset * QI5_0 + k / blocks_per_tile_x_row; + + if (need_check) { + i = min(i, i_max); + } + + const block_q5_0 * bxi = bx0 + i*blocks_per_row + kbxd; + + x_dmf[i * (WARP_SIZE/QI5_0) + i / QI5_0 + kbxd] = bxi->d; + } +} + +static __device__ __forceinline__ float vec_dot_q5_0_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + const int kyqs = k % (QI8_1/2) + QI8_1 * (k / (QI8_1/2)); + const int index_bx = i * (WARP_SIZE/QI5_0) + i/QI5_0 + k/QI5_0; + const float * x_dmf = (const float *) x_dm; + const float * y_df = (const float *) y_ds; + + int u[2*VDR_Q5_0_Q8_1_MMQ]; + +#pragma unroll + for (int l = 0; l < VDR_Q5_0_Q8_1_MMQ; ++l) { + u[2*l+0] = y_qs[j * WARP_SIZE + (kyqs + l) % WARP_SIZE]; + u[2*l+1] = y_qs[j * WARP_SIZE + (kyqs + l + QI5_0) % WARP_SIZE]; + } + + return vec_dot_q8_0_q8_1_impl + (&x_ql[i * (2*WARP_SIZE + 1) + 2 * k], u, x_dmf[index_bx], y_df[j * (WARP_SIZE/QI8_1) + (2*k/QI8_1) % (WARP_SIZE/QI8_1)]); +} + + +template static __device__ __forceinline__ void allocate_tiles_q5_1(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + __shared__ int tile_x_ql[mmq_y * (2*WARP_SIZE) + mmq_y]; + __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI5_1) + mmq_y/QI5_1]; + + *x_ql = tile_x_ql; + *x_dm = tile_x_dm; +} + +template static __device__ __forceinline__ void load_tiles_q5_1( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI5_1; + const int kqsx = k % QI5_1; + + const block_q5_1 * bx0 = (const block_q5_1 *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q5_1 * bxi = bx0 + i*blocks_per_row + kbx; + + const int ql = get_int_from_uint8_aligned(bxi->qs, kqsx); + const int qh = get_int_from_uint8_aligned(bxi->qh, 0) >> (4 * (k % QI5_1)); + + int qs0 = (ql >> 0) & 0x0F0F0F0F; + qs0 |= (qh << 4) & 0x00000010; // 0 -> 4 + qs0 |= (qh << 11) & 0x00001000; // 1 -> 12 + qs0 |= (qh << 18) & 0x00100000; // 2 -> 20 + qs0 |= (qh << 25) & 0x10000000; // 3 -> 28 + + x_ql[i * (2*WARP_SIZE + 1) + 2*k+0] = qs0; + + int qs1 = (ql >> 4) & 0x0F0F0F0F; + qs1 |= (qh >> 12) & 0x00000010; // 16 -> 4 + qs1 |= (qh >> 5) & 0x00001000; // 17 -> 12 + qs1 |= (qh << 2) & 0x00100000; // 18 -> 20 + qs1 |= (qh << 9) & 0x10000000; // 19 -> 28 + + x_ql[i * (2*WARP_SIZE + 1) + 2*k+1] = qs1; + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI5_1; + const int kbxd = k % blocks_per_tile_x_row; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI5_1) { + int i = i0 + i_offset * QI5_1 + k / blocks_per_tile_x_row; + + if (need_check) { + i = min(i, i_max); + } + + const block_q5_1 * bxi = bx0 + i*blocks_per_row + kbxd; + + x_dm[i * (WARP_SIZE/QI5_1) + i / QI5_1 + kbxd] = bxi->dm; + } +} + +static __device__ __forceinline__ float vec_dot_q5_1_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + const int kyqs = k % (QI8_1/2) + QI8_1 * (k / (QI8_1/2)); + const int index_bx = i * (WARP_SIZE/QI5_1) + + i/QI5_1 + k/QI5_1; + + int u[2*VDR_Q5_1_Q8_1_MMQ]; + +#pragma unroll + for (int l = 0; l < VDR_Q5_1_Q8_1_MMQ; ++l) { + u[2*l+0] = y_qs[j * WARP_SIZE + (kyqs + l) % WARP_SIZE]; + u[2*l+1] = y_qs[j * WARP_SIZE + (kyqs + l + QI5_1) % WARP_SIZE]; + } + + return vec_dot_q8_1_q8_1_impl + (&x_ql[i * (2*WARP_SIZE + 1) + 2 * k], u, x_dm[index_bx], y_ds[j * (WARP_SIZE/QI8_1) + (2*k/QI8_1) % (WARP_SIZE/QI8_1)]); +} + +template static __device__ __forceinline__ void allocate_tiles_q8_0(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + __shared__ int tile_x_qs[mmq_y * (WARP_SIZE) + mmq_y]; + __shared__ float tile_x_d[mmq_y * (WARP_SIZE/QI8_0) + mmq_y/QI8_0]; + + *x_ql = tile_x_qs; + *x_dm = (half2 *) tile_x_d; +} + +template static __device__ __forceinline__ void load_tiles_q8_0( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI8_0; + const int kqsx = k % QI8_0; + float * x_dmf = (float *) x_dm; + + const block_q8_0 * bx0 = (const block_q8_0 *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q8_0 * bxi = bx0 + i*blocks_per_row + kbx; + + x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_int8(bxi->qs, kqsx); + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI8_0; + const int kbxd = k % blocks_per_tile_x_row; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI8_0) { + int i = i0 + i_offset * QI8_0 + k / blocks_per_tile_x_row; + + if (need_check) { + i = min(i, i_max); + } + + const block_q8_0 * bxi = bx0 + i*blocks_per_row + kbxd; + + x_dmf[i * (WARP_SIZE/QI8_0) + i / QI8_0 + kbxd] = bxi->d; + } +} + +static __device__ __forceinline__ float vec_dot_q8_0_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); GGML_UNUSED(x_sc); + + const float * x_dmf = (const float *) x_dm; + const float * y_df = (const float *) y_ds; + + return vec_dot_q8_0_q8_1_impl + (&x_ql[i * (WARP_SIZE + 1) + k], &y_qs[j * WARP_SIZE + k], x_dmf[i * (WARP_SIZE/QI8_0) + i/QI8_0 + k/QI8_0], + y_df[j * (WARP_SIZE/QI8_1) + k/QI8_1]); +} + +template static __device__ __forceinline__ void allocate_tiles_q2_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); + + __shared__ int tile_x_ql[mmq_y * (WARP_SIZE) + mmq_y]; + __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI2_K) + mmq_y/QI2_K]; + __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/4) + mmq_y/4]; + + *x_ql = tile_x_ql; + *x_dm = tile_x_dm; + *x_sc = tile_x_sc; +} + +template static __device__ __forceinline__ void load_tiles_q2_K( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI2_K; + const int kqsx = k % QI2_K; + + const block_q2_K * bx0 = (const block_q2_K *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q2_K * bxi = bx0 + i*blocks_per_row + kbx; + + x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8_aligned(bxi->qs, kqsx); + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI2_K; + const int kbxd = k % blocks_per_tile_x_row; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI2_K) { + int i = (i0 + i_offset * QI2_K + k / blocks_per_tile_x_row) % mmq_y; + + if (need_check) { + i = min(i, i_max); + } + + const block_q2_K * bxi = bx0 + i*blocks_per_row + kbxd; + + x_dm[i * (WARP_SIZE/QI2_K) + i / QI2_K + kbxd] = bxi->dm; + } + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 4) { + int i = i0 + i_offset * 4 + k / (WARP_SIZE/4); + + if (need_check) { + i = min(i, i_max); + } + + const block_q2_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/4)) / (QI2_K/4); + + x_sc[i * (WARP_SIZE/4) + i / 4 + k % (WARP_SIZE/4)] = get_int_from_uint8_aligned(bxi->scales, k % (QI2_K/4)); + } +} + +static __device__ __forceinline__ float vec_dot_q2_K_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); + + const int kbx = k / QI2_K; + const int ky = (k % QI2_K) * QR2_K; + const float * y_df = (const float *) y_ds; + + int v[QR2_K*VDR_Q2_K_Q8_1_MMQ]; + + const int kqsx = i * (WARP_SIZE + 1) + kbx*QI2_K + (QI2_K/2) * (ky/(2*QI2_K)) + ky % (QI2_K/2); + const int shift = 2 * ((ky % (2*QI2_K)) / (QI2_K/2)); + +#pragma unroll + for (int l = 0; l < QR2_K*VDR_Q2_K_Q8_1_MMQ; ++l) { + v[l] = (x_ql[kqsx + l] >> shift) & 0x03030303; + } + + const uint8_t * scales = ((const uint8_t *) &x_sc[i * (WARP_SIZE/4) + i/4 + kbx*4]) + ky/4; + + const int index_y = j * WARP_SIZE + (QR2_K*k) % WARP_SIZE; + return vec_dot_q2_K_q8_1_impl_mmq(v, &y_qs[index_y], scales, x_dm[i * (WARP_SIZE/QI2_K) + i/QI2_K + kbx], y_df[index_y/QI8_1]); +} + +template static __device__ __forceinline__ void allocate_tiles_q3_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + + __shared__ int tile_x_ql[mmq_y * (WARP_SIZE) + mmq_y]; + __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI3_K) + mmq_y/QI3_K]; + __shared__ int tile_x_qh[mmq_y * (WARP_SIZE/2) + mmq_y/2]; + __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/4) + mmq_y/4]; + + *x_ql = tile_x_ql; + *x_dm = tile_x_dm; + *x_qh = tile_x_qh; + *x_sc = tile_x_sc; +} + +template static __device__ __forceinline__ void load_tiles_q3_K( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI3_K; + const int kqsx = k % QI3_K; + + const block_q3_K * bx0 = (const block_q3_K *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q3_K * bxi = bx0 + i*blocks_per_row + kbx; + + x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8(bxi->qs, kqsx); + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI3_K; + const int kbxd = k % blocks_per_tile_x_row; + float * x_dmf = (float *) x_dm; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI3_K) { + int i = (i0 + i_offset * QI3_K + k / blocks_per_tile_x_row) % mmq_y; + + if (need_check) { + i = min(i, i_max); + } + + const block_q3_K * bxi = bx0 + i*blocks_per_row + kbxd; + + x_dmf[i * (WARP_SIZE/QI3_K) + i / QI3_K + kbxd] = bxi->d; + } + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 2) { + int i = i0 + i_offset * 2 + k / (WARP_SIZE/2); + + if (need_check) { + i = min(i, i_max); + } + + const block_q3_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/2)) / (QI3_K/2); + + // invert the mask with ~ so that a 0/1 results in 4/0 being subtracted + x_qh[i * (WARP_SIZE/2) + i / 2 + k % (WARP_SIZE/2)] = ~get_int_from_uint8(bxi->hmask, k % (QI3_K/2)); + } + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 4) { + int i = i0 + i_offset * 4 + k / (WARP_SIZE/4); + + if (need_check) { + i = min(i, i_max); + } + + const block_q3_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/4)) / (QI3_K/4); + + const int ksc = k % (QI3_K/4); + + const int ksc_low = ksc % (QI3_K/8); + const int shift_low = 4 * (ksc / (QI3_K/8)); + const int sc_low = (get_int_from_uint8(bxi->scales, ksc_low) >> shift_low) & 0x0F0F0F0F; + + const int ksc_high = QI3_K/8; + const int shift_high = 2 * ksc; + const int sc_high = ((get_int_from_uint8(bxi->scales, ksc_high) >> shift_high) << 4) & 0x30303030; + + const int sc = __vsubss4(sc_low | sc_high, 0x20202020); + + x_sc[i * (WARP_SIZE/4) + i / 4 + k % (WARP_SIZE/4)] = sc; + } +} + +static __device__ __forceinline__ float vec_dot_q3_K_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + + const int kbx = k / QI3_K; + const int ky = (k % QI3_K) * QR3_K; + const float * x_dmf = (const float *) x_dm; + const float * y_df = (const float *) y_ds; + + const int8_t * scales = ((const int8_t *) (x_sc + i * (WARP_SIZE/4) + i/4 + kbx*4)) + ky/4; + + int v[QR3_K*VDR_Q3_K_Q8_1_MMQ]; + +#pragma unroll + for (int l = 0; l < QR3_K*VDR_Q3_K_Q8_1_MMQ; ++l) { + const int kqsx = i * (WARP_SIZE + 1) + kbx*QI3_K + (QI3_K/2) * (ky/(2*QI3_K)) + ky % (QI3_K/2); + const int shift = 2 * ((ky % 32) / 8); + const int vll = (x_ql[kqsx + l] >> shift) & 0x03030303; + + const int vh = x_qh[i * (WARP_SIZE/2) + i/2 + kbx * (QI3_K/2) + (ky+l)%8] >> ((ky+l) / 8); + const int vlh = (vh << 2) & 0x04040404; + + v[l] = __vsubss4(vll, vlh); + } + + const int index_y = j * WARP_SIZE + (k*QR3_K) % WARP_SIZE; + return vec_dot_q3_K_q8_1_impl_mmq(v, &y_qs[index_y], scales, x_dmf[i * (WARP_SIZE/QI3_K) + i/QI3_K + kbx], y_df[index_y/QI8_1]); +} + +template static __device__ __forceinline__ void allocate_tiles_q4_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); + + __shared__ int tile_x_ql[mmq_y * (WARP_SIZE) + mmq_y]; + __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI4_K) + mmq_y/QI4_K]; + __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/8) + mmq_y/8]; + + *x_ql = tile_x_ql; + *x_dm = tile_x_dm; + *x_sc = tile_x_sc; +} + +template static __device__ __forceinline__ void load_tiles_q4_K( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI4_K; // == 0 if QK_K == 256 + const int kqsx = k % QI4_K; // == k if QK_K == 256 + + const block_q4_K * bx0 = (const block_q4_K *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q4_K * bxi = bx0 + i*blocks_per_row + kbx; + + x_ql[i * (WARP_SIZE + 1) + k] = get_int_from_uint8_aligned(bxi->qs, kqsx); + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI4_K; // == 1 if QK_K == 256 + const int kbxd = k % blocks_per_tile_x_row; // == 0 if QK_K == 256 + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI4_K) { + int i = (i0 + i_offset * QI4_K + k / blocks_per_tile_x_row) % mmq_y; + + if (need_check) { + i = min(i, i_max); + } + + const block_q4_K * bxi = bx0 + i*blocks_per_row + kbxd; + +#if QK_K == 256 + x_dm[i * (WARP_SIZE/QI4_K) + i / QI4_K + kbxd] = bxi->dm; +#else + x_dm[i * (WARP_SIZE/QI4_K) + i / QI4_K + kbxd] = {bxi->dm[0], bxi->dm[1]}; +#endif + } + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 8) { + int i = (i0 + i_offset * 8 + k / (WARP_SIZE/8)) % mmq_y; + + if (need_check) { + i = min(i, i_max); + } + + const block_q4_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/8)) / (QI4_K/8); + + const int * scales = (const int *) bxi->scales; + + const int ksc = k % (WARP_SIZE/8); + + // scale arrangement after the following two lines: sc0,...,sc3, sc4,...,sc7, m0,...,m3, m4,...,m8 + int scales8 = (scales[(ksc%2) + (ksc!=0)] >> (4 * (ksc & (ksc/2)))) & 0x0F0F0F0F; // lower 4 bits + scales8 |= (scales[ksc/2] >> (2 * (ksc % 2))) & 0x30303030; // upper 2 bits + + x_sc[i * (WARP_SIZE/8) + i / 8 + ksc] = scales8; + } +} + +static __device__ __forceinline__ float vec_dot_q4_K_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); + + const uint8_t * sc = ((const uint8_t *) &x_sc[i * (WARP_SIZE/8) + i/8 + k/16]) + 2*((k % 16) / 8); + + const int index_y = j * WARP_SIZE + (QR4_K*k) % WARP_SIZE; + return vec_dot_q4_K_q8_1_impl_mmq(&x_ql[i * (WARP_SIZE + 1) + k], &y_qs[index_y], sc, sc+8, + x_dm[i * (WARP_SIZE/QI4_K) + i/QI4_K], &y_ds[index_y/QI8_1]); +} + +template static __device__ __forceinline__ void allocate_tiles_q5_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); + + __shared__ int tile_x_ql[mmq_y * (2*WARP_SIZE) + mmq_y]; + __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI5_K) + mmq_y/QI5_K]; + __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/8) + mmq_y/8]; + + *x_ql = tile_x_ql; + *x_dm = tile_x_dm; + *x_sc = tile_x_sc; +} + +template static __device__ __forceinline__ void load_tiles_q5_K( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI5_K; // == 0 if QK_K == 256 + const int kqsx = k % QI5_K; // == k if QK_K == 256 + + const block_q5_K * bx0 = (const block_q5_K *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q5_K * bxi = bx0 + i*blocks_per_row + kbx; + const int ky = QR5_K*kqsx; + + const int ql = get_int_from_uint8_aligned(bxi->qs, kqsx); + const int ql0 = (ql >> 0) & 0x0F0F0F0F; + const int ql1 = (ql >> 4) & 0x0F0F0F0F; + + const int qh = get_int_from_uint8_aligned(bxi->qh, kqsx % (QI5_K/4)); + const int qh0 = ((qh >> (2 * (kqsx / (QI5_K/4)) + 0)) << 4) & 0x10101010; + const int qh1 = ((qh >> (2 * (kqsx / (QI5_K/4)) + 1)) << 4) & 0x10101010; + + const int kq0 = ky - ky % (QI5_K/2) + k % (QI5_K/4) + 0; + const int kq1 = ky - ky % (QI5_K/2) + k % (QI5_K/4) + (QI5_K/4); + + x_ql[i * (2*WARP_SIZE + 1) + kq0] = ql0 | qh0; + x_ql[i * (2*WARP_SIZE + 1) + kq1] = ql1 | qh1; + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI5_K; // == 1 if QK_K == 256 + const int kbxd = k % blocks_per_tile_x_row; // == 0 if QK_K == 256 + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI5_K) { + int i = (i0 + i_offset * QI5_K + k / blocks_per_tile_x_row) % mmq_y; + + if (need_check) { + i = min(i, i_max); + } + + const block_q5_K * bxi = bx0 + i*blocks_per_row + kbxd; + +#if QK_K == 256 + x_dm[i * (WARP_SIZE/QI5_K) + i / QI5_K + kbxd] = bxi->dm; +#endif + } + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 8) { + int i = (i0 + i_offset * 8 + k / (WARP_SIZE/8)) % mmq_y; + + if (need_check) { + i = min(i, i_max); + } + + const block_q5_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/8)) / (QI5_K/8); + + const int * scales = (const int *) bxi->scales; + + const int ksc = k % (WARP_SIZE/8); + + // scale arrangement after the following two lines: sc0,...,sc3, sc4,...,sc7, m0,...,m3, m4,...,m8 + int scales8 = (scales[(ksc%2) + (ksc!=0)] >> (4 * (ksc & (ksc/2)))) & 0x0F0F0F0F; // lower 4 bits + scales8 |= (scales[ksc/2] >> (2 * (ksc % 2))) & 0x30303030; // upper 2 bits + + x_sc[i * (WARP_SIZE/8) + i / 8 + ksc] = scales8; + } +} + +static __device__ __forceinline__ float vec_dot_q5_K_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); + + const uint8_t * sc = ((const uint8_t *) &x_sc[i * (WARP_SIZE/8) + i/8 + k/16]) + 2 * ((k % 16) / 8); + + const int index_x = i * (QR5_K*WARP_SIZE + 1) + QR5_K*k; + const int index_y = j * WARP_SIZE + (QR5_K*k) % WARP_SIZE; + return vec_dot_q5_K_q8_1_impl_mmq(&x_ql[index_x], &y_qs[index_y], sc, sc+8, + x_dm[i * (WARP_SIZE/QI5_K) + i/QI5_K], &y_ds[index_y/QI8_1]); +} + +template static __device__ __forceinline__ void allocate_tiles_q6_K(int ** x_ql, half2 ** x_dm, int ** x_qh, int ** x_sc) { + GGML_UNUSED(x_qh); + + __shared__ int tile_x_ql[mmq_y * (2*WARP_SIZE) + mmq_y]; + __shared__ half2 tile_x_dm[mmq_y * (WARP_SIZE/QI6_K) + mmq_y/QI6_K]; + __shared__ int tile_x_sc[mmq_y * (WARP_SIZE/8) + mmq_y/8]; + + *x_ql = tile_x_ql; + *x_dm = tile_x_dm; + *x_sc = tile_x_sc; +} + +template static __device__ __forceinline__ void load_tiles_q6_K( + const void * __restrict__ vx, int * __restrict__ x_ql, half2 * __restrict__ x_dm, int * __restrict__ x_qh, + int * __restrict__ x_sc, const int & i_offset, const int & i_max, const int & k, const int & blocks_per_row) { + GGML_UNUSED(x_qh); + + GGML_CUDA_ASSUME(i_offset >= 0); + GGML_CUDA_ASSUME(i_offset < nwarps); + GGML_CUDA_ASSUME(k >= 0); + GGML_CUDA_ASSUME(k < WARP_SIZE); + + const int kbx = k / QI6_K; // == 0 if QK_K == 256 + const int kqsx = k % QI6_K; // == k if QK_K == 256 + + const block_q6_K * bx0 = (const block_q6_K *) vx; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps) { + int i = i0 + i_offset; + + if (need_check) { + i = min(i, i_max); + } + + const block_q6_K * bxi = bx0 + i*blocks_per_row + kbx; + const int ky = QR6_K*kqsx; + + const int ql = get_int_from_uint8(bxi->ql, kqsx); + const int ql0 = (ql >> 0) & 0x0F0F0F0F; + const int ql1 = (ql >> 4) & 0x0F0F0F0F; + + const int qh = get_int_from_uint8(bxi->qh, (QI6_K/4) * (kqsx / (QI6_K/2)) + kqsx % (QI6_K/4)); + const int qh0 = ((qh >> (2 * ((kqsx % (QI6_K/2)) / (QI6_K/4)))) << 4) & 0x30303030; + const int qh1 = (qh >> (2 * ((kqsx % (QI6_K/2)) / (QI6_K/4)))) & 0x30303030; + + const int kq0 = ky - ky % QI6_K + k % (QI6_K/2) + 0; + const int kq1 = ky - ky % QI6_K + k % (QI6_K/2) + (QI6_K/2); + + x_ql[i * (2*WARP_SIZE + 1) + kq0] = __vsubss4(ql0 | qh0, 0x20202020); + x_ql[i * (2*WARP_SIZE + 1) + kq1] = __vsubss4(ql1 | qh1, 0x20202020); + } + + const int blocks_per_tile_x_row = WARP_SIZE / QI6_K; // == 1 if QK_K == 256 + const int kbxd = k % blocks_per_tile_x_row; // == 0 if QK_K == 256 + float * x_dmf = (float *) x_dm; + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * QI6_K) { + int i = (i0 + i_offset * QI6_K + k / blocks_per_tile_x_row) % mmq_y; + + if (need_check) { + i = min(i, i_max); + } + + const block_q6_K * bxi = bx0 + i*blocks_per_row + kbxd; + + x_dmf[i * (WARP_SIZE/QI6_K) + i / QI6_K + kbxd] = bxi->d; + } + +#pragma unroll + for (int i0 = 0; i0 < mmq_y; i0 += nwarps * 8) { + int i = (i0 + i_offset * 8 + k / (WARP_SIZE/8)) % mmq_y; + + if (need_check) { + i = min(i, i_max); + } + + const block_q6_K * bxi = bx0 + i*blocks_per_row + (k % (WARP_SIZE/8)) / 4; + + x_sc[i * (WARP_SIZE/8) + i / 8 + k % (WARP_SIZE/8)] = get_int_from_int8(bxi->scales, k % (QI6_K/8)); + } +} + +static __device__ __forceinline__ float vec_dot_q6_K_q8_1_mul_mat( + const int * __restrict__ x_ql, const half2 * __restrict__ x_dm, const int * __restrict__ x_qh, const int * __restrict__ x_sc, + const int * __restrict__ y_qs, const half2 * __restrict__ y_ds, const int & i, const int & j, const int & k) { + GGML_UNUSED(x_qh); + + const float * x_dmf = (const float *) x_dm; + const float * y_df = (const float *) y_ds; + + const int8_t * sc = ((const int8_t *) &x_sc[i * (WARP_SIZE/8) + i/8 + k/8]); + + const int index_x = i * (QR6_K*WARP_SIZE + 1) + QR6_K*k; + const int index_y = j * WARP_SIZE + (QR6_K*k) % WARP_SIZE; + return vec_dot_q6_K_q8_1_impl_mmq(&x_ql[index_x], &y_qs[index_y], sc, x_dmf[i * (WARP_SIZE/QI6_K) + i/QI6_K], &y_df[index_y/QI8_1]); +} + +#define MMQ_X_Q4_0_RDNA2 64 +#define MMQ_Y_Q4_0_RDNA2 128 +#define NWARPS_Q4_0_RDNA2 8 +#define MMQ_X_Q4_0_RDNA1 64 +#define MMQ_Y_Q4_0_RDNA1 64 +#define NWARPS_Q4_0_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q4_0_AMPERE 4 +#define MMQ_Y_Q4_0_AMPERE 32 +#define NWARPS_Q4_0_AMPERE 4 +#else +#define MMQ_X_Q4_0_AMPERE 64 +#define MMQ_Y_Q4_0_AMPERE 128 +#define NWARPS_Q4_0_AMPERE 4 +#endif +#define MMQ_X_Q4_0_PASCAL 64 +#define MMQ_Y_Q4_0_PASCAL 64 +#define NWARPS_Q4_0_PASCAL 8 + +template +static __device__ __forceinline__ void mul_mat_q( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + + const block_q_t * x = (const block_q_t *) vx; + const block_q8_1 * y = (const block_q8_1 *) vy; + + const int blocks_per_row_x = ncols_x / qk; + const int blocks_per_col_y = nrows_y / QK8_1; + const int blocks_per_warp = WARP_SIZE / qi; + + const int & ncols_dst = ncols_y; + + const int row_dst_0 = blockIdx.x*mmq_y; + const int & row_x_0 = row_dst_0; + + const int col_dst_0 = blockIdx.y*mmq_x; + const int & col_y_0 = col_dst_0; + + int * tile_x_ql = nullptr; + half2 * tile_x_dm = nullptr; + int * tile_x_qh = nullptr; + int * tile_x_sc = nullptr; + + allocate_tiles(&tile_x_ql, &tile_x_dm, &tile_x_qh, &tile_x_sc); + + __shared__ int tile_y_qs[mmq_x * WARP_SIZE]; + __shared__ half2 tile_y_ds[mmq_x * WARP_SIZE/QI8_1]; + + float sum[mmq_y/WARP_SIZE][mmq_x/nwarps] = {{0.0f}}; + + for (int ib0 = 0; ib0 < blocks_per_row_x; ib0 += blocks_per_warp) { + + load_tiles(x + row_x_0*blocks_per_row_x + ib0, tile_x_ql, tile_x_dm, tile_x_qh, tile_x_sc, + threadIdx.y, nrows_x-row_x_0-1, threadIdx.x, blocks_per_row_x); + +#pragma unroll + for (int ir = 0; ir < qr; ++ir) { + const int kqs = ir*WARP_SIZE + threadIdx.x; + const int kbxd = kqs / QI8_1; + +#pragma unroll + for (int i = 0; i < mmq_x; i += nwarps) { + const int col_y_eff = min(col_y_0 + threadIdx.y + i, ncols_y-1); // to prevent out-of-bounds memory accesses + + const block_q8_1 * by0 = &y[col_y_eff*blocks_per_col_y + ib0 * (qk/QK8_1) + kbxd]; + + const int index_y = (threadIdx.y + i) * WARP_SIZE + kqs % WARP_SIZE; + tile_y_qs[index_y] = get_int_from_int8_aligned(by0->qs, threadIdx.x % QI8_1); + } + +#pragma unroll + for (int ids0 = 0; ids0 < mmq_x; ids0 += nwarps * QI8_1) { + const int ids = (ids0 + threadIdx.y * QI8_1 + threadIdx.x / (WARP_SIZE/QI8_1)) % mmq_x; + const int kby = threadIdx.x % (WARP_SIZE/QI8_1); + const int col_y_eff = min(col_y_0 + ids, ncols_y-1); + + // if the sum is not needed it's faster to transform the scale to f32 ahead of time + const half2 * dsi_src = &y[col_y_eff*blocks_per_col_y + ib0 * (qk/QK8_1) + ir*(WARP_SIZE/QI8_1) + kby].ds; + half2 * dsi_dst = &tile_y_ds[ids * (WARP_SIZE/QI8_1) + kby]; + if (need_sum) { + *dsi_dst = *dsi_src; + } else { + float * dfi_dst = (float *) dsi_dst; + *dfi_dst = __low2float(*dsi_src); + } + } + + __syncthreads(); + +// #pragma unroll // unrolling this loop causes too much register pressure + for (int k = ir*WARP_SIZE/qr; k < (ir+1)*WARP_SIZE/qr; k += vdr) { +#pragma unroll + for (int j = 0; j < mmq_x; j += nwarps) { +#pragma unroll + for (int i = 0; i < mmq_y; i += WARP_SIZE) { + sum[i/WARP_SIZE][j/nwarps] += vec_dot( + tile_x_ql, tile_x_dm, tile_x_qh, tile_x_sc, tile_y_qs, tile_y_ds, + threadIdx.x + i, threadIdx.y + j, k); + } + } + } + + __syncthreads(); + } + } + +#pragma unroll + for (int j = 0; j < mmq_x; j += nwarps) { + const int col_dst = col_dst_0 + j + threadIdx.y; + + if (col_dst >= ncols_dst) { + return; + } + +#pragma unroll + for (int i = 0; i < mmq_y; i += WARP_SIZE) { + const int row_dst = row_dst_0 + threadIdx.x + i; + + if (row_dst >= nrows_dst) { + continue; + } + + dst[col_dst*nrows_dst + row_dst] = sum[i/WARP_SIZE][j/nwarps]; + } + } +} + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q4_0_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) + mul_mat_q4_0( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q4_0_RDNA2; + const int mmq_y = MMQ_Y_Q4_0_RDNA2; + const int nwarps = NWARPS_Q4_0_RDNA2; +#else + const int mmq_x = MMQ_X_Q4_0_RDNA1; + const int mmq_y = MMQ_Y_Q4_0_RDNA1; + const int nwarps = NWARPS_Q4_0_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q4_0, VDR_Q4_0_Q8_1_MMQ, vec_dot_q4_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q4_0_AMPERE; + const int mmq_y = MMQ_Y_Q4_0_AMPERE; + const int nwarps = NWARPS_Q4_0_AMPERE; + + mul_mat_q, + load_tiles_q4_0, VDR_Q4_0_Q8_1_MMQ, vec_dot_q4_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q4_0_PASCAL; + const int mmq_y = MMQ_Y_Q4_0_PASCAL; + const int nwarps = NWARPS_Q4_0_PASCAL; + + mul_mat_q, + load_tiles_q4_0, VDR_Q4_0_Q8_1_MMQ, vec_dot_q4_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q4_0_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q4_1_RDNA2 64 +#define MMQ_Y_Q4_1_RDNA2 128 +#define NWARPS_Q4_1_RDNA2 8 +#define MMQ_X_Q4_1_RDNA1 64 +#define MMQ_Y_Q4_1_RDNA1 64 +#define NWARPS_Q4_1_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q4_1_AMPERE 4 +#define MMQ_Y_Q4_1_AMPERE 32 +#define NWARPS_Q4_1_AMPERE 4 +#else +#define MMQ_X_Q4_1_AMPERE 64 +#define MMQ_Y_Q4_1_AMPERE 128 +#define NWARPS_Q4_1_AMPERE 4 +#endif +#define MMQ_X_Q4_1_PASCAL 64 +#define MMQ_Y_Q4_1_PASCAL 64 +#define NWARPS_Q4_1_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q4_1_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#elif __CUDA_ARCH__ < CC_VOLTA + __launch_bounds__(WARP_SIZE*NWARPS_Q4_1_PASCAL, 2) +#endif // __CUDA_ARCH__ < CC_VOLTA + mul_mat_q4_1( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q4_1_RDNA2; + const int mmq_y = MMQ_Y_Q4_1_RDNA2; + const int nwarps = NWARPS_Q4_1_RDNA2; +#else + const int mmq_x = MMQ_X_Q4_1_RDNA1; + const int mmq_y = MMQ_Y_Q4_1_RDNA1; + const int nwarps = NWARPS_Q4_1_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q4_1, VDR_Q4_1_Q8_1_MMQ, vec_dot_q4_1_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q4_1_AMPERE; + const int mmq_y = MMQ_Y_Q4_1_AMPERE; + const int nwarps = NWARPS_Q4_1_AMPERE; + + mul_mat_q, + load_tiles_q4_1, VDR_Q4_1_Q8_1_MMQ, vec_dot_q4_1_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q4_1_PASCAL; + const int mmq_y = MMQ_Y_Q4_1_PASCAL; + const int nwarps = NWARPS_Q4_1_PASCAL; + + mul_mat_q, + load_tiles_q4_1, VDR_Q4_1_Q8_1_MMQ, vec_dot_q4_1_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q4_1_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q5_0_RDNA2 64 +#define MMQ_Y_Q5_0_RDNA2 128 +#define NWARPS_Q5_0_RDNA2 8 +#define MMQ_X_Q5_0_RDNA1 64 +#define MMQ_Y_Q5_0_RDNA1 64 +#define NWARPS_Q5_0_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q5_0_AMPERE 4 +#define MMQ_Y_Q5_0_AMPERE 32 +#define NWARPS_Q5_0_AMPERE 4 +#else +#define MMQ_X_Q5_0_AMPERE 128 +#define MMQ_Y_Q5_0_AMPERE 64 +#define NWARPS_Q5_0_AMPERE 4 +#endif +#define MMQ_X_Q5_0_PASCAL 64 +#define MMQ_Y_Q5_0_PASCAL 64 +#define NWARPS_Q5_0_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q5_0_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) + mul_mat_q5_0( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q5_0_RDNA2; + const int mmq_y = MMQ_Y_Q5_0_RDNA2; + const int nwarps = NWARPS_Q5_0_RDNA2; +#else + const int mmq_x = MMQ_X_Q5_0_RDNA1; + const int mmq_y = MMQ_Y_Q5_0_RDNA1; + const int nwarps = NWARPS_Q5_0_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q5_0, VDR_Q5_0_Q8_1_MMQ, vec_dot_q5_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q5_0_AMPERE; + const int mmq_y = MMQ_Y_Q5_0_AMPERE; + const int nwarps = NWARPS_Q5_0_AMPERE; + + mul_mat_q, + load_tiles_q5_0, VDR_Q5_0_Q8_1_MMQ, vec_dot_q5_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q5_0_PASCAL; + const int mmq_y = MMQ_Y_Q5_0_PASCAL; + const int nwarps = NWARPS_Q5_0_PASCAL; + + mul_mat_q, + load_tiles_q5_0, VDR_Q5_0_Q8_1_MMQ, vec_dot_q5_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q5_0_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q5_1_RDNA2 64 +#define MMQ_Y_Q5_1_RDNA2 128 +#define NWARPS_Q5_1_RDNA2 8 +#define MMQ_X_Q5_1_RDNA1 64 +#define MMQ_Y_Q5_1_RDNA1 64 +#define NWARPS_Q5_1_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q5_1_AMPERE 4 +#define MMQ_Y_Q5_1_AMPERE 32 +#define NWARPS_Q5_1_AMPERE 4 +#else +#define MMQ_X_Q5_1_AMPERE 128 +#define MMQ_Y_Q5_1_AMPERE 64 +#define NWARPS_Q5_1_AMPERE 4 +#endif +#define MMQ_X_Q5_1_PASCAL 64 +#define MMQ_Y_Q5_1_PASCAL 64 +#define NWARPS_Q5_1_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q5_1_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +mul_mat_q5_1( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q5_1_RDNA2; + const int mmq_y = MMQ_Y_Q5_1_RDNA2; + const int nwarps = NWARPS_Q5_1_RDNA2; +#else + const int mmq_x = MMQ_X_Q5_1_RDNA1; + const int mmq_y = MMQ_Y_Q5_1_RDNA1; + const int nwarps = NWARPS_Q5_1_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q5_1, VDR_Q5_1_Q8_1_MMQ, vec_dot_q5_1_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q5_1_AMPERE; + const int mmq_y = MMQ_Y_Q5_1_AMPERE; + const int nwarps = NWARPS_Q5_1_AMPERE; + + mul_mat_q, + load_tiles_q5_1, VDR_Q5_1_Q8_1_MMQ, vec_dot_q5_1_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q5_1_PASCAL; + const int mmq_y = MMQ_Y_Q5_1_PASCAL; + const int nwarps = NWARPS_Q5_1_PASCAL; + + mul_mat_q, + load_tiles_q5_1, VDR_Q5_1_Q8_1_MMQ, vec_dot_q5_1_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q5_1_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q8_0_RDNA2 64 +#define MMQ_Y_Q8_0_RDNA2 128 +#define NWARPS_Q8_0_RDNA2 8 +#define MMQ_X_Q8_0_RDNA1 64 +#define MMQ_Y_Q8_0_RDNA1 64 +#define NWARPS_Q8_0_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q8_0_AMPERE 4 +#define MMQ_Y_Q8_0_AMPERE 32 +#define NWARPS_Q8_0_AMPERE 4 +#else +#define MMQ_X_Q8_0_AMPERE 128 +#define MMQ_Y_Q8_0_AMPERE 64 +#define NWARPS_Q8_0_AMPERE 4 +#endif +#define MMQ_X_Q8_0_PASCAL 64 +#define MMQ_Y_Q8_0_PASCAL 64 +#define NWARPS_Q8_0_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q8_0_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) + mul_mat_q8_0( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q8_0_RDNA2; + const int mmq_y = MMQ_Y_Q8_0_RDNA2; + const int nwarps = NWARPS_Q8_0_RDNA2; +#else + const int mmq_x = MMQ_X_Q8_0_RDNA1; + const int mmq_y = MMQ_Y_Q8_0_RDNA1; + const int nwarps = NWARPS_Q8_0_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q8_0, VDR_Q8_0_Q8_1_MMQ, vec_dot_q8_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q8_0_AMPERE; + const int mmq_y = MMQ_Y_Q8_0_AMPERE; + const int nwarps = NWARPS_Q8_0_AMPERE; + + mul_mat_q, + load_tiles_q8_0, VDR_Q8_0_Q8_1_MMQ, vec_dot_q8_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q8_0_PASCAL; + const int mmq_y = MMQ_Y_Q8_0_PASCAL; + const int nwarps = NWARPS_Q8_0_PASCAL; + + mul_mat_q, + load_tiles_q8_0, VDR_Q8_0_Q8_1_MMQ, vec_dot_q8_0_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q8_0_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q2_K_RDNA2 64 +#define MMQ_Y_Q2_K_RDNA2 128 +#define NWARPS_Q2_K_RDNA2 8 +#define MMQ_X_Q2_K_RDNA1 128 +#define MMQ_Y_Q2_K_RDNA1 32 +#define NWARPS_Q2_K_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q2_K_AMPERE 4 +#define MMQ_Y_Q2_K_AMPERE 32 +#define NWARPS_Q2_K_AMPERE 4 +#else +#define MMQ_X_Q2_K_AMPERE 64 +#define MMQ_Y_Q2_K_AMPERE 128 +#define NWARPS_Q2_K_AMPERE 4 +#endif +#define MMQ_X_Q2_K_PASCAL 64 +#define MMQ_Y_Q2_K_PASCAL 64 +#define NWARPS_Q2_K_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q2_K_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +mul_mat_q2_K( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q2_K_RDNA2; + const int mmq_y = MMQ_Y_Q2_K_RDNA2; + const int nwarps = NWARPS_Q2_K_RDNA2; +#else + const int mmq_x = MMQ_X_Q2_K_RDNA1; + const int mmq_y = MMQ_Y_Q2_K_RDNA1; + const int nwarps = NWARPS_Q2_K_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q2_K, VDR_Q2_K_Q8_1_MMQ, vec_dot_q2_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q2_K_AMPERE; + const int mmq_y = MMQ_Y_Q2_K_AMPERE; + const int nwarps = NWARPS_Q2_K_AMPERE; + + mul_mat_q, + load_tiles_q2_K, VDR_Q2_K_Q8_1_MMQ, vec_dot_q2_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q2_K_PASCAL; + const int mmq_y = MMQ_Y_Q2_K_PASCAL; + const int nwarps = NWARPS_Q2_K_PASCAL; + + mul_mat_q, + load_tiles_q2_K, VDR_Q2_K_Q8_1_MMQ, vec_dot_q2_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q2_K_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q3_K_RDNA2 128 +#define MMQ_Y_Q3_K_RDNA2 64 +#define NWARPS_Q3_K_RDNA2 8 +#define MMQ_X_Q3_K_RDNA1 32 +#define MMQ_Y_Q3_K_RDNA1 128 +#define NWARPS_Q3_K_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q3_K_AMPERE 4 +#define MMQ_Y_Q3_K_AMPERE 32 +#define NWARPS_Q3_K_AMPERE 4 +#else +#define MMQ_X_Q3_K_AMPERE 128 +#define MMQ_Y_Q3_K_AMPERE 128 +#define NWARPS_Q3_K_AMPERE 4 +#endif +#define MMQ_X_Q3_K_PASCAL 64 +#define MMQ_Y_Q3_K_PASCAL 64 +#define NWARPS_Q3_K_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q3_K_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#elif __CUDA_ARCH__ < CC_VOLTA + __launch_bounds__(WARP_SIZE*NWARPS_Q3_K_PASCAL, 2) +#endif // __CUDA_ARCH__ < CC_VOLTA + mul_mat_q3_K( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q3_K_RDNA2; + const int mmq_y = MMQ_Y_Q3_K_RDNA2; + const int nwarps = NWARPS_Q3_K_RDNA2; +#else + const int mmq_x = MMQ_X_Q3_K_RDNA1; + const int mmq_y = MMQ_Y_Q3_K_RDNA1; + const int nwarps = NWARPS_Q3_K_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q3_K, VDR_Q3_K_Q8_1_MMQ, vec_dot_q3_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q3_K_AMPERE; + const int mmq_y = MMQ_Y_Q3_K_AMPERE; + const int nwarps = NWARPS_Q3_K_AMPERE; + + mul_mat_q, + load_tiles_q3_K, VDR_Q3_K_Q8_1_MMQ, vec_dot_q3_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q3_K_PASCAL; + const int mmq_y = MMQ_Y_Q3_K_PASCAL; + const int nwarps = NWARPS_Q3_K_PASCAL; + + mul_mat_q, + load_tiles_q3_K, VDR_Q3_K_Q8_1_MMQ, vec_dot_q3_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q3_K_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q4_K_RDNA2 64 +#define MMQ_Y_Q4_K_RDNA2 128 +#define NWARPS_Q4_K_RDNA2 8 +#define MMQ_X_Q4_K_RDNA1 32 +#define MMQ_Y_Q4_K_RDNA1 64 +#define NWARPS_Q4_K_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q4_K_AMPERE 4 +#define MMQ_Y_Q4_K_AMPERE 32 +#define NWARPS_Q4_K_AMPERE 4 +#else +#define MMQ_X_Q4_K_AMPERE 64 +#define MMQ_Y_Q4_K_AMPERE 128 +#define NWARPS_Q4_K_AMPERE 4 +#endif +#define MMQ_X_Q4_K_PASCAL 64 +#define MMQ_Y_Q4_K_PASCAL 64 +#define NWARPS_Q4_K_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q4_K_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#elif __CUDA_ARCH__ < CC_VOLTA + __launch_bounds__(WARP_SIZE*NWARPS_Q4_K_PASCAL, 2) +#endif // __CUDA_ARCH__ < CC_VOLTA + mul_mat_q4_K( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q4_K_RDNA2; + const int mmq_y = MMQ_Y_Q4_K_RDNA2; + const int nwarps = NWARPS_Q4_K_RDNA2; +#else + const int mmq_x = MMQ_X_Q4_K_RDNA1; + const int mmq_y = MMQ_Y_Q4_K_RDNA1; + const int nwarps = NWARPS_Q4_K_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q4_K, VDR_Q4_K_Q8_1_MMQ, vec_dot_q4_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q4_K_AMPERE; + const int mmq_y = MMQ_Y_Q4_K_AMPERE; + const int nwarps = NWARPS_Q4_K_AMPERE; + + mul_mat_q, + load_tiles_q4_K, VDR_Q4_K_Q8_1_MMQ, vec_dot_q4_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q4_K_PASCAL; + const int mmq_y = MMQ_Y_Q4_K_PASCAL; + const int nwarps = NWARPS_Q4_K_PASCAL; + + mul_mat_q, + load_tiles_q4_K, VDR_Q4_K_Q8_1_MMQ, vec_dot_q4_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q4_K_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q5_K_RDNA2 64 +#define MMQ_Y_Q5_K_RDNA2 128 +#define NWARPS_Q5_K_RDNA2 8 +#define MMQ_X_Q5_K_RDNA1 32 +#define MMQ_Y_Q5_K_RDNA1 64 +#define NWARPS_Q5_K_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q5_K_AMPERE 4 +#define MMQ_Y_Q5_K_AMPERE 32 +#define NWARPS_Q5_K_AMPERE 4 +#else +#define MMQ_X_Q5_K_AMPERE 64 +#define MMQ_Y_Q5_K_AMPERE 128 +#define NWARPS_Q5_K_AMPERE 4 +#endif +#define MMQ_X_Q5_K_PASCAL 64 +#define MMQ_Y_Q5_K_PASCAL 64 +#define NWARPS_Q5_K_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q5_K_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +mul_mat_q5_K( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q5_K_RDNA2; + const int mmq_y = MMQ_Y_Q5_K_RDNA2; + const int nwarps = NWARPS_Q5_K_RDNA2; +#else + const int mmq_x = MMQ_X_Q5_K_RDNA1; + const int mmq_y = MMQ_Y_Q5_K_RDNA1; + const int nwarps = NWARPS_Q5_K_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q5_K, VDR_Q5_K_Q8_1_MMQ, vec_dot_q5_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q5_K_AMPERE; + const int mmq_y = MMQ_Y_Q5_K_AMPERE; + const int nwarps = NWARPS_Q5_K_AMPERE; + + mul_mat_q, + load_tiles_q5_K, VDR_Q5_K_Q8_1_MMQ, vec_dot_q5_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q5_K_PASCAL; + const int mmq_y = MMQ_Y_Q5_K_PASCAL; + const int nwarps = NWARPS_Q5_K_PASCAL; + + mul_mat_q, + load_tiles_q5_K, VDR_Q5_K_Q8_1_MMQ, vec_dot_q5_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q5_K_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +#define MMQ_X_Q6_K_RDNA2 64 +#define MMQ_Y_Q6_K_RDNA2 128 +#define NWARPS_Q6_K_RDNA2 8 +#define MMQ_X_Q6_K_RDNA1 32 +#define MMQ_Y_Q6_K_RDNA1 64 +#define NWARPS_Q6_K_RDNA1 8 +#if defined(CUDA_USE_TENSOR_CORES) +#define MMQ_X_Q6_K_AMPERE 4 +#define MMQ_Y_Q6_K_AMPERE 32 +#define NWARPS_Q6_K_AMPERE 4 +#else +#define MMQ_X_Q6_K_AMPERE 64 +#define MMQ_Y_Q6_K_AMPERE 64 +#define NWARPS_Q6_K_AMPERE 4 +#endif +#define MMQ_X_Q6_K_PASCAL 64 +#define MMQ_Y_Q6_K_PASCAL 64 +#define NWARPS_Q6_K_PASCAL 8 + +template static __global__ void +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + __launch_bounds__(WARP_SIZE*NWARPS_Q6_K_RDNA2, 2) +#endif // defined(RDNA3) || defined(RDNA2) +#elif __CUDA_ARCH__ < CC_VOLTA + __launch_bounds__(WARP_SIZE*NWARPS_Q6_K_PASCAL, 2) +#endif // __CUDA_ARCH__ < CC_VOLTA + mul_mat_q6_K( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int ncols_y, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) +#if defined(RDNA3) || defined(RDNA2) + const int mmq_x = MMQ_X_Q6_K_RDNA2; + const int mmq_y = MMQ_Y_Q6_K_RDNA2; + const int nwarps = NWARPS_Q6_K_RDNA2; +#else + const int mmq_x = MMQ_X_Q6_K_RDNA1; + const int mmq_y = MMQ_Y_Q6_K_RDNA1; + const int nwarps = NWARPS_Q6_K_RDNA1; +#endif // defined(RDNA3) || defined(RDNA2) + + mul_mat_q, + load_tiles_q6_K, VDR_Q6_K_Q8_1_MMQ, vec_dot_q6_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= CC_VOLTA + const int mmq_x = MMQ_X_Q6_K_AMPERE; + const int mmq_y = MMQ_Y_Q6_K_AMPERE; + const int nwarps = NWARPS_Q6_K_AMPERE; + + mul_mat_q, + load_tiles_q6_K, VDR_Q6_K_Q8_1_MMQ, vec_dot_q6_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + +#elif __CUDA_ARCH__ >= MIN_CC_DP4A + const int mmq_x = MMQ_X_Q6_K_PASCAL; + const int mmq_y = MMQ_Y_Q6_K_PASCAL; + const int nwarps = NWARPS_Q6_K_PASCAL; + + mul_mat_q, + load_tiles_q6_K, VDR_Q6_K_Q8_1_MMQ, vec_dot_q6_K_q8_1_mul_mat> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); +#else + GGML_UNUSED(vec_dot_q6_K_q8_1_mul_mat); + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= CC_VOLTA +} + +static void ggml_mul_mat_q4_0_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q4_0_RDNA2; + mmq_y = MMQ_Y_Q4_0_RDNA2; + nwarps = NWARPS_Q4_0_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q4_0_RDNA1; + mmq_y = MMQ_Y_Q4_0_RDNA1; + nwarps = NWARPS_Q4_0_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q4_0_AMPERE; + mmq_y = MMQ_Y_Q4_0_AMPERE; + nwarps = NWARPS_Q4_0_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q4_0_PASCAL; + mmq_y = MMQ_Y_Q4_0_PASCAL; + nwarps = NWARPS_Q4_0_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q4_0<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q4_0<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +static void ggml_mul_mat_q4_1_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q4_1_RDNA2; + mmq_y = MMQ_Y_Q4_1_RDNA2; + nwarps = NWARPS_Q4_1_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q4_1_RDNA1; + mmq_y = MMQ_Y_Q4_1_RDNA1; + nwarps = NWARPS_Q4_1_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q4_1_AMPERE; + mmq_y = MMQ_Y_Q4_1_AMPERE; + nwarps = NWARPS_Q4_1_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q4_1_PASCAL; + mmq_y = MMQ_Y_Q4_1_PASCAL; + nwarps = NWARPS_Q4_1_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q4_1<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q4_1<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +static void ggml_mul_mat_q5_0_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q5_0_RDNA2; + mmq_y = MMQ_Y_Q5_0_RDNA2; + nwarps = NWARPS_Q5_0_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q5_0_RDNA1; + mmq_y = MMQ_Y_Q5_0_RDNA1; + nwarps = NWARPS_Q5_0_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q5_0_AMPERE; + mmq_y = MMQ_Y_Q5_0_AMPERE; + nwarps = NWARPS_Q5_0_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q5_0_PASCAL; + mmq_y = MMQ_Y_Q5_0_PASCAL; + nwarps = NWARPS_Q5_0_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q5_0<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q5_0<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +static void ggml_mul_mat_q5_1_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q5_1_RDNA2; + mmq_y = MMQ_Y_Q5_1_RDNA2; + nwarps = NWARPS_Q5_1_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q5_1_RDNA1; + mmq_y = MMQ_Y_Q5_1_RDNA1; + nwarps = NWARPS_Q5_1_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q5_1_AMPERE; + mmq_y = MMQ_Y_Q5_1_AMPERE; + nwarps = NWARPS_Q5_1_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q5_1_PASCAL; + mmq_y = MMQ_Y_Q5_1_PASCAL; + nwarps = NWARPS_Q5_1_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q5_1<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q5_1<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +static void ggml_mul_mat_q8_0_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q8_0_RDNA2; + mmq_y = MMQ_Y_Q8_0_RDNA2; + nwarps = NWARPS_Q8_0_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q8_0_RDNA1; + mmq_y = MMQ_Y_Q8_0_RDNA1; + nwarps = NWARPS_Q8_0_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q8_0_AMPERE; + mmq_y = MMQ_Y_Q8_0_AMPERE; + nwarps = NWARPS_Q8_0_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q8_0_PASCAL; + mmq_y = MMQ_Y_Q8_0_PASCAL; + nwarps = NWARPS_Q8_0_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q8_0<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q8_0<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +static void ggml_mul_mat_q2_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q2_K_RDNA2; + mmq_y = MMQ_Y_Q2_K_RDNA2; + nwarps = NWARPS_Q2_K_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q2_K_RDNA1; + mmq_y = MMQ_Y_Q2_K_RDNA1; + nwarps = NWARPS_Q2_K_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q2_K_AMPERE; + mmq_y = MMQ_Y_Q2_K_AMPERE; + nwarps = NWARPS_Q2_K_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q2_K_PASCAL; + mmq_y = MMQ_Y_Q2_K_PASCAL; + nwarps = NWARPS_Q2_K_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q2_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q2_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +static void ggml_mul_mat_q3_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + +#if QK_K == 256 + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q3_K_RDNA2; + mmq_y = MMQ_Y_Q3_K_RDNA2; + nwarps = NWARPS_Q3_K_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q3_K_RDNA1; + mmq_y = MMQ_Y_Q3_K_RDNA1; + nwarps = NWARPS_Q3_K_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q3_K_AMPERE; + mmq_y = MMQ_Y_Q3_K_AMPERE; + nwarps = NWARPS_Q3_K_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q3_K_PASCAL; + mmq_y = MMQ_Y_Q3_K_PASCAL; + nwarps = NWARPS_Q3_K_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q3_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q3_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +#endif +} + +static void ggml_mul_mat_q4_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q4_K_RDNA2; + mmq_y = MMQ_Y_Q4_K_RDNA2; + nwarps = NWARPS_Q4_K_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q4_K_RDNA1; + mmq_y = MMQ_Y_Q4_K_RDNA1; + nwarps = NWARPS_Q4_K_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q4_K_AMPERE; + mmq_y = MMQ_Y_Q4_K_AMPERE; + nwarps = NWARPS_Q4_K_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q4_K_PASCAL; + mmq_y = MMQ_Y_Q4_K_PASCAL; + nwarps = NWARPS_Q4_K_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q4_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q4_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +static void ggml_mul_mat_q5_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q5_K_RDNA2; + mmq_y = MMQ_Y_Q5_K_RDNA2; + nwarps = NWARPS_Q5_K_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q5_K_RDNA1; + mmq_y = MMQ_Y_Q5_K_RDNA1; + nwarps = NWARPS_Q5_K_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q5_K_AMPERE; + mmq_y = MMQ_Y_Q5_K_AMPERE; + nwarps = NWARPS_Q5_K_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q5_K_PASCAL; + mmq_y = MMQ_Y_Q5_K_PASCAL; + nwarps = NWARPS_Q5_K_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q5_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q5_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +static void ggml_mul_mat_q6_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, const int ncols_x, const int nrows_x, + const int ncols_y, const int nrows_y, const int nrows_dst, cudaStream_t stream) { + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + const int compute_capability = ggml_cuda_info().devices[id].cc; + + int mmq_x, mmq_y, nwarps; + if (compute_capability >= CC_RDNA2) { + mmq_x = MMQ_X_Q6_K_RDNA2; + mmq_y = MMQ_Y_Q6_K_RDNA2; + nwarps = NWARPS_Q6_K_RDNA2; + } else if (compute_capability >= CC_OFFSET_AMD) { + mmq_x = MMQ_X_Q6_K_RDNA1; + mmq_y = MMQ_Y_Q6_K_RDNA1; + nwarps = NWARPS_Q6_K_RDNA1; + } else if (compute_capability >= CC_VOLTA) { + mmq_x = MMQ_X_Q6_K_AMPERE; + mmq_y = MMQ_Y_Q6_K_AMPERE; + nwarps = NWARPS_Q6_K_AMPERE; + } else if (compute_capability >= MIN_CC_DP4A) { + mmq_x = MMQ_X_Q6_K_PASCAL; + mmq_y = MMQ_Y_Q6_K_PASCAL; + nwarps = NWARPS_Q6_K_PASCAL; + } else { + GGML_ASSERT(false); + } + + const int block_num_x = (nrows_x + mmq_y - 1) / mmq_y; + const int block_num_y = (ncols_y + mmq_x - 1) / mmq_x; + const dim3 block_nums(block_num_x, block_num_y, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + if (nrows_x % mmq_y == 0) { + const bool need_check = false; + mul_mat_q6_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } else { + const bool need_check = true; + mul_mat_q6_K<<>> + (vx, vy, dst, ncols_x, nrows_x, ncols_y, nrows_y, nrows_dst); + } +} + +void ggml_cuda_op_mul_mat_q( + ggml_backend_cuda_context & ctx, + const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, + const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, + const int64_t src1_padded_row_size, cudaStream_t stream) { + + const int64_t ne00 = src0->ne[0]; + + const int64_t ne10 = src1->ne[0]; + GGML_ASSERT(ne10 % QK8_1 == 0); + + const int64_t ne0 = dst->ne[0]; + + const int64_t row_diff = row_high - row_low; + + int id = ggml_cuda_get_device(); + + // the main device has a larger memory buffer to hold the results from all GPUs + // nrows_dst == nrows of the matrix that the kernel writes into + const int64_t nrows_dst = id == ctx.device ? ne0 : row_diff; + + switch (src0->type) { + case GGML_TYPE_Q4_0: + ggml_mul_mat_q4_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q4_1: + ggml_mul_mat_q4_1_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q5_0: + ggml_mul_mat_q5_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q5_1: + ggml_mul_mat_q5_1_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q8_0: + ggml_mul_mat_q8_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q2_K: + ggml_mul_mat_q2_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q3_K: + ggml_mul_mat_q3_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q4_K: + ggml_mul_mat_q4_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q5_K: + ggml_mul_mat_q5_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + case GGML_TYPE_Q6_K: + ggml_mul_mat_q6_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_ncols, src1_padded_row_size, nrows_dst, stream); + break; + default: + GGML_ASSERT(false); + break; + } + + GGML_UNUSED(src1); + GGML_UNUSED(dst); + GGML_UNUSED(src1_ddf_i); +} + +bool ggml_cuda_supports_mmq(enum ggml_type type) { + switch (type) { + case GGML_TYPE_Q4_0: + case GGML_TYPE_Q4_1: + case GGML_TYPE_Q5_0: + case GGML_TYPE_Q5_1: + case GGML_TYPE_Q8_0: + case GGML_TYPE_Q2_K: + case GGML_TYPE_Q3_K: + case GGML_TYPE_Q4_K: + case GGML_TYPE_Q5_K: + case GGML_TYPE_Q6_K: + return true; + default: + return false; + } +} diff --git a/ggml-cuda/mmq.cuh b/ggml-cuda/mmq.cuh new file mode 100644 index 000000000..807817c4a --- /dev/null +++ b/ggml-cuda/mmq.cuh @@ -0,0 +1,9 @@ +#include "common.cuh" + +void ggml_cuda_op_mul_mat_q( + ggml_backend_cuda_context & ctx, + const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, + const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, + const int64_t src1_padded_row_size, cudaStream_t stream); + +bool ggml_cuda_supports_mmq(enum ggml_type type); diff --git a/ggml-cuda/mmvq.cu b/ggml-cuda/mmvq.cu new file mode 100644 index 000000000..8b2d7a7ff --- /dev/null +++ b/ggml-cuda/mmvq.cu @@ -0,0 +1,395 @@ +#include "mmvq.cuh" +#include "vecdotq.cuh" + +typedef float (*vec_dot_q_cuda_t)(const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs); + +template +#if !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) +// tell the compiler to use as many registers as it wants, see nwarps definition below +__launch_bounds__((ncols_y <= 4 ? 4 : 2)*WARP_SIZE, 1) +#endif // !(defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__)) +static __global__ void mul_mat_vec_q( + const void * __restrict__ vx, const void * __restrict__ vy, float * __restrict__ dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int nrows_dst) { + +#if defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) && (defined(RDNA2) || defined(RDNA3)) + constexpr int nwarps = 1; + constexpr int rows_per_cuda_block = 1; +#else + constexpr int nwarps = ncols_y <= 4 ? 4 : 2; + constexpr int rows_per_cuda_block = ncols_y == 1 ? 1 : 2; +#endif // defined(GGML_USE_HIPBLAS) && defined(__HIP_PLATFORM_AMD__) && !defined(RDNA2) && !defined(RDNA3) + + const int tid = WARP_SIZE*threadIdx.y + threadIdx.x; + const int row0 = rows_per_cuda_block*blockIdx.x; + const int blocks_per_row_x = ncols_x / qk; + const int blocks_per_col_y = nrows_y / QK8_1; + constexpr int blocks_per_iter = vdr * nwarps*WARP_SIZE / qi; + +// partial sum for each thread + float tmp[ncols_y][rows_per_cuda_block] = {0.0f}; + + const block_q_t * x = (const block_q_t *) vx; + const block_q8_1 * y = (const block_q8_1 *) vy; + + for (int kbx = tid / (qi/vdr); kbx < blocks_per_row_x; kbx += blocks_per_iter) { + const int kby = kbx * (qk/QK8_1); // y block index that aligns with kbx + + // x block quant index when casting the quants to int + const int kqs = vdr * (tid % (qi/vdr)); + +#pragma unroll + for (int j = 0; j < ncols_y; ++j) { +#pragma unroll + for (int i = 0; i < rows_per_cuda_block; ++i) { + tmp[j][i] += vec_dot_q_cuda( + &x[kbx + (row0 + i)*blocks_per_row_x], &y[j*blocks_per_col_y + kby], kqs); + } + } + } + + __shared__ float tmp_shared[nwarps-1 > 0 ? nwarps-1 : 1][ncols_y][rows_per_cuda_block][WARP_SIZE]; + if (threadIdx.y > 0) { +#pragma unroll + for (int j = 0; j < ncols_y; ++j) { +#pragma unroll + for (int i = 0; i < rows_per_cuda_block; ++i) { + tmp_shared[threadIdx.y-1][j][i][threadIdx.x] = tmp[j][i]; + } + } + } + __syncthreads(); + if (threadIdx.y > 0) { + return; + } + + // sum up partial sums and write back result +#pragma unroll + for (int j = 0; j < ncols_y; ++j) { +#pragma unroll + for (int i = 0; i < rows_per_cuda_block; ++i) { +#pragma unroll + for (int l = 0; l < nwarps-1; ++l) { + tmp[j][i] += tmp_shared[l][j][i][threadIdx.x]; + } + tmp[j][i] = warp_reduce_sum(tmp[j][i]); + } + + if (threadIdx.x < rows_per_cuda_block) { + dst[j*nrows_dst + row0 + threadIdx.x] = tmp[j][threadIdx.x]; + } + } +} + +template +static void mul_mat_vec_q_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + GGML_ASSERT(ncols_x % qk == 0); + GGML_ASSERT(ncols_y <= MMVQ_MAX_BATCH_SIZE); + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + + int64_t nwarps = 1; + int64_t rows_per_cuda_block = 1; + + if (ggml_cuda_info().devices[id].cc < CC_RDNA2) { // NVIDIA and AMD older than RDNA2 + switch(ncols_y) { + case 1: + nwarps = 4; + rows_per_cuda_block = 1; + break; + case 2: + case 3: + case 4: + nwarps = 4; + rows_per_cuda_block = 2; + break; + case 5: + case 6: + case 7: + case 8: + nwarps = 2; + rows_per_cuda_block = 2; + break; + default: + GGML_ASSERT(false); + break; + } + } + const int64_t nblocks = (nrows_x + rows_per_cuda_block - 1) / rows_per_cuda_block; + const dim3 block_nums(nblocks, 1, 1); + const dim3 block_dims(WARP_SIZE, nwarps, 1); + + switch (ncols_y) { + case 1: + mul_mat_vec_q<1, qk, qi, block_q_t, vdr, vec_dot> + <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); + break; + case 2: + mul_mat_vec_q<2, qk, qi, block_q_t, vdr, vec_dot> + <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); + break; + case 3: + mul_mat_vec_q<3, qk, qi, block_q_t, vdr, vec_dot> + <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); + break; + case 4: + mul_mat_vec_q<4, qk, qi, block_q_t, vdr, vec_dot> + <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); + break; + case 5: + mul_mat_vec_q<5, qk, qi, block_q_t, vdr, vec_dot> + <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); + break; + case 6: + mul_mat_vec_q<6, qk, qi, block_q_t, vdr, vec_dot> + <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); + break; + case 7: + mul_mat_vec_q<7, qk, qi, block_q_t, vdr, vec_dot> + <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); + break; + case 8: + mul_mat_vec_q<8, qk, qi, block_q_t, vdr, vec_dot> + <<>>(vx, vy, dst, ncols_x, nrows_x, nrows_y, nrows_dst); + break; + default: + GGML_ASSERT(false); + break; + } +} + +static void mul_mat_vec_q4_0_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q4_1_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q5_0_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q5_1_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q8_0_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q2_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q3_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q4_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q5_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_q6_K_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_iq2_xxs_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_iq2_xs_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_iq2_s_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_iq3_xxs_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_iq1_s_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_iq4_nl_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_iq4_xs_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +static void mul_mat_vec_iq3_s_q8_1_cuda( + const void * vx, const void * vy, float * dst, + const int ncols_x, const int nrows_x, const int nrows_y, const int ncols_y, const int nrows_dst, cudaStream_t stream) { + + mul_mat_vec_q_cuda + (vx, vy, dst, ncols_x, nrows_x, nrows_y, ncols_y, nrows_dst, stream); +} + +void ggml_cuda_op_mul_mat_vec_q( + ggml_backend_cuda_context & ctx, + const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, + const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, + const int64_t src1_padded_row_size, cudaStream_t stream) { + + const int64_t ne00 = src0->ne[0]; + const int64_t row_diff = row_high - row_low; + + const int64_t ne10 = src1->ne[0]; + GGML_ASSERT(ne10 % QK8_1 == 0); + + const int64_t ne0 = dst->ne[0]; + + int id; + CUDA_CHECK(cudaGetDevice(&id)); + + // the main device has a larger memory buffer to hold the results from all GPUs + // nrows_dst == nrows of the matrix that the kernel writes into + const int64_t nrows_dst = id == ctx.device ? ne0 : row_diff; + + switch (src0->type) { + case GGML_TYPE_Q4_0: + mul_mat_vec_q4_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q4_1: + mul_mat_vec_q4_1_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q5_0: + mul_mat_vec_q5_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q5_1: + mul_mat_vec_q5_1_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q8_0: + mul_mat_vec_q8_0_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q2_K: + mul_mat_vec_q2_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q3_K: + mul_mat_vec_q3_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q4_K: + mul_mat_vec_q4_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q5_K: + mul_mat_vec_q5_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_Q6_K: + mul_mat_vec_q6_K_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_IQ2_XXS: + mul_mat_vec_iq2_xxs_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_IQ2_XS: + mul_mat_vec_iq2_xs_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_IQ2_S: + mul_mat_vec_iq2_s_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_IQ3_XXS: + mul_mat_vec_iq3_xxs_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_IQ1_S: + mul_mat_vec_iq1_s_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_IQ4_NL: + mul_mat_vec_iq4_nl_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_IQ4_XS: + mul_mat_vec_iq4_xs_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + case GGML_TYPE_IQ3_S: + mul_mat_vec_iq3_s_q8_1_cuda(src0_dd_i, src1_ddq_i, dst_dd_i, ne00, row_diff, src1_padded_row_size, src1_ncols, nrows_dst, stream); + break; + default: + GGML_ASSERT(false); + break; + } + + GGML_UNUSED(src1); + GGML_UNUSED(dst); + GGML_UNUSED(src1_ddf_i); + GGML_UNUSED(src1_ncols); + GGML_UNUSED(src1_padded_row_size); +} diff --git a/ggml-cuda/mmvq.cuh b/ggml-cuda/mmvq.cuh new file mode 100644 index 000000000..88c42c4b7 --- /dev/null +++ b/ggml-cuda/mmvq.cuh @@ -0,0 +1,7 @@ +#include "common.cuh" + +void ggml_cuda_op_mul_mat_vec_q( + ggml_backend_cuda_context & ctx, + const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, const char * src0_dd_i, const float * src1_ddf_i, + const char * src1_ddq_i, float * dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, + const int64_t src1_padded_row_size, cudaStream_t stream); diff --git a/ggml-cuda/norm.cu b/ggml-cuda/norm.cu new file mode 100644 index 000000000..86f774534 --- /dev/null +++ b/ggml-cuda/norm.cu @@ -0,0 +1,215 @@ +#include "norm.cuh" + +template +static __global__ void norm_f32(const float * x, float * dst, const int ncols, const float eps) { + const int row = blockIdx.x*blockDim.y + threadIdx.y; + const int tid = threadIdx.x; + + float2 mean_var = make_float2(0.f, 0.f); + + for (int col = tid; col < ncols; col += block_size) { + const float xi = x[row*ncols + col]; + mean_var.x += xi; + mean_var.y += xi * xi; + } + + // sum up partial sums + mean_var = warp_reduce_sum(mean_var); + if (block_size > WARP_SIZE) { + __shared__ float2 s_sum[32]; + int warp_id = threadIdx.x / WARP_SIZE; + int lane_id = threadIdx.x % WARP_SIZE; + if (lane_id == 0) { + s_sum[warp_id] = mean_var; + } + __syncthreads(); + mean_var = s_sum[lane_id]; + mean_var = warp_reduce_sum(mean_var); + } + + const float mean = mean_var.x / ncols; + const float var = mean_var.y / ncols - mean * mean; + const float inv_std = rsqrtf(var + eps); + + for (int col = tid; col < ncols; col += block_size) { + dst[row*ncols + col] = (x[row*ncols + col] - mean) * inv_std; + } +} + +template +static __global__ void group_norm_f32(const float * x, float * dst, const int group_size, const int ne_elements, const float eps) { + // blockIdx.x: num_groups idx + // threadIdx.x: block_size idx + int start = blockIdx.x * group_size; + int end = start + group_size; + + start += threadIdx.x; + + if (end >= ne_elements) { + end = ne_elements; + } + + float tmp = 0.0f; // partial sum for thread in warp + + for (int j = start; j < end; j += block_size) { + tmp += x[j]; + } + + tmp = warp_reduce_sum(tmp); + if (block_size > WARP_SIZE) { + __shared__ float s_sum[32]; + int warp_id = threadIdx.x / WARP_SIZE; + int lane_id = threadIdx.x % WARP_SIZE; + if (lane_id == 0) { + s_sum[warp_id] = tmp; + } + __syncthreads(); + tmp = s_sum[lane_id]; + tmp = warp_reduce_sum(tmp); + } + + float mean = tmp / group_size; + tmp = 0.0f; + + for (int j = start; j < end; j += block_size) { + float xi = x[j] - mean; + dst[j] = xi; + tmp += xi * xi; + } + + tmp = warp_reduce_sum(tmp); + if (block_size > WARP_SIZE) { + __shared__ float s_sum[32]; + int warp_id = threadIdx.x / WARP_SIZE; + int lane_id = threadIdx.x % WARP_SIZE; + if (lane_id == 0) { + s_sum[warp_id] = tmp; + } + __syncthreads(); + tmp = s_sum[lane_id]; + tmp = warp_reduce_sum(tmp); + } + + float variance = tmp / group_size; + float scale = rsqrtf(variance + eps); + for (int j = start; j < end; j += block_size) { + dst[j] *= scale; + } +} + +template +static __global__ void rms_norm_f32(const float * x, float * dst, const int ncols, const float eps) { + const int row = blockIdx.x*blockDim.y + threadIdx.y; + const int tid = threadIdx.x; + + float tmp = 0.0f; // partial sum for thread in warp + + for (int col = tid; col < ncols; col += block_size) { + const float xi = x[row*ncols + col]; + tmp += xi * xi; + } + + // sum up partial sums + tmp = warp_reduce_sum(tmp); + if (block_size > WARP_SIZE) { + __shared__ float s_sum[32]; + int warp_id = threadIdx.x / WARP_SIZE; + int lane_id = threadIdx.x % WARP_SIZE; + if (lane_id == 0) { + s_sum[warp_id] = tmp; + } + __syncthreads(); + tmp = s_sum[lane_id]; + tmp = warp_reduce_sum(tmp); + } + + const float mean = tmp / ncols; + const float scale = rsqrtf(mean + eps); + + for (int col = tid; col < ncols; col += block_size) { + dst[row*ncols + col] = scale * x[row*ncols + col]; + } +} + +static void norm_f32_cuda(const float * x, float * dst, const int ncols, const int nrows, const float eps, cudaStream_t stream) { + GGML_ASSERT(ncols % WARP_SIZE == 0); + if (ncols < 1024) { + const dim3 block_dims(WARP_SIZE, 1, 1); + norm_f32<<>>(x, dst, ncols, eps); + } else { + const dim3 block_dims(1024, 1, 1); + norm_f32<1024><<>>(x, dst, ncols, eps); + } +} + +static void group_norm_f32_cuda(const float * x, float * dst, const int num_groups, const int group_size, const int ne_elements, cudaStream_t stream) { + static const float eps = 1e-6f; + if (group_size < 1024) { + const dim3 block_dims(WARP_SIZE, 1, 1); + group_norm_f32<<>>(x, dst, group_size, ne_elements, eps); + } else { + const dim3 block_dims(1024, 1, 1); + group_norm_f32<1024><<>>(x, dst, group_size, ne_elements, eps); + } +} + +static void rms_norm_f32_cuda(const float * x, float * dst, const int ncols, const int nrows, const float eps, cudaStream_t stream) { + GGML_ASSERT(ncols % WARP_SIZE == 0); + if (ncols < 1024) { + const dim3 block_dims(WARP_SIZE, 1, 1); + rms_norm_f32<<>>(x, dst, ncols, eps); + } else { + const dim3 block_dims(1024, 1, 1); + rms_norm_f32<1024><<>>(x, dst, ncols, eps); + } +} + +void ggml_cuda_op_norm(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + const int64_t ne00 = src0->ne[0]; + const int64_t nrows = ggml_nrows(src0); + + float eps; + memcpy(&eps, dst->op_params, sizeof(float)); + + norm_f32_cuda(src0_d, dst_d, ne00, nrows, eps, stream); +} + +void ggml_cuda_op_group_norm(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + int num_groups = dst->op_params[0]; + int group_size = src0->ne[0] * src0->ne[1] * ((src0->ne[2] + num_groups - 1) / num_groups); + group_norm_f32_cuda(src0_d, dst_d, num_groups * src0->ne[3], group_size, ggml_nelements(src0), stream); +} + +void ggml_cuda_op_rms_norm(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + const int64_t ne00 = src0->ne[0]; + const int64_t nrows = ggml_nrows(src0); + + float eps; + memcpy(&eps, dst->op_params, sizeof(float)); + + rms_norm_f32_cuda(src0_d, dst_d, ne00, nrows, eps, stream); +} diff --git a/ggml-cuda/norm.cuh b/ggml-cuda/norm.cuh new file mode 100644 index 000000000..431a8f74d --- /dev/null +++ b/ggml-cuda/norm.cuh @@ -0,0 +1,7 @@ +#include "common.cuh" + +void ggml_cuda_op_norm(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_group_norm(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_rms_norm(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/pad.cu b/ggml-cuda/pad.cu new file mode 100644 index 000000000..aba539e8d --- /dev/null +++ b/ggml-cuda/pad.cu @@ -0,0 +1,49 @@ +#include "pad.cuh" + +static __global__ void pad_f32(const float * x, float * dst, const int ne0, const int ne00, const int ne01, const int ne02, const int ne03) { + // blockIdx.z: idx of ne2*ne3, aka ne02*ne03 + // blockIdx.y: idx of ne1 + // blockIDx.x: idx of ne0 / BLOCK_SIZE + int nidx = threadIdx.x + blockIdx.x * blockDim.x; + if (nidx >= ne0) { + return; + } + + // operation + int offset_dst = + nidx + + blockIdx.y * ne0 + + blockIdx.z * ne0 * gridDim.y; + if (nidx < ne00 && blockIdx.y < ne01 && blockIdx.z < ne02*ne03) { + int offset_src = + nidx + + blockIdx.y * ne00 + + blockIdx.z * ne00 * ne01; + dst[offset_dst] = x[offset_src]; + } else { + dst[offset_dst] = 0.0f; + } +} + +static void pad_f32_cuda(const float * x, float * dst, + const int ne00, const int ne01, const int ne02, const int ne03, + const int ne0, const int ne1, const int ne2, const int ne3, cudaStream_t stream) { + int num_blocks = (ne0 + CUDA_PAD_BLOCK_SIZE - 1) / CUDA_PAD_BLOCK_SIZE; + dim3 gridDim(num_blocks, ne1, ne2*ne3); + pad_f32<<>>(x, dst, ne0, ne00, ne01, ne02, ne03); +} + +void ggml_cuda_op_pad(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT(dst->type == GGML_TYPE_F32); + GGML_ASSERT(src0->ne[3] == 1 && dst->ne[3] == 1); // just 3D tensors + + pad_f32_cuda(src0_d, dst_d, + src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], + dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3], stream); +} diff --git a/ggml-cuda/pad.cuh b/ggml-cuda/pad.cuh new file mode 100644 index 000000000..8fd386b00 --- /dev/null +++ b/ggml-cuda/pad.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_PAD_BLOCK_SIZE 256 + +void ggml_cuda_op_pad(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/pool2d.cu b/ggml-cuda/pool2d.cu new file mode 100644 index 000000000..c6d51e4d6 --- /dev/null +++ b/ggml-cuda/pool2d.cu @@ -0,0 +1,94 @@ +#include "pool2d.cuh" + +template +static __global__ void pool2d_nchw_kernel( + const int ih, const int iw, const int oh, const int ow, + const int kh, const int kw, const int sh, const int sw, + const int ph, const int pw, const int parallel_elements, + const Ti* src, To* dst, const enum ggml_op_pool op) { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + if (idx >= parallel_elements) { + return; + } + + const int I_HW = ih * iw; + const int O_HW = oh * ow; + const int nc = idx / O_HW; + const int cur_oh = idx % O_HW / ow; + const int cur_ow = idx % O_HW % ow; + const Ti* i_ptr = src + nc * I_HW; + To* o_ptr = dst + nc * O_HW; + const int start_h = cur_oh * sh - ph; + const int bh = max(0, start_h); + const int eh = min(ih, start_h + kh); + const int start_w = cur_ow * sw - pw; + const int bw = max(0, start_w); + const int ew = min(iw, start_w + kw); + const To scale = 1. / (kh * kw); + To res = 0; + + switch (op) { + case GGML_OP_POOL_AVG: res = 0; break; + case GGML_OP_POOL_MAX: res = -FLT_MAX; break; + default: assert(false); + } + + for (int i = bh; i < eh; i += 1) { + for (int j = bw; j < ew; j += 1) { +#if __CUDA_ARCH__ >= 350 + Ti cur = __ldg(i_ptr + i * iw + j); +#else + Ti cur = i_ptr[i * iw + j]; +#endif + switch (op) { + case GGML_OP_POOL_AVG: res += cur * scale; break; + case GGML_OP_POOL_MAX: res = max(res, (To)cur); break; + default: assert(false); + } + } + } + o_ptr[cur_oh * ow + cur_ow] = res; +} + +static void pool2d_nchw_kernel_f32_f32_cuda( + const int ih, const int iw, const int oh, const int ow, + const int kh, const int kw, const int sh, const int sw, + const int ph, const int pw, const int parallel_elements, + const float * src, float * dst, const enum ggml_op_pool op, + cudaStream_t stream) { + + const int num_blocks = (parallel_elements + CUDA_POOL2D_BLOCK_SIZE - 1) / CUDA_POOL2D_BLOCK_SIZE; + dim3 block_nums(num_blocks); + pool2d_nchw_kernel<<>>(ih, iw, oh, ow, kh, kw, sh, sw, ph, pw, parallel_elements, src, dst, op); +} + +void ggml_cuda_op_pool2d(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + const int32_t * opts = (const int32_t *)dst->op_params; + enum ggml_op_pool op = static_cast(opts[0]); + const int k0 = opts[1]; + const int k1 = opts[2]; + const int s0 = opts[3]; + const int s1 = opts[4]; + const int p0 = opts[5]; + const int p1 = opts[6]; + + const int64_t IH = src0->ne[1]; + const int64_t IW = src0->ne[0]; + + const int64_t N = dst->ne[3]; + const int64_t OC = dst->ne[2]; + const int64_t OH = dst->ne[1]; + const int64_t OW = dst->ne[0]; + + const int parallel_elements = N * OC * OH * OW; + + pool2d_nchw_kernel_f32_f32_cuda(IH, IW, OH, OW, k1, k0, s1, s0, p1, p0, parallel_elements, src0_d, dst_d, op, stream); +} diff --git a/ggml-cuda/pool2d.cuh b/ggml-cuda/pool2d.cuh new file mode 100644 index 000000000..7841292bc --- /dev/null +++ b/ggml-cuda/pool2d.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_POOL2D_BLOCK_SIZE 256 + +void ggml_cuda_op_pool2d(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/quantize.cu b/ggml-cuda/quantize.cu new file mode 100644 index 000000000..a1fbc9932 --- /dev/null +++ b/ggml-cuda/quantize.cu @@ -0,0 +1,45 @@ +#include "quantize.cuh" + +static __global__ void quantize_q8_1(const float * __restrict__ x, void * __restrict__ vy, const int kx, const int kx_padded) { + const int ix = blockDim.x*blockIdx.x + threadIdx.x; + + if (ix >= kx_padded) { + return; + } + + const int iy = blockDim.y*blockIdx.y + threadIdx.y; + + const int i_padded = iy*kx_padded + ix; + + block_q8_1 * y = (block_q8_1 *) vy; + + const int ib = i_padded / QK8_1; // block index + const int iqs = i_padded % QK8_1; // quant index + + const float xi = ix < kx ? x[iy*kx + ix] : 0.0f; + float amax = fabsf(xi); + float sum = xi; + + amax = warp_reduce_max(amax); + sum = warp_reduce_sum(sum); + + const float d = amax / 127; + const int8_t q = amax == 0.0f ? 0 : roundf(xi / d); + + y[ib].qs[iqs] = q; + + if (iqs > 0) { + return; + } + + reinterpret_cast(y[ib].ds.x) = d; + reinterpret_cast(y[ib].ds.y) = sum; +} + +void quantize_row_q8_1_cuda(const float * x, void * vy, const int kx, const int ky, const int kx_padded, cudaStream_t stream) { + const int block_num_x = (kx_padded + CUDA_QUANTIZE_BLOCK_SIZE - 1) / CUDA_QUANTIZE_BLOCK_SIZE; + const dim3 num_blocks(block_num_x, ky, 1); + const dim3 block_size(CUDA_QUANTIZE_BLOCK_SIZE, 1, 1); + quantize_q8_1<<>>(x, vy, kx, kx_padded); +} + diff --git a/ggml-cuda/quantize.cuh b/ggml-cuda/quantize.cuh new file mode 100644 index 000000000..adb89c83a --- /dev/null +++ b/ggml-cuda/quantize.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_QUANTIZE_BLOCK_SIZE 256 + +void quantize_row_q8_1_cuda(const float * x, void * vy, const int kx, const int ky, const int kx_padded, cudaStream_t stream); diff --git a/ggml-cuda/rope.cu b/ggml-cuda/rope.cu new file mode 100644 index 000000000..4b0d2e5ad --- /dev/null +++ b/ggml-cuda/rope.cu @@ -0,0 +1,308 @@ +#include "rope.cuh" + +struct rope_corr_dims { + float v[4]; +}; + +static __device__ float rope_yarn_ramp(const float low, const float high, const int i0) { + const float y = (i0 / 2 - low) / max(0.001f, high - low); + return 1.0f - min(1.0f, max(0.0f, y)); +} + +// YaRN algorithm based on LlamaYaRNScaledRotaryEmbedding.py from https://github.com/jquesnelle/yarn +// MIT licensed. Copyright (c) 2023 Jeffrey Quesnelle and Bowen Peng. +static __device__ void rope_yarn( + float theta_extrap, float freq_scale, rope_corr_dims corr_dims, int64_t i0, float ext_factor, float mscale, + float * cos_theta, float * sin_theta +) { + // Get n-d rotational scaling corrected for extrapolation + float theta_interp = freq_scale * theta_extrap; + float theta = theta_interp; + if (ext_factor != 0.0f) { + float ramp_mix = rope_yarn_ramp(corr_dims.v[0], corr_dims.v[1], i0) * ext_factor; + theta = theta_interp * (1 - ramp_mix) + theta_extrap * ramp_mix; + + // Get n-d magnitude scaling corrected for interpolation + mscale *= 1.0f + 0.1f * logf(1.0f / freq_scale); + } + *cos_theta = cosf(theta) * mscale; + *sin_theta = sinf(theta) * mscale; +} + +// rope == RoPE == rotary positional embedding +template +static __global__ void rope( + const T * x, T * dst, int ncols, const int32_t * pos, float freq_scale, int p_delta_rows, float freq_base, + float ext_factor, float attn_factor, rope_corr_dims corr_dims +) { + const int col = 2*(blockDim.y*blockIdx.y + threadIdx.y); + + if (col >= ncols) { + return; + } + + const int row = blockDim.x*blockIdx.x + threadIdx.x; + const int i = row*ncols + col; + const int i2 = row/p_delta_rows; + + const int p = has_pos ? pos[i2] : 0; + const float theta_base = p*powf(freq_base, -float(col)/ncols); + + float cos_theta, sin_theta; + rope_yarn(theta_base, freq_scale, corr_dims, col, ext_factor, attn_factor, &cos_theta, &sin_theta); + + const float x0 = x[i + 0]; + const float x1 = x[i + 1]; + + dst[i + 0] = x0*cos_theta - x1*sin_theta; + dst[i + 1] = x0*sin_theta + x1*cos_theta; +} + +template +static __global__ void rope_neox( + const T * x, T * dst, int ncols, int n_dims, const int32_t * pos, float freq_scale, int p_delta_rows, + float ext_factor, float attn_factor, rope_corr_dims corr_dims, float theta_scale, float inv_ndims +) { + const int col = 2*(blockDim.y*blockIdx.y + threadIdx.y); + + if (col >= ncols) { + return; + } + + const int row = blockDim.x*blockIdx.x + threadIdx.x; + const int ib = col / n_dims; + const int ic = col % n_dims; + + if (ib > 0) { + const int i = row*ncols + ib*n_dims + ic; + + dst[i + 0] = x[i + 0]; + dst[i + 1] = x[i + 1]; + + return; + } + + const int i = row*ncols + ib*n_dims + ic/2; + const int i2 = row/p_delta_rows; + + float cur_rot = inv_ndims * ic - ib; + + const int p = has_pos ? pos[i2] : 0; + const float theta_base = p*freq_scale*powf(theta_scale, col/2.0f); + + float cos_theta, sin_theta; + rope_yarn(theta_base, freq_scale, corr_dims, cur_rot, ext_factor, attn_factor, &cos_theta, &sin_theta); + + const float x0 = x[i + 0]; + const float x1 = x[i + n_dims/2]; + + dst[i + 0] = x0*cos_theta - x1*sin_theta; + dst[i + n_dims/2] = x0*sin_theta + x1*cos_theta; +} + +static __global__ void rope_glm_f32( + const float * x, float * dst, int ncols, const int32_t * pos, float freq_scale, int p_delta_rows, float freq_base, + int n_ctx +) { + const int col = blockDim.x*blockIdx.x + threadIdx.x; + const int half_n_dims = ncols/4; + + if (col >= half_n_dims) { + return; + } + + const int row = blockDim.y*blockIdx.y + threadIdx.y; + const int i = row*ncols + col; + const int i2 = row/p_delta_rows; + + const float col_theta_scale = powf(freq_base, -2.0f*col/ncols); + // FIXME: this is likely wrong + const int p = pos != nullptr ? pos[i2] : 0; + + const float theta = min(p, n_ctx - 2)*freq_scale*col_theta_scale; + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + const float x0 = x[i + 0]; + const float x1 = x[i + half_n_dims]; + + dst[i + 0] = x0*cos_theta - x1*sin_theta; + dst[i + half_n_dims] = x0*sin_theta + x1*cos_theta; + + const float block_theta = ((float)max(p - n_ctx - 2, 0))*col_theta_scale; + const float sin_block_theta = sinf(block_theta); + const float cos_block_theta = cosf(block_theta); + + const float x2 = x[i + half_n_dims * 2]; + const float x3 = x[i + half_n_dims * 3]; + + dst[i + half_n_dims * 2] = x2*cos_block_theta - x3*sin_block_theta; + dst[i + half_n_dims * 3] = x2*sin_block_theta + x3*cos_block_theta; +} + + +template +static void rope_cuda( + const T * x, T * dst, int ncols, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, + float freq_base, float ext_factor, float attn_factor, rope_corr_dims corr_dims, cudaStream_t stream +) { + GGML_ASSERT(ncols % 2 == 0); + const dim3 block_dims(1, CUDA_ROPE_BLOCK_SIZE, 1); + const int num_blocks_x = (ncols + 2*CUDA_ROPE_BLOCK_SIZE - 1) / (2*CUDA_ROPE_BLOCK_SIZE); + const dim3 block_nums(nrows, num_blocks_x, 1); + if (pos == nullptr) { + rope<<>>( + x, dst, ncols, pos, freq_scale, p_delta_rows, freq_base, ext_factor, attn_factor, corr_dims + ); + } else { + rope<<>>( + x, dst, ncols, pos, freq_scale, p_delta_rows, freq_base, ext_factor, attn_factor, corr_dims + ); + } +} + +template +static void rope_neox_cuda( + const T * x, T * dst, int ncols, int n_dims, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, + float freq_base, float ext_factor, float attn_factor, rope_corr_dims corr_dims, cudaStream_t stream +) { + GGML_ASSERT(ncols % 2 == 0); + const dim3 block_dims(1, CUDA_ROPE_BLOCK_SIZE, 1); + const int num_blocks_x = (ncols + 2*CUDA_ROPE_BLOCK_SIZE - 1) / (2*CUDA_ROPE_BLOCK_SIZE); + const dim3 block_nums(nrows, num_blocks_x, 1); + + const float theta_scale = powf(freq_base, -2.0f/n_dims); + const float inv_ndims = -1.0f / n_dims; + + if (pos == nullptr) { + rope_neox<<>>( + x, dst, ncols, n_dims, pos, freq_scale, p_delta_rows, ext_factor, attn_factor, corr_dims, + theta_scale, inv_ndims + ); + } else { + rope_neox<<>>( + x, dst, ncols, n_dims, pos, freq_scale, p_delta_rows, ext_factor, attn_factor, corr_dims, + theta_scale, inv_ndims + ); + } +} + +static void rope_glm_f32_cuda( + const float * x, float * dst, int ncols, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, + float freq_base, int n_ctx, cudaStream_t stream +) { + GGML_ASSERT(ncols % 4 == 0); + const dim3 block_dims(CUDA_ROPE_BLOCK_SIZE/4, 1, 1); + const int num_blocks_x = (ncols + CUDA_ROPE_BLOCK_SIZE - 1) / CUDA_ROPE_BLOCK_SIZE; + const dim3 block_nums(num_blocks_x, nrows, 1); + rope_glm_f32<<>>(x, dst, ncols, pos, freq_scale, p_delta_rows, freq_base, n_ctx); +} + +static void rope_cuda_f16( + const half * x, half * dst, int ncols, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, + float freq_base, float ext_factor, float attn_factor, rope_corr_dims corr_dims, cudaStream_t stream) { + + rope_cuda(x, dst, ncols, nrows, pos, freq_scale, p_delta_rows, freq_base, ext_factor, attn_factor, corr_dims, stream); +} + +static void rope_cuda_f32( + const float * x, float * dst, int ncols, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, + float freq_base, float ext_factor, float attn_factor, rope_corr_dims corr_dims, cudaStream_t stream) { + + rope_cuda(x, dst, ncols, nrows, pos, freq_scale, p_delta_rows, freq_base, ext_factor, attn_factor, corr_dims, stream); +} + +static void rope_neox_cuda_f16( + const half * x, half * dst, int ncols, int n_dims, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, + float freq_base, float ext_factor, float attn_factor, rope_corr_dims corr_dims, cudaStream_t stream) { + + rope_neox_cuda(x, dst, ncols, n_dims, nrows, pos, freq_scale, p_delta_rows, freq_base, ext_factor, attn_factor, corr_dims, stream); +} + +static void rope_neox_cuda_f32( + const float * x, float * dst, int ncols, int n_dims, int nrows, const int32_t * pos, float freq_scale, int p_delta_rows, + float freq_base, float ext_factor, float attn_factor, rope_corr_dims corr_dims, cudaStream_t stream +) { + + rope_neox_cuda(x, dst, ncols, n_dims, nrows, pos, freq_scale, p_delta_rows, freq_base, ext_factor, attn_factor, corr_dims, stream); +} + +void ggml_cuda_op_rope(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + const float * src0_d = (const float *)src0->data; + const float * src1_d = (const float *)src1->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_F16); + GGML_ASSERT( dst->type == GGML_TYPE_F32 || dst->type == GGML_TYPE_F16); + GGML_ASSERT(src0->type == dst->type); + + const int64_t ne00 = src0->ne[0]; + const int64_t ne01 = src0->ne[1]; + const int64_t ne2 = dst->ne[2]; + const int64_t nrows = ggml_nrows(src0); + + //const int n_past = ((int32_t *) dst->op_params)[0]; + const int n_dims = ((int32_t *) dst->op_params)[1]; + const int mode = ((int32_t *) dst->op_params)[2]; + const int n_ctx = ((int32_t *) dst->op_params)[3]; + const int n_orig_ctx = ((int32_t *) dst->op_params)[4]; + + // RoPE alteration for extended context + float freq_base, freq_scale, ext_factor, attn_factor, beta_fast, beta_slow; + memcpy(&freq_base, (int32_t *) dst->op_params + 5, sizeof(float)); + memcpy(&freq_scale, (int32_t *) dst->op_params + 6, sizeof(float)); + memcpy(&ext_factor, (int32_t *) dst->op_params + 7, sizeof(float)); + memcpy(&attn_factor, (int32_t *) dst->op_params + 8, sizeof(float)); + memcpy(&beta_fast, (int32_t *) dst->op_params + 9, sizeof(float)); + memcpy(&beta_slow, (int32_t *) dst->op_params + 10, sizeof(float)); + + const int32_t * pos = nullptr; + if ((mode & 1) == 0) { + GGML_ASSERT(src1->type == GGML_TYPE_I32); + GGML_ASSERT(src1->ne[0] == ne2); + pos = (const int32_t *) src1_d; + } + + const bool is_neox = mode & 2; + const bool is_glm = mode & 4; + + rope_corr_dims corr_dims; + ggml_rope_yarn_corr_dims(n_dims, n_orig_ctx, freq_base, beta_fast, beta_slow, corr_dims.v); + + // compute + if (is_glm) { + GGML_ASSERT(false); + rope_glm_f32_cuda(src0_d, dst_d, ne00, nrows, pos, freq_scale, ne01, freq_base, n_ctx, stream); + } else if (is_neox) { + if (src0->type == GGML_TYPE_F32) { + rope_neox_cuda_f32( + (const float *)src0_d, (float *)dst_d, ne00, n_dims, nrows, pos, freq_scale, ne01, freq_base, ext_factor, + attn_factor, corr_dims, stream + ); + } else if (src0->type == GGML_TYPE_F16) { + rope_neox_cuda_f16( + (const half *)src0_d, (half *)dst_d, ne00, n_dims, nrows, pos, freq_scale, ne01, freq_base, ext_factor, + attn_factor, corr_dims, stream + ); + } else { + GGML_ASSERT(false); + } + } else { + if (src0->type == GGML_TYPE_F32) { + rope_cuda_f32( + (const float *)src0_d, (float *)dst_d, ne00, nrows, pos, freq_scale, ne01, freq_base, ext_factor, + attn_factor, corr_dims, stream + ); + } else if (src0->type == GGML_TYPE_F16) { + rope_cuda_f16( + (const half *)src0_d, (half *)dst_d, ne00, nrows, pos, freq_scale, ne01, freq_base, ext_factor, + attn_factor, corr_dims, stream + ); + } else { + GGML_ASSERT(false); + } + } +} diff --git a/ggml-cuda/rope.cuh b/ggml-cuda/rope.cuh new file mode 100644 index 000000000..0f787a0b2 --- /dev/null +++ b/ggml-cuda/rope.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_ROPE_BLOCK_SIZE 256 + +void ggml_cuda_op_rope(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/scale.cu b/ggml-cuda/scale.cu new file mode 100644 index 000000000..6e3617d1c --- /dev/null +++ b/ggml-cuda/scale.cu @@ -0,0 +1,32 @@ +#include "scale.cuh" + +static __global__ void scale_f32(const float * x, float * dst, const float scale, const int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + + dst[i] = scale * x[i]; +} + +static void scale_f32_cuda(const float * x, float * dst, const float scale, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_SCALE_BLOCK_SIZE - 1) / CUDA_SCALE_BLOCK_SIZE; + scale_f32<<>>(x, dst, scale, k); +} + +void ggml_cuda_op_scale(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + float scale; + memcpy(&scale, dst->op_params, sizeof(float)); + + scale_f32_cuda(src0_d, dst_d, scale, ggml_nelements(src0), stream); + CUDA_CHECK(cudaGetLastError()); +} diff --git a/ggml-cuda/scale.cuh b/ggml-cuda/scale.cuh new file mode 100644 index 000000000..8ff75c829 --- /dev/null +++ b/ggml-cuda/scale.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_SCALE_BLOCK_SIZE 256 + +void ggml_cuda_op_scale(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/softmax.cu b/ggml-cuda/softmax.cu new file mode 100644 index 000000000..9bda18e58 --- /dev/null +++ b/ggml-cuda/softmax.cu @@ -0,0 +1,201 @@ +#include "softmax.cuh" + +template +static __global__ void soft_max_f32(const float * x, const float * mask, const float * pos, float * dst, const int ncols_par, const int nrows_y, const float scale, const float max_bias, const float m0, const float m1, uint32_t n_head_log2) { + const int ncols = ncols_template == 0 ? ncols_par : ncols_template; + + const int tid = threadIdx.x; + const int rowx = blockIdx.x; + const int rowy = rowx % nrows_y; // broadcast the mask in the row dimension + + const int block_size = block_size_template == 0 ? blockDim.x : block_size_template; + + const int warp_id = threadIdx.x / WARP_SIZE; + const int lane_id = threadIdx.x % WARP_SIZE; + + float slope = 0.0f; + + // ALiBi + if (max_bias > 0.0f) { + const int h = rowx/nrows_y; // head index + + const float base = h < n_head_log2 ? m0 : m1; + const int exp = h < n_head_log2 ? h + 1 : 2*(h - n_head_log2) + 1; + + slope = powf(base, exp); + } + + extern __shared__ float data_soft_max_f32[]; + float * buf_iw = data_soft_max_f32; // shared memory buffer for inter-warp communication + // shared memory buffer to cache values between iterations: + float * vals = vals_smem ? buf_iw + WARP_SIZE : dst + rowx*ncols; + + float max_val = -INFINITY; + +#pragma unroll + for (int col0 = 0; col0 < ncols; col0 += block_size) { + const int col = col0 + tid; + + if (ncols_template == 0 && col >= ncols) { + break; + } + + const int ix = rowx*ncols + col; + const int iy = rowy*ncols + col; + + const float val = x[ix]*scale + (mask ? mask[iy] : 0.0f) + (pos ? slope*pos[col] : 0.0f); + + vals[col] = val; + max_val = max(max_val, val); + } + + // find the max value in the block + max_val = warp_reduce_max(max_val); + if (block_size > WARP_SIZE) { + if (warp_id == 0) { + buf_iw[lane_id] = -INFINITY; + } + __syncthreads(); + + if (lane_id == 0) { + buf_iw[warp_id] = max_val; + } + __syncthreads(); + + max_val = buf_iw[lane_id]; + max_val = warp_reduce_max(max_val); + } + + float tmp = 0.0f; // partial sum + +#pragma unroll + for (int col0 = 0; col0 < ncols; col0 += block_size) { + const int col = col0 + tid; + + if (ncols_template == 0 && col >= ncols) { + break; + } + + const float val = expf(vals[col] - max_val); + tmp += val; + vals[col] = val; + } + + // find the sum of exps in the block + tmp = warp_reduce_sum(tmp); + if (block_size > WARP_SIZE) { + __syncthreads(); + if (warp_id == 0) { + buf_iw[lane_id] = 0.0f; + } + __syncthreads(); + + if (lane_id == 0) { + buf_iw[warp_id] = tmp; + } + __syncthreads(); + + tmp = buf_iw[lane_id]; + tmp = warp_reduce_sum(tmp); + } + + const float inv_sum = 1.0f / tmp; + +#pragma unroll + for (int col0 = 0; col0 < ncols; col0 += block_size) { + const int col = col0 + tid; + + if (ncols_template == 0 && col >= ncols) { + return; + } + + const int idst = rowx*ncols + col; + dst[idst] = vals[col] * inv_sum; + } +} + +static void soft_max_f32_cuda(const float * x, const float * mask, const float * pos, float * dst, const int ncols_x, const int nrows_x, const int nrows_y, const float scale, const float max_bias, cudaStream_t stream) { + int nth = WARP_SIZE; + while (nth < ncols_x && nth < CUDA_SOFT_MAX_BLOCK_SIZE) nth *= 2; + const dim3 block_dims(nth, 1, 1); + const dim3 block_nums(nrows_x, 1, 1); + const size_t shmem = (GGML_PAD(ncols_x, WARP_SIZE) + WARP_SIZE)*sizeof(float); + static_assert(CUDA_SOFT_MAX_BLOCK_SIZE == 1024, "These values need to be adjusted."); + + const uint32_t n_head_kv = nrows_x/nrows_y; + const uint32_t n_head_log2 = 1u << (uint32_t) floorf(log2f((float) n_head_kv)); + + const float m0 = powf(2.0f, -(max_bias ) / n_head_log2); + const float m1 = powf(2.0f, -(max_bias / 2.0f) / n_head_log2); + + if (shmem < ggml_cuda_info().devices[ggml_cuda_get_device()].smpb) { + switch (ncols_x) { + case 32: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + case 64: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + case 128: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + case 256: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + case 512: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + case 1024: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + case 2048: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + case 4096: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + default: + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + break; + } + } else { + const size_t shmem_low = WARP_SIZE*sizeof(float); + soft_max_f32<<>>(x, mask, pos, dst, ncols_x, nrows_y, scale, max_bias, m0, m1, n_head_log2); + } +} + +void ggml_cuda_op_soft_max(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + const float * src0_d = (const float *)src0->data; + const float * src1_d = src1 ? (const float *)src1->data : nullptr; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + GGML_ASSERT(!src1 || src1->type == GGML_TYPE_F32); // src1 contains mask and it is optional + + const int64_t ne00 = src0->ne[0]; + const int64_t nrows_x = ggml_nrows(src0); + const int64_t nrows_y = src0->ne[1]; + + float scale = 1.0f; + float max_bias = 0.0f; + + memcpy(&scale, (float *) dst->op_params + 0, sizeof(float)); + memcpy(&max_bias, (float *) dst->op_params + 1, sizeof(float)); + + // positions tensor + float * src2_dd = nullptr; + + ggml_tensor * src2 = dst->src[2]; + const bool use_src2 = src2 != nullptr; + + if (use_src2) { + src2_dd = (float *)src2->data; + } + + soft_max_f32_cuda(src0_d, src1_d, src2_dd, dst_d, ne00, nrows_x, nrows_y, scale, max_bias, stream); +} diff --git a/ggml-cuda/softmax.cuh b/ggml-cuda/softmax.cuh new file mode 100644 index 000000000..4ef4ff86c --- /dev/null +++ b/ggml-cuda/softmax.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_SOFT_MAX_BLOCK_SIZE 1024 + +void ggml_cuda_op_soft_max(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/sumrows.cu b/ggml-cuda/sumrows.cu new file mode 100644 index 000000000..82e8e875f --- /dev/null +++ b/ggml-cuda/sumrows.cu @@ -0,0 +1,40 @@ +#include "sumrows.cuh" + +static __global__ void k_sum_rows_f32(const float * x, float * dst, const int ncols) { + const int row = blockIdx.x; + const int col = threadIdx.x; + + float sum = 0.0f; + for (int i = col; i < ncols; i += blockDim.x) { + sum += x[row * ncols + i]; + } + + sum = warp_reduce_sum(sum); + + if (col == 0) { + dst[row] = sum; + } +} + +static void sum_rows_f32_cuda(const float * x, float * dst, const int ncols, const int nrows, cudaStream_t stream) { + const dim3 block_dims(WARP_SIZE, 1, 1); + const dim3 block_nums(nrows, 1, 1); + k_sum_rows_f32<<>>(x, dst, ncols); +} + +void ggml_cuda_op_sum_rows(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + GGML_ASSERT(ggml_is_contiguous(src0)); + + + const int64_t ncols = src0->ne[0]; + const int64_t nrows = ggml_nrows(src0); + + sum_rows_f32_cuda(src0_d, dst_d, ncols, nrows, stream); +} diff --git a/ggml-cuda/sumrows.cuh b/ggml-cuda/sumrows.cuh new file mode 100644 index 000000000..e7545f83c --- /dev/null +++ b/ggml-cuda/sumrows.cuh @@ -0,0 +1,3 @@ +#include "common.cuh" + +void ggml_cuda_op_sum_rows(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/tsembd.cu b/ggml-cuda/tsembd.cu new file mode 100644 index 000000000..153ddbcda --- /dev/null +++ b/ggml-cuda/tsembd.cu @@ -0,0 +1,47 @@ +#include "tsembd.cuh" + +static __global__ void timestep_embedding_f32(const float * timesteps, float * dst, const int nb1, const int dim, const int max_period) { + // blockIDx.y: idx of timesteps->ne[0] + // blockIDx.x: idx of ((dim + 1) / 2) / BLOCK_SIZE + int i = blockIdx.y; + int j = threadIdx.x + blockIdx.x * blockDim.x; + float * embed_data = (float *)((char *)dst + i*nb1); + + if (dim % 2 != 0 && j == ((dim + 1) / 2)) { + embed_data[dim] = 0.f; + } + + int half = dim / 2; + if (j >= half) { + return; + } + + float timestep = timesteps[i]; + float freq = (float)expf(-logf(max_period) * j / half); + float arg = timestep * freq; + embed_data[j] = cosf(arg); + embed_data[j + half] = sinf(arg); +} + +static void timestep_embedding_f32_cuda(const float * x, float * dst, const int ne00, const int nb1, + const int dim, const int max_period, cudaStream_t stream) { + int half_ceil = (dim + 1) / 2; + int num_blocks = (half_ceil + CUDA_TIMESTEP_EMBEDDING_BLOCK_SIZE - 1) / CUDA_TIMESTEP_EMBEDDING_BLOCK_SIZE; + dim3 gridDim(num_blocks, ne00, 1); + timestep_embedding_f32<<>>(x, dst, nb1, dim, max_period); +} + +void ggml_cuda_op_timestep_embedding(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT(dst->type == GGML_TYPE_F32); + + const int dim = dst->op_params[0]; + const int max_period = dst->op_params[1]; + + timestep_embedding_f32_cuda(src0_d, dst_d, src0->ne[0], dst->nb[1], dim, max_period, stream); +} diff --git a/ggml-cuda/tsembd.cuh b/ggml-cuda/tsembd.cuh new file mode 100644 index 000000000..84340e3d7 --- /dev/null +++ b/ggml-cuda/tsembd.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_TIMESTEP_EMBEDDING_BLOCK_SIZE 256 + +void ggml_cuda_op_timestep_embedding(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/unary.cu b/ggml-cuda/unary.cu new file mode 100644 index 000000000..1a7f09469 --- /dev/null +++ b/ggml-cuda/unary.cu @@ -0,0 +1,240 @@ +#include "unary.cuh" + +static __global__ void gelu_f32(const float * x, float * dst, const int k) { + const float GELU_COEF_A = 0.044715f; + const float SQRT_2_OVER_PI = 0.79788456080286535587989211986876f; + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + + float xi = x[i]; + dst[i] = 0.5f*xi*(1.0f + tanhf(SQRT_2_OVER_PI*xi*(1.0f + GELU_COEF_A*xi*xi))); +} + +static __global__ void gelu_quick_f32(const float * x, float * dst, int k) { + const float GELU_QUICK_COEF = -1.702f; + const int i = blockDim.x*blockIdx.x + threadIdx.x; + if (i >= k) { + return; + } + dst[i] = x[i] * (1.0f / (1.0f + expf(GELU_QUICK_COEF * x[i]))); +} + +static __global__ void silu_f32(const float * x, float * dst, const int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + dst[i] = x[i] / (1.0f + expf(-x[i])); +} + +static __global__ void tanh_f32(const float * x, float * dst, int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + if (i >= k) { + return; + } + dst[i] = tanhf(x[i]); +} + +static __global__ void relu_f32(const float * x, float * dst, const int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + dst[i] = fmaxf(x[i], 0); +} + +static __global__ void hardsigmoid_f32(const float * x, float * dst, const int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + dst[i] = fminf(1.0f, fmaxf(0.0f, (x[i] + 3.0f) / 6.0f)); +} + +static __global__ void hardswish_f32(const float * x, float * dst, const int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + dst[i] = x[i] * fminf(1.0f, fmaxf(0.0f, (x[i] + 3.0f) / 6.0f)); +} + +static __global__ void leaky_relu_f32(const float * x, float * dst, const int k, const float negative_slope) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + if (i >= k) { + return; + } + dst[i] = fmaxf(x[i], 0) + fminf(x[i], 0.0f) * negative_slope; +} + +static __global__ void sqr_f32(const float * x, float * dst, const int k) { + const int i = blockDim.x*blockIdx.x + threadIdx.x; + + if (i >= k) { + return; + } + dst[i] = x[i] * x[i]; +} + +static void gelu_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_GELU_BLOCK_SIZE - 1) / CUDA_GELU_BLOCK_SIZE; + gelu_f32<<>>(x, dst, k); +} + +static void gelu_quick_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_GELU_BLOCK_SIZE - 1) / CUDA_GELU_BLOCK_SIZE; + gelu_quick_f32<<>>(x, dst, k); +} + +static void silu_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_SILU_BLOCK_SIZE - 1) / CUDA_SILU_BLOCK_SIZE; + silu_f32<<>>(x, dst, k); +} + +static void tanh_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_TANH_BLOCK_SIZE - 1) / CUDA_TANH_BLOCK_SIZE; + tanh_f32<<>>(x, dst, k); +} + +static void relu_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_RELU_BLOCK_SIZE - 1) / CUDA_RELU_BLOCK_SIZE; + relu_f32<<>>(x, dst, k); +} + +static void hardsigmoid_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_HARDSIGMOID_BLOCK_SIZE - 1) / CUDA_HARDSIGMOID_BLOCK_SIZE; + hardsigmoid_f32<<>>(x, dst, k); +} + +static void hardswish_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_HARDSWISH_BLOCK_SIZE - 1) / CUDA_HARDSWISH_BLOCK_SIZE; + hardswish_f32<<>>(x, dst, k); +} + +static void leaky_relu_f32_cuda(const float * x, float * dst, const int k, const float negative_slope, cudaStream_t stream) { + const int num_blocks = (k + CUDA_RELU_BLOCK_SIZE - 1) / CUDA_RELU_BLOCK_SIZE; + leaky_relu_f32<<>>(x, dst, k, negative_slope); +} + +static void sqr_f32_cuda(const float * x, float * dst, const int k, cudaStream_t stream) { + const int num_blocks = (k + CUDA_SQR_BLOCK_SIZE - 1) / CUDA_SQR_BLOCK_SIZE; + sqr_f32<<>>(x, dst, k); +} + +void ggml_cuda_op_gelu(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + gelu_f32_cuda(src0_d, dst_d, ggml_nelements(src0), stream); +} + +void ggml_cuda_op_silu(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + silu_f32_cuda(src0_d, dst_d, ggml_nelements(src0), stream); +} + +void ggml_cuda_op_gelu_quick(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + gelu_quick_f32_cuda(src0_d, dst_d, ggml_nelements(src0), stream); +} + +void ggml_cuda_op_tanh(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + tanh_f32_cuda(src0_d, dst_d, ggml_nelements(src0), stream); +} + +void ggml_cuda_op_relu(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + relu_f32_cuda(src0_d, dst_d, ggml_nelements(src0), stream); +} + +void ggml_cuda_op_hardsigmoid(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + hardsigmoid_f32_cuda(src0_d, dst_d, ggml_nelements(src0), stream); +} + +void ggml_cuda_op_hardswish(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + hardswish_f32_cuda(src0_d, dst_d, ggml_nelements(src0), stream); +} + +void ggml_cuda_op_leaky_relu(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + float negative_slope; + memcpy(&negative_slope, dst->op_params, sizeof(float)); + + leaky_relu_f32_cuda(src0_d, dst_d, ggml_nelements(src0), negative_slope, stream); +} + +void ggml_cuda_op_sqr(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT( dst->type == GGML_TYPE_F32); + + sqr_f32_cuda(src0_d, dst_d, ggml_nelements(src0), stream); +} diff --git a/ggml-cuda/unary.cuh b/ggml-cuda/unary.cuh new file mode 100644 index 000000000..2002ed989 --- /dev/null +++ b/ggml-cuda/unary.cuh @@ -0,0 +1,27 @@ +#include "common.cuh" + +#define CUDA_GELU_BLOCK_SIZE 256 +#define CUDA_SILU_BLOCK_SIZE 256 +#define CUDA_TANH_BLOCK_SIZE 256 +#define CUDA_RELU_BLOCK_SIZE 256 +#define CUDA_HARDSIGMOID_BLOCK_SIZE 256 +#define CUDA_HARDSWISH_BLOCK_SIZE 256 +#define CUDA_SQR_BLOCK_SIZE 256 + +void ggml_cuda_op_gelu(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_silu(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_gelu_quick(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_tanh(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_relu(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_hardsigmoid(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_hardswish(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_leaky_relu(ggml_backend_cuda_context & ctx, ggml_tensor * dst); + +void ggml_cuda_op_sqr(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/upscale.cu b/ggml-cuda/upscale.cu new file mode 100644 index 000000000..2f62fed48 --- /dev/null +++ b/ggml-cuda/upscale.cu @@ -0,0 +1,48 @@ +#include "upscale.cuh" + +static __global__ void upscale_f32(const float * x, float * dst, const int ne00, const int ne00xne01, const int scale_factor) { + // blockIdx.z: idx of ne02*ne03 + // blockIdx.y: idx of ne01*scale_factor, aka ne1 + // blockIDx.x: idx of ne00*scale_factor / BLOCK_SIZE + // ne00xne01: ne00 * ne01 + int ne0 = ne00 * scale_factor; + int nidx = threadIdx.x + blockIdx.x * blockDim.x; + if (nidx >= ne0) { + return; + } + // operation + int i00 = nidx / scale_factor; + int i01 = blockIdx.y / scale_factor; + int offset_src = + i00 + + i01 * ne00 + + blockIdx.z * ne00xne01; + int offset_dst = + nidx + + blockIdx.y * ne0 + + blockIdx.z * ne0 * gridDim.y; + dst[offset_dst] = x[offset_src]; +} + +static void upscale_f32_cuda(const float * x, float * dst, const int ne00, const int ne01, const int ne02, const int ne03, + const int scale_factor, cudaStream_t stream) { + int ne0 = (ne00 * scale_factor); + int num_blocks = (ne0 + CUDA_UPSCALE_BLOCK_SIZE - 1) / CUDA_UPSCALE_BLOCK_SIZE; + dim3 gridDim(num_blocks, (ne01 * scale_factor), ne02*ne03); + upscale_f32<<>>(x, dst, ne00, ne00 * ne01, scale_factor); +} + +void ggml_cuda_op_upscale(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { + const ggml_tensor * src0 = dst->src[0]; + const float * src0_d = (const float *)src0->data; + float * dst_d = (float *)dst->data; + cudaStream_t stream = ctx.stream(); + + GGML_ASSERT(src0->type == GGML_TYPE_F32); + GGML_ASSERT(dst->type == GGML_TYPE_F32); + GGML_ASSERT(src0->ne[3] == 1 && dst->ne[3] == 1); // just 3D tensors + + const int scale_factor = dst->op_params[0]; + + upscale_f32_cuda(src0_d, dst_d, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], scale_factor, stream); +} diff --git a/ggml-cuda/upscale.cuh b/ggml-cuda/upscale.cuh new file mode 100644 index 000000000..d4d765230 --- /dev/null +++ b/ggml-cuda/upscale.cuh @@ -0,0 +1,5 @@ +#include "common.cuh" + +#define CUDA_UPSCALE_BLOCK_SIZE 256 + +void ggml_cuda_op_upscale(ggml_backend_cuda_context & ctx, ggml_tensor * dst); diff --git a/ggml-cuda/vecdotq.cuh b/ggml-cuda/vecdotq.cuh new file mode 100644 index 000000000..d911d851d --- /dev/null +++ b/ggml-cuda/vecdotq.cuh @@ -0,0 +1,1284 @@ +#include "common.cuh" + +static __device__ __forceinline__ int get_int_from_int8(const int8_t * x8, const int & i32) { + const uint16_t * x16 = (const uint16_t *) (x8 + sizeof(int) * i32); // assume at least 2 byte alignment + + int x32 = 0; + x32 |= x16[0] << 0; + x32 |= x16[1] << 16; + + return x32; +} + +static __device__ __forceinline__ int get_int_from_uint8(const uint8_t * x8, const int & i32) { + const uint16_t * x16 = (const uint16_t *) (x8 + sizeof(int) * i32); // assume at least 2 byte alignment + + int x32 = 0; + x32 |= x16[0] << 0; + x32 |= x16[1] << 16; + + return x32; +} + +static __device__ __forceinline__ int get_int_from_int8_aligned(const int8_t * x8, const int & i32) { + return *((const int *) (x8 + sizeof(int) * i32)); // assume at least 4 byte alignment +} + +static __device__ __forceinline__ int get_int_from_uint8_aligned(const uint8_t * x8, const int & i32) { + return *((const int *) (x8 + sizeof(int) * i32)); // assume at least 4 byte alignment +} + + +// VDR = vec dot ratio, how many contiguous integers each thread processes when the vec dot kernel is called +// MMVQ = mul_mat_vec_q, MMQ = mul_mat_q + +#define VDR_Q4_0_Q8_1_MMVQ 2 +#define VDR_Q4_0_Q8_1_MMQ 4 + +template static __device__ __forceinline__ float vec_dot_q4_0_q8_1_impl( + const int * v, const int * u, const float & d4, const half2 & ds8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + int sumi = 0; + +#pragma unroll + for (int i = 0; i < vdr; ++i) { + const int vi0 = (v[i] >> 0) & 0x0F0F0F0F; + const int vi1 = (v[i] >> 4) & 0x0F0F0F0F; + + // SIMD dot product of quantized values + sumi = __dp4a(vi0, u[2*i+0], sumi); + sumi = __dp4a(vi1, u[2*i+1], sumi); + } + + const float2 ds8f = __half22float2(ds8); + + // second part effectively subtracts 8 from each quant value + return d4 * (sumi * ds8f.x - (8*vdr/QI4_0) * ds8f.y); +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q4_1_Q8_1_MMVQ 2 +#define VDR_Q4_1_Q8_1_MMQ 4 + +template static __device__ __forceinline__ float vec_dot_q4_1_q8_1_impl( + const int * v, const int * u, const half2 & dm4, const half2 & ds8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + int sumi = 0; + +#pragma unroll + for (int i = 0; i < vdr; ++i) { + const int vi0 = (v[i] >> 0) & 0x0F0F0F0F; + const int vi1 = (v[i] >> 4) & 0x0F0F0F0F; + + // SIMD dot product of quantized values + sumi = __dp4a(vi0, u[2*i+0], sumi); + sumi = __dp4a(vi1, u[2*i+1], sumi); + } + +#ifdef GGML_CUDA_F16 + const float2 tmp = __half22float2(__hmul2(dm4, ds8)); + const float d4d8 = tmp.x; + const float m4s8 = tmp.y; +#else + const float2 dm4f = __half22float2(dm4); + const float2 ds8f = __half22float2(ds8); + const float d4d8 = dm4f.x * ds8f.x; + const float m4s8 = dm4f.y * ds8f.y; +#endif // GGML_CUDA_F16 + + // scale second part of sum by QI8_1/(vdr * QR4_1) to compensate for multiple threads adding it + return sumi * d4d8 + m4s8 / (QI8_1 / (vdr * QR4_1)); +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q5_0_Q8_1_MMVQ 2 +#define VDR_Q5_0_Q8_1_MMQ 4 + +template static __device__ __forceinline__ float vec_dot_q5_0_q8_1_impl( + const int * vl, const int * vh, const int * u, const float & d5, const half2 & ds8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + int sumi = 0; + +#pragma unroll + for (int i = 0; i < vdr; ++i) { + int vi0 = (vl[i] >> 0) & 0x0F0F0F0F; // lower 4 qs bits, still need qh as 5th bits + vi0 |= (vh[i] << 4) & 0x00000010; // 0 -> 4 + vi0 |= (vh[i] << 11) & 0x00001000; // 1 -> 12 + vi0 |= (vh[i] << 18) & 0x00100000; // 2 -> 20 + vi0 |= (vh[i] << 25) & 0x10000000; // 3 -> 28 + sumi = __dp4a(vi0, u[2*i+0], sumi); // SIMD dot product of quantized values + + int vi1 = (vl[i] >> 4) & 0x0F0F0F0F; // upper 4 qs bits, still need qh as 5th bits + vi1 |= (vh[i] >> 12) & 0x00000010; // 16 -> 4 + vi1 |= (vh[i] >> 5) & 0x00001000; // 17 -> 12 + vi1 |= (vh[i] << 2) & 0x00100000; // 18 -> 20 + vi1 |= (vh[i] << 9) & 0x10000000; // 19 -> 28 + sumi = __dp4a(vi1, u[2*i+1], sumi); // SIMD dot product of quantized values + } + + const float2 ds8f = __half22float2(ds8); + + // second part effectively subtracts 16 from each quant value + return d5 * (sumi * ds8f.x - (16*vdr/QI5_0) * ds8f.y); +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q5_1_Q8_1_MMVQ 2 +#define VDR_Q5_1_Q8_1_MMQ 4 + +template static __device__ __forceinline__ float vec_dot_q5_1_q8_1_impl( + const int * vl, const int * vh, const int * u, const half2 & dm5, const half2 & ds8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + int sumi = 0; + +#pragma unroll + for (int i = 0; i < vdr; ++i) { + int vi0 = (vl[i] >> 0) & 0x0F0F0F0F; // lower 4 qs bits, still need qh as 5th bits + vi0 |= (vh[i] << 4) & 0x00000010; // 0 -> 4 + vi0 |= (vh[i] << 11) & 0x00001000; // 1 -> 12 + vi0 |= (vh[i] << 18) & 0x00100000; // 2 -> 20 + vi0 |= (vh[i] << 25) & 0x10000000; // 3 -> 28 + sumi = __dp4a(vi0, u[2*i+0], sumi); // SIMD dot product of quantized values + + int vi1 = (vl[i] >> 4) & 0x0F0F0F0F; // upper 4 qs bits, still need qh as 5th bits + vi1 |= (vh[i] >> 12) & 0x00000010; // 16 -> 4 + vi1 |= (vh[i] >> 5) & 0x00001000; // 17 -> 12 + vi1 |= (vh[i] << 2) & 0x00100000; // 18 -> 20 + vi1 |= (vh[i] << 9) & 0x10000000; // 19 -> 28 + sumi = __dp4a(vi1, u[2*i+1], sumi); // SIMD dot product of quantized values + } + +#ifdef GGML_CUDA_F16 + const float2 tmp = __half22float2(__hmul2(dm5, ds8)); + const float d5d8 = tmp.x; + const float m5s8 = tmp.y; +#else + const float2 dm5f = __half22float2(dm5); + const float2 ds8f = __half22float2(ds8); + const float d5d8 = dm5f.x * ds8f.x; + const float m5s8 = dm5f.y * ds8f.y; +#endif // GGML_CUDA_F16 + + // scale second part of sum by QI5_1 / vdr to compensate for multiple threads adding it + return sumi*d5d8 + m5s8 / (QI5_1 / vdr); + +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q8_0_Q8_1_MMVQ 2 +#define VDR_Q8_0_Q8_1_MMQ 8 + +template static __device__ __forceinline__ float vec_dot_q8_0_q8_1_impl( + const int * v, const int * u, const float & d8_0, const float & d8_1) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + int sumi = 0; + +#pragma unroll + for (int i = 0; i < vdr; ++i) { + // SIMD dot product of quantized values + sumi = __dp4a(v[i], u[i], sumi); + } + + return d8_0*d8_1 * sumi; +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +template static __device__ __forceinline__ float vec_dot_q8_1_q8_1_impl( + const int * v, const int * u, const half2 & dm8, const half2 & ds8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + int sumi = 0; + +#pragma unroll + for (int i = 0; i < vdr; ++i) { + // SIMD dot product of quantized values + sumi = __dp4a(v[i], u[i], sumi); + } + +#ifdef GGML_CUDA_F16 + const float2 tmp = __half22float2(__hmul2(dm8, ds8)); + const float d8d8 = tmp.x; + const float m8s8 = tmp.y; +#else + const float2 dm8f = __half22float2(dm8); + const float2 ds8f = __half22float2(ds8); + const float d8d8 = dm8f.x * ds8f.x; + const float m8s8 = dm8f.y * ds8f.y; +#endif // GGML_CUDA_F16 + + // scale second part of sum by QI8_1/ vdr to compensate for multiple threads adding it + return sumi*d8d8 + m8s8 / (QI8_1 / vdr); +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q2_K_Q8_1_MMVQ 1 +#define VDR_Q2_K_Q8_1_MMQ 2 + +// contiguous v/x values +static __device__ __forceinline__ float vec_dot_q2_K_q8_1_impl_mmvq( + const int & v, const int * __restrict__ u, const uint8_t * __restrict__ scales, + const half2 & dm2, const float * __restrict__ d8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + float sumf_d = 0.0f; + float sumf_m = 0.0f; + +#pragma unroll + for (int i = 0; i < QR2_K; ++i) { + const int sc = scales[2*i]; + + const int vi = (v >> (2*i)) & 0x03030303; + + sumf_d += d8[i] * (__dp4a(vi, u[i], 0) * (sc & 0xF)); // SIMD dot product + + // fill int with 4x m + int m = sc >> 4; + m |= m << 8; + m |= m << 16; + sumf_m += d8[i] * __dp4a(m, u[i], 0); // multiply constant q2_K part with sum of q8_1 values + } + + const float2 dm2f = __half22float2(dm2); + + return dm2f.x*sumf_d - dm2f.y*sumf_m; +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +// contiguous u/y values +static __device__ __forceinline__ float vec_dot_q2_K_q8_1_impl_mmq( + const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ scales, + const half2 & dm2, const float & d8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + int sumi_d = 0; + int sumi_m = 0; + +#pragma unroll + for (int i0 = 0; i0 < QI8_1; i0 += QI8_1/2) { + int sumi_d_sc = 0; + + const int sc = scales[i0 / (QI8_1/2)]; + + // fill int with 4x m + int m = sc >> 4; + m |= m << 8; + m |= m << 16; + +#pragma unroll + for (int i = i0; i < i0 + QI8_1/2; ++i) { + sumi_d_sc = __dp4a(v[i], u[i], sumi_d_sc); // SIMD dot product + sumi_m = __dp4a(m, u[i], sumi_m); // multiply sum of q8_1 values with m + } + + sumi_d += sumi_d_sc * (sc & 0xF); + } + + const float2 dm2f = __half22float2(dm2); + + return d8 * (dm2f.x*sumi_d - dm2f.y*sumi_m); +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q3_K_Q8_1_MMVQ 1 +#define VDR_Q3_K_Q8_1_MMQ 2 + +// contiguous v/x values +static __device__ __forceinline__ float vec_dot_q3_K_q8_1_impl_mmvq( + const int & vl, const int & vh, const int * __restrict__ u, const uint8_t * __restrict__ scales, + const int & scale_offset, const float & d3, const float * __restrict__ d8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + float sumf = 0.0f; + +#pragma unroll + for (int i = 0; i < QR3_K; ++i) { + const int isc = scale_offset + 2*i; + + const int isc_low = isc % (QK_K/32); + const int sc_shift_low = 4 * (isc / (QK_K/32)); + const int sc_low = (scales[isc_low] >> sc_shift_low) & 0xF; + + const int isc_high = isc % (QK_K/64); + const int sc_shift_high = 2 * (isc / (QK_K/64)); + const int sc_high = ((scales[(QK_K/32) + isc_high] >> sc_shift_high) & 3) << 4; + + const int sc = (sc_low | sc_high) - 32; + + const int vil = (vl >> (2*i)) & 0x03030303; + + const int vih = ((vh >> i) << 2) & 0x04040404; + + const int vi = __vsubss4(vil, vih); + + sumf += d8[i] * (__dp4a(vi, u[i], 0) * sc); // SIMD dot product + } + + return d3 * sumf; +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +// contiguous u/y values +static __device__ __forceinline__ float vec_dot_q3_K_q8_1_impl_mmq( + const int * __restrict__ v, const int * __restrict__ u, const int8_t * __restrict__ scales, + const float & d3, const float & d8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + int sumi = 0; + +#pragma unroll + for (int i0 = 0; i0 < QR3_K*VDR_Q3_K_Q8_1_MMQ; i0 += QI8_1/2) { + int sumi_sc = 0; + + for (int i = i0; i < i0 + QI8_1/2; ++i) { + sumi_sc = __dp4a(v[i], u[i], sumi_sc); // SIMD dot product + } + + sumi += sumi_sc * scales[i0 / (QI8_1/2)]; + } + + return d3*d8 * sumi; +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q4_K_Q8_1_MMVQ 2 +#define VDR_Q4_K_Q8_1_MMQ 8 + +// contiguous v/x values +static __device__ __forceinline__ float vec_dot_q4_K_q8_1_impl_vmmq( + const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ sc, + const uint8_t * __restrict__ m, const half2 & dm4, const float * __restrict__ d8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + float sumf_d = 0.0f; + float sumf_m = 0.0f; + +#pragma unroll + for (int i = 0; i < QR4_K; ++i) { + const int v0i = (v[0] >> (4*i)) & 0x0F0F0F0F; + const int v1i = (v[1] >> (4*i)) & 0x0F0F0F0F; + + const int dot1 = __dp4a(v1i, u[2*i+1], __dp4a(v0i, u[2*i+0], 0)); // SIMD dot product + const int dot2 = __dp4a(0x01010101, u[2*i+1], __dp4a(0x01010101, u[2*i+0], 0)); // sum of u + + sumf_d += d8[i] * (dot1 * sc[i]); + sumf_m += d8[i] * (dot2 * m[i]); // multiply constant part of q4_K with sum of q8_1 values + } + + const float2 dm4f = __half22float2(dm4); + + return dm4f.x*sumf_d - dm4f.y*sumf_m; + +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +// contiguous u/y values +static __device__ __forceinline__ float vec_dot_q4_K_q8_1_impl_mmq( + const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ sc, + const uint8_t * __restrict__ m, const half2 & dm4, const half2 * __restrict__ ds8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + float sumf_d = 0.0f; + float sumf_m = 0.0f; + +#pragma unroll + for (int i = 0; i < QR4_K*VDR_Q4_K_Q8_1_MMQ/QI8_1; ++i) { + int sumi_d = 0; + +#pragma unroll + for (int j = 0; j < QI8_1; ++j) { + sumi_d = __dp4a((v[j] >> (4*i)) & 0x0F0F0F0F, u[i*QI8_1 + j], sumi_d); // SIMD dot product + } + + const float2 ds8f = __half22float2(ds8[i]); + + sumf_d += ds8f.x * (sc[i] * sumi_d); + sumf_m += ds8f.y * m[i]; // sum of q8_1 block * q4_K min val + } + + const float2 dm4f = __half22float2(dm4); + + return dm4f.x*sumf_d - dm4f.y*sumf_m; + +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q5_K_Q8_1_MMVQ 2 +#define VDR_Q5_K_Q8_1_MMQ 8 + +// contiguous v/x values +static __device__ __forceinline__ float vec_dot_q5_K_q8_1_impl_vmmq( + const int * __restrict__ vl, const int * __restrict__ vh, const int * __restrict__ u, const uint8_t * __restrict__ sc, + const uint8_t * __restrict__ m, const half2 & dm5, const float * __restrict__ d8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + float sumf_d = 0.0f; + float sumf_m = 0.0f; + +#pragma unroll + for (int i = 0; i < QR5_K; ++i) { + const int vl0i = (vl[0] >> (4*i)) & 0x0F0F0F0F; + const int vl1i = (vl[1] >> (4*i)) & 0x0F0F0F0F; + + const int vh0i = ((vh[0] >> i) << 4) & 0x10101010; + const int vh1i = ((vh[1] >> i) << 4) & 0x10101010; + + const int v0i = vl0i | vh0i; + const int v1i = vl1i | vh1i; + + const int dot1 = __dp4a(v0i, u[2*i+0], __dp4a(v1i, u[2*i+1], 0)); // SIMD dot product + const int dot2 = __dp4a(0x01010101, u[2*i+0], __dp4a(0x01010101, u[2*i+1], 0)); // sum of u + + sumf_d += d8[i] * (dot1 * sc[i]); + sumf_m += d8[i] * (dot2 * m[i]); + + } + + const float2 dm5f = __half22float2(dm5); + + return dm5f.x*sumf_d - dm5f.y*sumf_m; + +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +// contiguous u/y values +static __device__ __forceinline__ float vec_dot_q5_K_q8_1_impl_mmq( + const int * __restrict__ v, const int * __restrict__ u, const uint8_t * __restrict__ sc, + const uint8_t * __restrict__ m, const half2 & dm4, const half2 * __restrict__ ds8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + float sumf_d = 0.0f; + float sumf_m = 0.0f; + +#pragma unroll + for (int i = 0; i < QR5_K*VDR_Q5_K_Q8_1_MMQ/QI8_1; ++i) { + int sumi_d = 0; + +#pragma unroll + for (int j = 0; j < QI8_1; ++j) { + sumi_d = __dp4a(v[i*QI8_1 + j], u[i*QI8_1 + j], sumi_d); // SIMD dot product + } + + const float2 ds8f = __half22float2(ds8[i]); + + sumf_d += ds8f.x * (sc[i] * sumi_d); + sumf_m += ds8f.y * m[i]; // sum of q8_1 block * q4_K min val + } + + const float2 dm4f = __half22float2(dm4); + + return dm4f.x*sumf_d - dm4f.y*sumf_m; + +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +#define VDR_Q6_K_Q8_1_MMVQ 1 +#define VDR_Q6_K_Q8_1_MMQ 8 + +// contiguous v/x values +static __device__ __forceinline__ float vec_dot_q6_K_q8_1_impl_mmvq( + const int & vl, const int & vh, const int * __restrict__ u, const int8_t * __restrict__ scales, + const float & d, const float * __restrict__ d8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + float sumf = 0.0f; + +#pragma unroll + for (int i = 0; i < QR6_K; ++i) { + const int sc = scales[4*i]; + + const int vil = (vl >> (4*i)) & 0x0F0F0F0F; + + const int vih = ((vh >> (4*i)) << 4) & 0x30303030; + + const int vi = __vsubss4((vil | vih), 0x20202020); // vi = (vil | vih) - 32 + + sumf += d8[i] * (__dp4a(vi, u[i], 0) * sc); // SIMD dot product + } + + return d*sumf; +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +// contiguous u/y values +static __device__ __forceinline__ float vec_dot_q6_K_q8_1_impl_mmq( + const int * __restrict__ v, const int * __restrict__ u, const int8_t * __restrict__ sc, + const float & d6, const float * __restrict__ d8) { + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + float sumf_d = 0.0f; + +#pragma unroll + for (int i0 = 0; i0 < VDR_Q6_K_Q8_1_MMQ; i0 += 4) { + int2 sumi_d = {0, 0}; // 2 q6_K scales per q8_1 scale + +#pragma unroll + for (int i = i0; i < i0 + 2; ++i) { + sumi_d.x = __dp4a(v[2*i+0], u[2*i+0], sumi_d.x); // SIMD dot product + sumi_d.x = __dp4a(v[2*i+1], u[2*i+1], sumi_d.x); // SIMD dot product + + sumi_d.y = __dp4a(v[2*i+4], u[2*i+4], sumi_d.y); // SIMD dot product + sumi_d.y = __dp4a(v[2*i+5], u[2*i+5], sumi_d.y); // SIMD dot product + } + + sumf_d += d8[i0/4] * (sc[i0/2+0]*sumi_d.x + sc[i0/2+1]*sumi_d.y); + } + + return d6 * sumf_d; + +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A +} + +static __device__ __forceinline__ float vec_dot_q4_0_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_q4_0 * bq4_0 = (const block_q4_0 *) vbq; + + int v[VDR_Q4_0_Q8_1_MMVQ]; + int u[2*VDR_Q4_0_Q8_1_MMVQ]; + +#pragma unroll + for (int i = 0; i < VDR_Q4_0_Q8_1_MMVQ; ++i) { + v[i] = get_int_from_uint8(bq4_0->qs, iqs + i); + u[2*i+0] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); + u[2*i+1] = get_int_from_int8_aligned(bq8_1->qs, iqs + i + QI4_0); + } + + return vec_dot_q4_0_q8_1_impl(v, u, bq4_0->d, bq8_1->ds); +} + + +static __device__ __forceinline__ float vec_dot_q4_1_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_q4_1 * bq4_1 = (const block_q4_1 *) vbq; + + int v[VDR_Q4_1_Q8_1_MMVQ]; + int u[2*VDR_Q4_1_Q8_1_MMVQ]; + +#pragma unroll + for (int i = 0; i < VDR_Q4_1_Q8_1_MMVQ; ++i) { + v[i] = get_int_from_uint8_aligned(bq4_1->qs, iqs + i); + u[2*i+0] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); + u[2*i+1] = get_int_from_int8_aligned(bq8_1->qs, iqs + i + QI4_1); + } + + return vec_dot_q4_1_q8_1_impl(v, u, bq4_1->dm, bq8_1->ds); +} + +static __device__ __forceinline__ float vec_dot_q5_0_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_q5_0 * bq5_0 = (const block_q5_0 *) vbq; + + int vl[VDR_Q5_0_Q8_1_MMVQ]; + int vh[VDR_Q5_0_Q8_1_MMVQ]; + int u[2*VDR_Q5_0_Q8_1_MMVQ]; + +#pragma unroll + for (int i = 0; i < VDR_Q5_0_Q8_1_MMVQ; ++i) { + vl[i] = get_int_from_uint8(bq5_0->qs, iqs + i); + vh[i] = get_int_from_uint8(bq5_0->qh, 0) >> (4 * (iqs + i)); + u[2*i+0] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); + u[2*i+1] = get_int_from_int8_aligned(bq8_1->qs, iqs + i + QI5_0); + } + + return vec_dot_q5_0_q8_1_impl(vl, vh, u, bq5_0->d, bq8_1->ds); +} + +static __device__ __forceinline__ float vec_dot_q5_1_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_q5_1 * bq5_1 = (const block_q5_1 *) vbq; + + int vl[VDR_Q5_1_Q8_1_MMVQ]; + int vh[VDR_Q5_1_Q8_1_MMVQ]; + int u[2*VDR_Q5_1_Q8_1_MMVQ]; + +#pragma unroll + for (int i = 0; i < VDR_Q5_1_Q8_1_MMVQ; ++i) { + vl[i] = get_int_from_uint8_aligned(bq5_1->qs, iqs + i); + vh[i] = get_int_from_uint8_aligned(bq5_1->qh, 0) >> (4 * (iqs + i)); + u[2*i+0] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); + u[2*i+1] = get_int_from_int8_aligned(bq8_1->qs, iqs + i + QI5_1); + } + + return vec_dot_q5_1_q8_1_impl(vl, vh, u, bq5_1->dm, bq8_1->ds); +} + +static __device__ __forceinline__ float vec_dot_q8_0_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_q8_0 * bq8_0 = (const block_q8_0 *) vbq; + + int v[VDR_Q8_0_Q8_1_MMVQ]; + int u[VDR_Q8_0_Q8_1_MMVQ]; + +#pragma unroll + for (int i = 0; i < VDR_Q8_0_Q8_1_MMVQ; ++i) { + v[i] = get_int_from_int8(bq8_0->qs, iqs + i); + u[i] = get_int_from_int8_aligned(bq8_1->qs, iqs + i); + } + + return vec_dot_q8_0_q8_1_impl(v, u, bq8_0->d, __low2half(bq8_1->ds)); +} + +static __device__ __forceinline__ float vec_dot_q2_K_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_q2_K * bq2_K = (const block_q2_K *) vbq; + + const int bq8_offset = QR2_K * (iqs / QI8_1); + const int scale_offset = iqs - iqs % QI8_1 + (iqs % QI8_1) / (QI8_1/2); + + const uint8_t * scales = bq2_K->scales + scale_offset; + + const int v = get_int_from_uint8_aligned(bq2_K->qs, iqs); + int u[QR2_K]; + float d8[QR2_K]; + +#pragma unroll + for (int i = 0; i < QR2_K; ++ i) { + u[i] = get_int_from_int8_aligned(bq8_1[bq8_offset + i].qs, iqs % QI8_1); + d8[i] = __low2float(bq8_1[bq8_offset + i].ds); + } + + return vec_dot_q2_K_q8_1_impl_mmvq(v, u, scales, bq2_K->dm, d8); +} + +static __device__ __forceinline__ float vec_dot_q3_K_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_q3_K * bq3_K = (const block_q3_K *) vbq; + + const int bq8_offset = QR3_K * (iqs / (QI3_K/2)); + const int scale_offset = iqs - iqs % QI8_1 + (iqs % QI8_1) / (QI8_1/2); + + const float d = bq3_K->d; + + const int vl = get_int_from_uint8(bq3_K->qs, iqs); + + // invert the mask with ~ so that a 0/1 results in 4/0 being subtracted + const int vh = ~get_int_from_uint8(bq3_K->hmask, iqs % (QI3_K/2)) >> bq8_offset; + + int u[QR3_K]; + float d8[QR3_K]; + +#pragma unroll + for (int i = 0; i < QR3_K; ++i) { + u[i] = get_int_from_int8_aligned(bq8_1[bq8_offset + i].qs, iqs % QI8_1); + d8[i] = __low2float(bq8_1[bq8_offset + i].ds); + } + + return vec_dot_q3_K_q8_1_impl_mmvq(vl, vh, u, bq3_K->scales, scale_offset, d, d8); +} + +static __device__ __forceinline__ float vec_dot_q4_K_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + +#ifndef GGML_QKK_64 + const block_q4_K * bq4_K = (const block_q4_K *) vbq; + + int v[2]; + int u[2*QR4_K]; + float d8[QR4_K]; + + // iqs is in 0,2..30. bq8_offset = iqs/4 -> bq8_offset = 0, 2, 4, 6 + const int bq8_offset = QR4_K * ((iqs/2) / (QI8_1/2)); + + // iqs = 0....3 -> bq8_offset = 0, want q4_offset = 0, 4, 8, 12 + // iqs = 4....7 -> bq8_offset = 2, want q4_offset = 32, 36, 40, 44 + // iqs = 8...11 -> bq8_offset = 4, want q4_offset = 64, 68, 72, 76 + // iqs = 12..15 -> bq8_offset = 6, want q4_offset = 96, 100, 104, 108 + + const int * q4 = (const int *)(bq4_K->qs + 16 * bq8_offset + 4 * ((iqs/2)%4)); + v[0] = q4[0]; + v[1] = q4[4]; + + const uint16_t * scales = (const uint16_t *)bq4_K->scales; + uint16_t aux[2]; + const int j = bq8_offset/2; + if (j < 2) { + aux[0] = scales[j+0] & 0x3f3f; + aux[1] = scales[j+2] & 0x3f3f; + } else { + aux[0] = ((scales[j+2] >> 0) & 0x0f0f) | ((scales[j-2] & 0xc0c0) >> 2); + aux[1] = ((scales[j+2] >> 4) & 0x0f0f) | ((scales[j-0] & 0xc0c0) >> 2); + } + const uint8_t * sc = (const uint8_t *)aux; + const uint8_t * m = sc + 2; + + for (int i = 0; i < QR4_K; ++i) { + const block_q8_1 * bq8i = bq8_1 + bq8_offset + i; + d8[i] = __low2float(bq8i->ds); + + const int * q8 = (const int *)bq8i->qs + ((iqs/2)%4); + u[2*i+0] = q8[0]; + u[2*i+1] = q8[4]; + } + + return vec_dot_q4_K_q8_1_impl_vmmq(v, u, sc, m, bq4_K->dm, d8); + +#else + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + const block_q4_K * bq4_K = (const block_q4_K *) vbq; + + float sumf_d = 0.0f; + float sumf_m = 0.0f; + + uint16_t aux16[2]; + const uint8_t * s = (const uint8_t *)aux16; + + const uint16_t * a = (const uint16_t *)bq4_K->scales; + aux16[0] = a[0] & 0x0f0f; + aux16[1] = (a[0] >> 4) & 0x0f0f; + + const float dall = bq4_K->dm[0]; + const float dmin = bq4_K->dm[1]; + + const float d8_1 = __low2float(bq8_1[0].ds); + const float d8_2 = __low2float(bq8_1[1].ds); + + const int ui1 = *((const int *)bq8_1[0].qs + (iqs/2)); + const int ui2 = *((const int *)bq8_1[0].qs + (iqs/2) + 4); + const int ui3 = *((const int *)bq8_1[1].qs + (iqs/2)); + const int ui4 = *((const int *)bq8_1[1].qs + (iqs/2) + 4); + + const int * q4 = (const int *)bq4_K->qs + (iqs/2); + const int v1 = q4[0]; + const int v2 = q4[4]; + + const int dot1 = __dp4a(ui2, v2 & 0x0f0f0f0f, __dp4a(ui1, v1 & 0x0f0f0f0f, 0)); + const int dot2 = __dp4a(ui4, (v2 >> 4) & 0x0f0f0f0f, __dp4a(ui3, (v1 >> 4) & 0x0f0f0f0f, 0)); + const int dot3 = __dp4a(0x01010101, ui2, __dp4a(0x01010101, ui1, 0)); + const int dot4 = __dp4a(0x01010101, ui4, __dp4a(0x01010101, ui3, 0)); + + sumf_d += d8_1 * (dot1 * s[0]) + d8_2 * (dot2 * s[1]); + sumf_m += d8_1 * (dot3 * s[2]) + d8_2 * (dot4 * s[3]); + + return dall * sumf_d - dmin * sumf_m; + +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A + +#endif +} + +static __device__ __forceinline__ float vec_dot_q5_K_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + +#ifndef GGML_QKK_64 + const block_q5_K * bq5_K = (const block_q5_K *) vbq; + + int vl[2]; + int vh[2]; + int u[2*QR5_K]; + float d8[QR5_K]; + + const int bq8_offset = QR5_K * ((iqs/2) / (QI8_1/2)); + const int * ql = (const int *)(bq5_K->qs + 16 * bq8_offset + 4 * ((iqs/2)%4)); + const int * qh = (const int *)(bq5_K->qh + 4 * ((iqs/2)%4)); + + vl[0] = ql[0]; + vl[1] = ql[4]; + + vh[0] = qh[0] >> bq8_offset; + vh[1] = qh[4] >> bq8_offset; + + const uint16_t * scales = (const uint16_t *)bq5_K->scales; + uint16_t aux[2]; + const int j = bq8_offset/2; + if (j < 2) { + aux[0] = scales[j+0] & 0x3f3f; + aux[1] = scales[j+2] & 0x3f3f; + } else { + aux[0] = ((scales[j+2] >> 0) & 0x0f0f) | ((scales[j-2] & 0xc0c0) >> 2); + aux[1] = ((scales[j+2] >> 4) & 0x0f0f) | ((scales[j-0] & 0xc0c0) >> 2); + } + const uint8_t * sc = (const uint8_t *)aux; + const uint8_t * m = sc + 2; + +#pragma unroll + for (int i = 0; i < QR5_K; ++i) { + const block_q8_1 * bq8i = bq8_1 + bq8_offset + i; + d8[i] = __low2float(bq8i->ds); + + const int * q8 = (const int *)bq8i->qs + ((iqs/2)%4); + u[2*i+0] = q8[0]; + u[2*i+1] = q8[4]; + } + + return vec_dot_q5_K_q8_1_impl_vmmq(vl, vh, u, sc, m, bq5_K->dm, d8); + +#else + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + const block_q5_K * bq5_K = (const block_q5_K *) vbq; + + const int8_t * s = bq5_K->scales; + + const float d = bq5_K->d; + + const float d8_1 = __low2half(bq8_1[0].ds); + const float d8_2 = __low2half(bq8_1[1].ds); + + const int ui1 = *((const int *)bq8_1[0].qs + (iqs/2)); + const int ui2 = *((const int *)bq8_1[0].qs + (iqs/2) + 4); + const int ui3 = *((const int *)bq8_1[1].qs + (iqs/2)); + const int ui4 = *((const int *)bq8_1[1].qs + (iqs/2) + 4); + + const int * ql = (const int *)bq5_K->qs + (iqs/2); + const int vl1 = ql[0]; + const int vl2 = ql[4]; + + const int step = 4 * (iqs/2); // 0, 4, 8, 12 + const int im = step/8; // = 0 for iqs = 0, 2, = 1 for iqs = 4, 6 + const int in = step%8; // 0, 4, 0, 4 + const int vh = (*((const int *)(bq5_K->qh + in))) >> im; + + const int v1 = (((vh << 4) & 0x10101010) ^ 0x10101010) | ((vl1 >> 0) & 0x0f0f0f0f); + const int v2 = (((vh << 2) & 0x10101010) ^ 0x10101010) | ((vl2 >> 0) & 0x0f0f0f0f); + const int v3 = (((vh >> 0) & 0x10101010) ^ 0x10101010) | ((vl1 >> 4) & 0x0f0f0f0f); + const int v4 = (((vh >> 2) & 0x10101010) ^ 0x10101010) | ((vl2 >> 4) & 0x0f0f0f0f); + + const float sumf_d = d8_1 * (__dp4a(ui1, v1, 0) * s[0] + __dp4a(ui2, v2, 0) * s[1]) + + d8_2 * (__dp4a(ui3, v3, 0) * s[2] + __dp4a(ui4, v4, 0) * s[3]); + + return d * sumf_d; + +#else + NO_DEVICE_CODE; +#endif // __CUDA_ARCH__ >= MIN_CC_DP4A + +#endif +} + +static __device__ __forceinline__ float vec_dot_q6_K_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_q6_K * bq6_K = (const block_q6_K *) vbq; + + const int bq8_offset = 2 * QR6_K * (iqs / (QI6_K/2)) + (iqs % (QI6_K/2)) / (QI6_K/4); + const int scale_offset = (QI6_K/4) * (iqs / (QI6_K/2)) + (iqs % (QI6_K/2)) / (QI6_K/8); + const int vh_shift = 2 * ((iqs % (QI6_K/2)) / (QI6_K/4)); + + const int vl = get_int_from_uint8(bq6_K->ql, iqs); + const int vh = get_int_from_uint8(bq6_K->qh, (QI6_K/4) * (iqs / (QI6_K/2)) + iqs % (QI6_K/4)) >> vh_shift; + + const int8_t * scales = bq6_K->scales + scale_offset; + + int u[QR6_K]; + float d8[QR6_K]; + +#pragma unroll + for (int i = 0; i < QR6_K; ++i) { + u[i] = get_int_from_int8_aligned(bq8_1[bq8_offset + 2*i].qs, iqs % QI8_1); + d8[i] = __low2float(bq8_1[bq8_offset + 2*i].ds); + } + + return vec_dot_q6_K_q8_1_impl_mmvq(vl, vh, u, scales, bq6_K->d, d8); +} + +static __device__ __forceinline__ float vec_dot_iq2_xxs_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { +#if QK_K == 256 + const block_iq2_xxs * bq2 = (const block_iq2_xxs *) vbq; + +#if QR2_XXS == 8 + const int ib32 = iqs; + const uint16_t * q2 = bq2->qs + 4*ib32; + const uint8_t * aux8 = (const uint8_t *)q2; + const int8_t * q8 = bq8_1[ib32].qs; + uint32_t aux32 = q2[2] | (q2[3] << 16); + int sumi = 0; + for (int l = 0; l < 4; ++l) { + const uint8_t * grid = (const uint8_t *)(iq2xxs_grid + aux8[l]); + const uint8_t signs = ksigns_iq2xs[aux32 & 127]; + for (int j = 0; j < 8; ++j) { + sumi += q8[j] * grid[j] * (signs & kmask_iq2xs[j] ? -1 : 1); + } + q8 += 8; + aux32 >>= 7; + } + const float d = (float)bq2->d * (0.5f + aux32) * __low2float(bq8_1[ib32].ds) * 0.25f; + return d * sumi; +#else + // iqs is 0...15 + const int ib32 = iqs/2; + const int il = iqs%2; + const uint16_t * q2 = bq2->qs + 4*ib32; + const uint8_t * aux8 = (const uint8_t *)q2; + const uint8_t * grid1 = (const uint8_t *)(iq2xxs_grid + aux8[2*il+0]); + const uint8_t * grid2 = (const uint8_t *)(iq2xxs_grid + aux8[2*il+1]); + const uint32_t aux32 = q2[2] | (q2[3] << 16); + const float d = (float)bq2->d * (0.5f + (aux32 >> 28)) * __low2float(bq8_1[ib32].ds) * 0.25f; + const uint8_t signs1 = ksigns_iq2xs[(aux32 >> 14*il) & 127]; + const uint8_t signs2 = ksigns_iq2xs[(aux32 >> (14*il + 7)) & 127]; + const int8_t * q8 = bq8_1[ib32].qs + 16*il; + int sumi1 = 0, sumi2 = 0; + for (int j = 0; j < 8; ++j) { + sumi1 += q8[j+0] * grid1[j] * (signs1 & kmask_iq2xs[j] ? -1 : 1); + sumi2 += q8[j+8] * grid2[j] * (signs2 & kmask_iq2xs[j] ? -1 : 1); + } + return d * (sumi1 + sumi2); +#endif +#else + assert(false); + return 0.f; +#endif +} + +static __device__ __forceinline__ float vec_dot_iq2_xs_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics +#if QK_K == 256 + const block_iq2_xs * bq2 = (const block_iq2_xs *) vbq; + + const int ib32 = iqs; + const uint16_t * q2 = bq2->qs + 4*ib32; + const int8_t * q8 = bq8_1[ib32].qs; + const uint8_t ls1 = bq2->scales[ib32] & 0xf; + const uint8_t ls2 = bq2->scales[ib32] >> 4; + int sumi1 = 0; + for (int l = 0; l < 2; ++l) { + const uint32_t * grid = (const uint32_t *)(iq2xs_grid + (q2[l] & 511)); + const uint32_t * signs = (const uint32_t *)(ksigns64 + (q2[l] >> 9)); + const int grid_l = __vsub4(grid[0] ^ signs[0], signs[0]); + const int grid_h = __vsub4(grid[1] ^ signs[1], signs[1]); + sumi1 = __dp4a(grid_l, *((const int *)q8 + 0), sumi1); + sumi1 = __dp4a(grid_h, *((const int *)q8 + 1), sumi1); + q8 += 8; + } + int sumi2 = 0; + for (int l = 2; l < 4; ++l) { + const uint32_t * grid = (const uint32_t *)(iq2xs_grid + (q2[l] & 511)); + const uint32_t * signs = (const uint32_t *)(ksigns64 + (q2[l] >> 9)); + const int grid_l = __vsub4(grid[0] ^ signs[0], signs[0]); + const int grid_h = __vsub4(grid[1] ^ signs[1], signs[1]); + sumi2 = __dp4a(grid_l, *((const int *)q8 + 0), sumi2); + sumi2 = __dp4a(grid_h, *((const int *)q8 + 1), sumi2); + q8 += 8; + } + const float d = (float)bq2->d * __low2float(bq8_1[ib32].ds) * 0.25f; + return d * ((0.5f + ls1) * sumi1 + (0.5f + ls2) * sumi2); +#else + GGML_UNUSED(ksigns64); + assert(false); + return 0.f; +#endif +#else + GGML_UNUSED(ksigns64); + assert(false); + return 0.f; +#endif +} + +// TODO +static __device__ __forceinline__ float vec_dot_iq2_s_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics +#if QK_K == 256 + const block_iq2_s * bq2 = (const block_iq2_s *) vbq; + + const int ib32 = iqs; + const int8_t * q8 = bq8_1[ib32].qs; + const uint8_t * signs = bq2->qs + QK_K/8 + 4*ib32; + const uint8_t ls1 = bq2->scales[ib32] & 0xf; + const uint8_t ls2 = bq2->scales[ib32] >> 4; + int sumi1 = 0; + for (int l = 0; l < 2; ++l) { + const uint32_t * grid = (const uint32_t *)(iq2s_grid + (bq2->qs[4*ib32+l] | ((bq2->qh[ib32] << (8-2*l)) & 0x300))); + const uint32_t signs0 = __vcmpeq4(((signs[l] & 0xf) * 0x01010101) & 0x08040201, 0x08040201); + const uint32_t signs1 = __vcmpeq4(((signs[l] >> 4) * 0x01010101) & 0x08040201, 0x08040201); + const int grid_l = __vsub4(grid[0] ^ signs0, signs0); + const int grid_h = __vsub4(grid[1] ^ signs1, signs1); + sumi1 = __dp4a(grid_l, *((const int *)q8 + 0), sumi1); + sumi1 = __dp4a(grid_h, *((const int *)q8 + 1), sumi1); + q8 += 8; + } + int sumi2 = 0; + for (int l = 2; l < 4; ++l) { + const uint32_t * grid = (const uint32_t *)(iq2s_grid + (bq2->qs[4*ib32+l] | ((bq2->qh[ib32] << (8-2*l)) & 0x300))); + const uint32_t signs0 = __vcmpeq4(((signs[l] & 0xf) * 0x01010101) & 0x08040201, 0x08040201); + const uint32_t signs1 = __vcmpeq4(((signs[l] >> 4) * 0x01010101) & 0x08040201, 0x08040201); + const int grid_l = __vsub4(grid[0] ^ signs0, signs0); + const int grid_h = __vsub4(grid[1] ^ signs1, signs1); + sumi2 = __dp4a(grid_l, *((const int *)q8 + 0), sumi2); + sumi2 = __dp4a(grid_h, *((const int *)q8 + 1), sumi2); + q8 += 8; + } + const float d = (float)bq2->d * __low2float(bq8_1[ib32].ds) * 0.25f; + return d * ((0.5f + ls1) * sumi1 + (0.5f + ls2) * sumi2); +#else + GGML_UNUSED(ksigns64); + assert(false); + return 0.f; +#endif +#else + GGML_UNUSED(ksigns64); + assert(false); + return 0.f; +#endif +} + +static __device__ __forceinline__ float vec_dot_iq3_xxs_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics +#if QK_K == 256 + const block_iq3_xxs * bq2 = (const block_iq3_xxs *) vbq; + + const int ib32 = iqs; + const uint8_t * q3 = bq2->qs + 8*ib32; + const uint16_t * gas = (const uint16_t *)(bq2->qs + QK_K/4) + 2*ib32; + const int8_t * q8 = bq8_1[ib32].qs; + uint32_t aux32 = gas[0] | (gas[1] << 16); + int sumi = 0; + for (int l = 0; l < 4; ++l) { + const uint32_t * grid1 = iq3xxs_grid + q3[2*l+0]; + const uint32_t * grid2 = iq3xxs_grid + q3[2*l+1]; + const uint32_t * signs = (const uint32_t *)(ksigns64 + (aux32 & 127)); + const int grid_l = __vsub4(grid1[0] ^ signs[0], signs[0]); + const int grid_h = __vsub4(grid2[0] ^ signs[1], signs[1]); + sumi = __dp4a(grid_l, *((int *)q8+0), sumi); + sumi = __dp4a(grid_h, *((int *)q8+1), sumi); + q8 += 8; + aux32 >>= 7; + } + const float d = (float)bq2->d * (0.5f + aux32) * __low2float(bq8_1[ib32].ds) * 0.5f; + return d * sumi; +#else + assert(false); + return 0.f; +#endif +#else + assert(false); + return 0.f; +#endif +} + +// TODO: don't use lookup table for signs +static __device__ __forceinline__ float vec_dot_iq3_s_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics +#if QK_K == 256 + const block_iq3_s * bq2 = (const block_iq3_s *) vbq; + + const int ib32 = iqs; + const uint8_t * qs = bq2->qs + 8*ib32; + const int8_t * q8 = bq8_1[ib32].qs; + int sumi = 0; + for (int l = 0; l < 4; ++l) { + const uint32_t * grid1 = iq3s_grid + (qs[2*l+0] | ((bq2->qh[ib32] << (8 - 2*l)) & 256)); + const uint32_t * grid2 = iq3s_grid + (qs[2*l+1] | ((bq2->qh[ib32] << (7 - 2*l)) & 256)); + uint32_t signs0 = __vcmpeq4(((bq2->signs[4*ib32+l] & 0xf) * 0x01010101) & 0x08040201, 0x08040201); + uint32_t signs1 = __vcmpeq4(((bq2->signs[4*ib32+l] >> 4) * 0x01010101) & 0x08040201, 0x08040201); + const int grid_l = __vsub4(grid1[0] ^ signs0, signs0); + const int grid_h = __vsub4(grid2[0] ^ signs1, signs1); + sumi = __dp4a(grid_l, *((int *)q8+0), sumi); + sumi = __dp4a(grid_h, *((int *)q8+1), sumi); + q8 += 8; + } + const float d = (float)bq2->d * (1 + 2*((bq2->scales[ib32/2] >> 4*(ib32%2)) & 0xf)) * __low2float(bq8_1[ib32].ds); + return d * sumi; +#else + assert(false); + return 0.f; +#endif +#else + assert(false); + return 0.f; +#endif +} + +static __device__ __forceinline__ float vec_dot_iq1_s_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { +#if QK_K == 256 + const block_iq1_s * bq1 = (const block_iq1_s *) vbq; + + const int ib32 = iqs; + int sumi = 0; +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + const int * q8 = (const int *)bq8_1[ib32].qs; + for (int l = 0; l < 4; ++l) { + const int * grid = (const int *)(iq1s_grid_gpu + (bq1->qs[4*ib32+l] | (((bq1->qh[ib32] >> 3*l) & 7) << 8))); + int grid0 = grid[0] & 0x0f0f0f0f; + int grid1 = (grid[0] >> 4) & 0x0f0f0f0f; + sumi = __dp4a(q8[2*l+1], grid1, __dp4a(q8[2*l+0], grid0, sumi)); + } +#else + const int8_t * q8 = bq8_1[ib32].qs; + for (int l = 0; l < 4; ++l) { + const uint8_t * grid = (const uint8_t *)(iq1s_grid_gpu + (bq1->qs[4*ib32+l] | (((bq1->qh[ib32] >> 3*l) & 7) << 8))); + for (int j = 0; j < 4; ++j) { + sumi += q8[j] * (grid[j] & 0xf) + q8[j+4] * (grid[j] >> 4); + } + q8 += 8; + } +#endif + const float delta = bq1->qh[ib32] & 0x8000 ? -1-IQ1S_DELTA : -1+IQ1S_DELTA; + const float d1q = (float)bq1->d * (2*((bq1->qh[ib32] >> 12) & 7) + 1); + const float d = d1q * __low2float (bq8_1[ib32].ds); + const float m = d1q * __high2float(bq8_1[ib32].ds); + return d * sumi + m * delta; +#else + assert(false); + return 0.f; +#endif +} + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics +static __device__ __forceinline__ void get_int_from_table_16(const uint32_t & q4, const uint8_t * values, + int & val1, int & val2) { + + uint32_t aux32; const uint8_t * q8 = (const uint8_t *)&aux32; + aux32 = q4 & 0x0f0f0f0f; + uint16_t v1 = values[q8[0]] | (values[q8[1]] << 8); + uint16_t v2 = values[q8[2]] | (values[q8[3]] << 8); + val1 = v1 | (v2 << 16); + aux32 = (q4 >> 4) & 0x0f0f0f0f; + v1 = values[q8[0]] | (values[q8[1]] << 8); + v2 = values[q8[2]] | (values[q8[3]] << 8); + val2 = v1 | (v2 << 16); +} +#endif + +static __device__ __forceinline__ float vec_dot_iq4_nl_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + + const block_iq4_nl * bq = (const block_iq4_nl *) vbq; + +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + const uint16_t * q4 = (const uint16_t *)bq->qs + 2*iqs; + const int32_t * q8 = (const int32_t *)bq8_1->qs + iqs; + + const uint8_t * values = (const uint8_t *)kvalues_iq4nl; + + int v1, v2; + int sumi1 = 0, sumi2 = 0; + for (int l = 0; l < VDR_Q4_0_Q8_1_MMVQ; ++l) { + const uint32_t aux = q4[2*l] | (q4[2*l+1] << 16); + get_int_from_table_16(aux, values, v1, v2); + sumi1 = __dp4a(v1, q8[l+0], sumi1); + sumi2 = __dp4a(v2, q8[l+4], sumi2); + } + +#else + const uint8_t * q4 = bq->qs + 4*iqs; + const int8_t * q8 = bq8_1->qs + 4*iqs; + + int sumi1 = 0, sumi2 = 0; + for (int l = 0; l < 4*VDR_Q4_0_Q8_1_MMVQ; ++l) { + sumi1 += q8[l+ 0] * kvalues_iq4nl[q4[l] & 0xf]; + sumi2 += q8[l+16] * kvalues_iq4nl[q4[l] >> 4]; + } +#endif + const float d = (float)bq->d * __low2float(bq8_1->ds); + return d * (sumi1 + sumi2); +} + +static __device__ __forceinline__ float vec_dot_iq4_xs_q8_1( + const void * __restrict__ vbq, const block_q8_1 * __restrict__ bq8_1, const int & iqs) { + +#if QK_K == 256 +#if __CUDA_ARCH__ >= MIN_CC_DP4A // lowest compute capability for integer intrinsics + + const block_iq4_xs * bq4 = (const block_iq4_xs *) vbq; + const uint8_t * values = (const uint8_t *)kvalues_iq4nl; + + //// iqs is 0...7 + //const int ib64 = iqs/2; + //const int il = iqs%2; + //const int32_t * q8_1 = (const int *)bq8_1[2*ib64+0].qs + 2*il; + //const int32_t * q8_2 = (const int *)bq8_1[2*ib64+1].qs + 2*il; + //const uint32_t * q4_1 = (const uint32_t *)bq4->qs + 8*ib64 + 2*il; + //const uint32_t * q4_2 = q4_1 + 4; + //const int8_t ls1 = (bq4->scales_l[ib64] & 0xf) | (((bq4->scales_h >> (4*ib64+0)) & 3) << 4); + //const int8_t ls2 = (bq4->scales_l[ib64] >> 4) | (((bq4->scales_h >> (4*ib64+2)) & 3) << 4); + //const float d1 = (float)bq4->d * (ls1 - 32) * __low2float(bq8_1[2*ib64+0].ds); + //const float d2 = (float)bq4->d * (ls2 - 32) * __low2float(bq8_1[2*ib64+1].ds); + //int v1, v2; + //int sumi1 = 0, sumi2 = 0; + //for (int j = 0; j < 2; ++j) { + // get_int_from_table_16(q4_1[j], values, v1, v2); + // sumi1 = __dp4a(v2, q8_1[j+4], __dp4a(v1, q8_1[j+0], sumi1)); + // get_int_from_table_16(q4_2[j], values, v1, v2); + // sumi2 = __dp4a(v2, q8_2[j+4], __dp4a(v1, q8_2[j+0], sumi2)); + //} + //return d1 * sumi1 + d2 * sumi2; + + // iqs is 0...7 + const int ib32 = iqs; + const int32_t * q8 = (const int *)bq8_1[ib32].qs; + const uint32_t * q4 = (const uint32_t *)bq4->qs + 4*ib32; + const int8_t ls = ((bq4->scales_l[ib32/2] >> 4*(ib32%2)) & 0xf) | (((bq4->scales_h >> 2*ib32) & 3) << 4); + const float d = (float)bq4->d * (ls - 32) * __low2float(bq8_1[ib32].ds); + int v1, v2; + int sumi1 = 0, sumi2 = 0; + for (int j = 0; j < 4; ++j) { + get_int_from_table_16(q4[j], values, v1, v2); + sumi1 = __dp4a(v1, q8[j+0], sumi1); + sumi2 = __dp4a(v2, q8[j+4], sumi2); + } + return d * (sumi1 + sumi2); + + //// iqs is 0...15 + //const int ib32 = iqs/2; + //const int il = iqs%2; + //const int32_t * q8 = (const int *)bq8_1[ib32].qs + 2*il; + //const uint32_t * q4 = (const uint32_t *)bq4->qs + 4*ib32 + 2*il; + //const int8_t ls = ((bq4->scales_l[ib32/2] >> 4*(ib32%2)) & 0xf) | (((bq4->scales_h >> 2*ib32) & 3) << 4); + //const float d = (float)bq4->d * (ls - 32) * __low2float(bq8_1[ib32].ds); + //int v1, v2; + //int sumi1 = 0, sumi2 = 0; + //for (int j = 0; j < 2; ++j) { + // get_int_from_table_16(q4[j], values, v1, v2); + // sumi1 = __dp4a(v1, q8[j+0], sumi1); + // sumi2 = __dp4a(v2, q8[j+4], sumi2); + //} + //return d * (sumi1 + sumi2); +#else + assert(false); + return 0.f; +#endif +#else + return vec_dot_iq4_xs_q8_1(vbq, bq8_1, iqs); +#endif +}