mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-07-31 07:00:47 -05:00
* sampling : add support for backend sampling This commit adds support for performing sampling operations on the backend (e.g. GPU) as part of the model computation graph. The motivation for this feature is to enable sampling to be performed directly on the backend as part of the computation graph being executed, allowing for some or all of the sampling to be done on the backend. For example, the backend sampler chain might select/sample a token directly in which case only the sampled token needs to be transferred from device memory to host memory. It is also possible for the backend samplers to perform filtering of the logits, or compute and filter the probability distribution, in which case only the filtered logits or probabilites need to be transferred back to system memory for further processing by CPU samplers. Currently the backend sampling works in a similar manner to how pooling works, it is a function that is called by build_graph and the sampler operations become part of the models computation graph. * llama-cli : add backend sampler configuration * server : add backend sampling options/configuration * webui : add backend sampling options * ggml : add initial cumsum implementation for CUDA * sampling : enable all backend sampler tests This commit enables all exisiting backend sampler tests in the test-backend-sampler. Previously, some tests were disabled because there were missing ggml operation implementations. * graph : do not include llama-model.h * sampling : always expose sampled_ids This commit precomputes and caches the full-vocab token id list in llama_context's constructor, so llama_get_backend_sampled_token_ids_ith always returns a valid pointer. The motivation for this is that this enables both common/sampling.cpp and src/llama-sampling.cpp can simplify their logic. Not all backends samplers that process logits need to set the sampled_tokens_id as they may not change the order of the logits, for example the temperature sampler only scales the logits but does not change their order. Simliar the logit bias sampler only adds bias to specific token ids but does not change the order of the logits. In these cases there will not be a device to host copy of the sampled token ids, and this is the use case where having this precomputed list is useful. * sampling : ensure at most one output token per seq This commit adds a check in the batch allocator to ensure that when backend sampling is enabled, at most one output token is specified per sequence. * CUDA: Optimize argsort for gpu-based token sampling Argsort is used for top-k currently. WE optimize argsort by 2 things: 1. Use `DeviceRadixSort` for single-row/sequence to parallelize it across our SMs 2. Use `DeviceSegmentedSort` for multi-row/sequence as this is the correct entrypoint (the function chooses different execution paths, it contains `DeviceSegmentedRadixSort` as one of the paths and will choose the best one according to heuristics. https://nvidia.github.io/cccl/cub/api/structcub_1_1DeviceSegmentedSort.html#overview Some perf numbers for a RTX PRO 6000: On the kernel level, tested with `GGML_CUDA_DISABLE_GRAPHS=1 ./test-backend-ops -o ARGSORT perf` Before: ``` ARGSORT(type=f32,ne=[65000,16,1,1],order=0): 4130 runs - 359.24 us/run ARGSORT(type=f32,ne=[200000,1,1,1],order=0): 8192 runs - 861.34 us/run ARGSORT(type=f32,ne=[200000,16,1,1],order=0): 1343 runs - 1020.01 us/run ``` After: ``` ARGSORT(type=f32,ne=[65000,16,1,1],order=0): 4130 runs - 312.41 us/run ARGSORT(type=f32,ne=[200000,1,1,1],order=0): 16384 runs - 63.48 us/run ARGSORT(type=f32,ne=[200000,16,1,1],order=0): 1343 runs - 874.36 us/run ``` --- On the model level, tested with `llama-cli -m gpt-oss-20b-mxfp4.gguf -n 200 -p "What is the Capital of Sweden?" -no-cnv -fa 1 --backend-sampling` Before: ``` llama_perf_sampler_print: sampling time = 0.25 ms / 207 runs ( 0.00 ms per token, 824701.20 tokens per second) llama_perf_context_print: load time = 18215.58 ms llama_perf_context_print: prompt eval time = 28.20 ms / 7 tokens ( 4.03 ms per token, 248.19 tokens per second) llama_perf_context_print: eval time = 714.79 ms / 199 runs ( 3.59 ms per token, 278.40 tokens per second) llama_perf_context_print: total time = 857.62 ms / 206 tokens ``` After ``` llama_perf_sampler_print: sampling time = 0.25 ms / 207 runs ( 0.00 ms per token, 828000.00 tokens per second) llama_perf_context_print: load time = 18366.92 ms llama_perf_context_print: prompt eval time = 35.92 ms / 7 tokens ( 5.13 ms per token, 194.87 tokens per second) llama_perf_context_print: eval time = 532.79 ms / 199 runs ( 2.68 ms per token, 373.50 tokens per second) llama_perf_context_print: total time = 683.65 ms / 206 tokens ``` * sampling : remove version from sampler chain This commit removes the version field from the sampler chain and instead used the sampler pointer itself for change detection. * sampling : always populate logits for sampled probs This commit updates common/sampler.cpp set_logits and src/llama-sampling.cpp llama_sampler_sample to always populate the logits field when backend sampled probabilities are available. The motivation for this is that this ensure that CPU sampler always have access to the logits values even when probabilites have been produced by backend samplers. * sampling : simplify backend sampling logic decode This commit tries to simplify the backend sampling logic in llama_context::decode. * squash! sampling : simplify backend sampling logic decode Fix condition to check if backend actually sampled tokens, not just that backend samplers are available. * common : fix regression caused by extra memory allocations during sampling * squash! sampling : simplify backend sampling logic decode The commit fixes a variable shadowing issue in the `llama_context::decode` function which was introduced in a previous refactoring. * squash! common : fix regression caused by extra memory allocations during sampling Apply the same changes to llama-sampling.cpp, llama_sampler_sample as were applied in commit38f408c25. * sampling : introduce sampling_info struct This commit introduces a sampling_info struct to encapsulate all backend sampling related data within the llama_context class. It also updates to use more descriptive names for sampled tokens and candidates in the backend sampler ggml data structure. * sampling : return early if backend sampling is disabled * sampling : use pinned memory for backend sampling buffers * common, tools : refactor model loading to support backend samplers This commit refactors the model loading process in common/common.cpp to enable backend sampler to be configure prior to the llama_context creation. The motivation for this change is that just being able to set/reset the backend samplers after the llama_context has been created will cause a resize to occur in llama_context::output_reserve which we want to avoid. * sampling : add stride variable for clarity * sampling: clarify candidate ids usage in comments * sampling : fix copying both sampled tokens and logits/probs from backend This commit fixes the issue where both sampled tokens and logits/probs were not being copied correctly from the backend to the host when multiple backend samplers were used. A test for this scenario has also been added to ensure that both types of data are copied correctly when different backend samplers are employed. * tests : cleanup test-backend-sampler.cpp * common : remove build-info.cpp from commit [no ci] This file was generated during the build process and should not be included in previous commits. * sampling : cleanup and clarify output_reserve * sampling : remove redundant checks for stride and size [no ci] * sampling : add debug log when backend sampler selects token This commit adds a debug log statement in the llama_sampler_sample to indicate when a backend sampler has selected a token for a given index. The modification helps in tracing the sampling process and understanding the flow of control when backend samplers are used. * examples : update batched to use backend sampling This commit updates the batched example to demonstrate how to use backend samplers. * llama-cli : fix dangling reference to sampler config * common : initialize backend samplers * samplers : add missing cont * sampling : add assertions for contiguous tensors in async copy functions * examples : add info about hybrid sampling in batched [no ci] * sampling : remove backend-dist option (wip) This commit removes the `--backend-dist` option and instead uses the configured --samplers chain to determine which samplers run on the backend. Backend sampling is still enabled using With `--backend_sampling`, and the sampler chain, either explictly specified using `--samplers` or the default, is automatically analyzed to determine which samplers can run on the backend. The system finds the longest contiguous chain of backend supported samplers from the start of the sampler sequence. For example: * If the chain is `top-k -> temperature -> top-p`, and both `top-k` and `temperature` are backend-supported but `top-p` is not, then `top-k` and `temperature` will run on the backend, while `top-p` and subsequent samplers run on the CPU. * If all configured samplers are supported, the final distribution sampling will also happen on the backend, transferring only the sampled token IDs back to the host. * If the sampler chain starts with an unsupported sampler (e.g., `penalties`), all sampling runs on the CPU. Note that this is currently the case with the default sampler so to use backend sampling it is required to specify a sampler chain. See below for an example. The following shows how llama-cli can be run with backend sampling: ```console $ llama-cli -m models/Qwen2.5-VL-3B-Instruct-Q8_0.gguf \ --prompt 'What is the capital of Sweden?' \ -n 20 \ -no-cnv \ --verbose-prompt \ -ngl 40 \ --backend-sampling \ --samplers 'top_k;temperature' ``` In this case the all sampling will happen on the backend since both `top_k` and `temperature` are supported backend samplers. To enable a partial backend sampling (hybrid sampling), for example running `top_k` and `temperature` on the backend and `typ_p` on the CPU the following sampler chain could be specified: ```console $ llama-cli -m models/Qwen2.5-VL-3B-Instruct-Q8_0.gguf \ --prompt 'What is the capital of Sweden?' \ -n 20 \ -no-cnv \ --verbose-prompt \ -ngl 40 \ --backend-sampling \ --samplers 'top_k;temperature;top_p' ``` If this looks good then I'll follow up with updates the llama-cli and llama-server documentation to reflect these changes. * CUDA: Add top-k implementation * sampling : add min-p backend sampler * Use `FetchContent` over CPM as it's bundled with CMake Thanks @ggerganov for the suggestion * common : add get_active_samplers function to check enabled samplers This commit adds a function to check if a sampler is actually enabled, meaning that it does not have values that disables its effect. This is then used by the backend samplers initialization to avoid considering samplers that are not enabled when determining the split point between them. The motivation for this is that this allows the default sampler chain for `--samplers` to be used and any sampler that is not enabled will not cause the backend samplers to be skipped. For example, before this change if the penalties sampler was included in the samplers list but had default values that disable it, it would cause the backend samplers to be skipped entirely. This commit also contains some refactoring to remove some code duplication. * cuda : fix editorconfig-checker warning * sampling : use argmax for min-p sampling * sampling : fix temperature check to allow zero temperature This commit modifies the temperature sampling check to allow a temperature value of zero. Previously, the check only allowed positive temperature values, which excluded the valid case of zero temperature. The motivation for this is to enable a zero temperature setting which is also currently causing the following test to fail: ```console (venv) $ cd tools/server/tests (venv) $ ./tests.sh unit/test_basic.py::test_load_split_model ``` * cuda : fix top-k compilation when CUB is unavailable This commit adds a macro guard around argsort_f32_i32_cuda_cub usage in the top-k fallback path, falling back to bitonic sort when GGML_CUDA_USE_CUB is not defined. The motivation for this is that some environments like AMD HIP do not have CUB available, causing compilation failure. Refs: https://github.com/ggml-org/llama.cpp/actions/runs/19728226426/job/56523606840#step:6:208 * sampling : add comments about backend sampler [no ci] This commit adds a comment to llama_context's constructor explaining why backend samplers are initialized early in the process. * sampling : remove backend sampling chain from common_sampler This commit removes the backend sampling chain from the common_sampler structure and related functions. The motivation for this change is that the backend samplers are not currently set on the context, and if they are they would cause the a graph reallocation to occur. Instead, the intialization is handled like it currently is by llama_context's constructor. * Fix top-k comp & behavior for non-CUB path Some changes were made in5ea3be265bwhich were incomplete. In the case of non-CUB, bitonic sort and its limitations of ncols < 1024 have to apply, similar to argsort.cu * sampling : support intermixed backend/cpu samplers This commit updates the backend sampling implementation to support intermixed usage of backend and CPU samplers within the same batch. The initial implementation was developed as an all-or-nothing solution: either perform backend sampling for the entire batch, or perform CPU sampling for the entire batch. The motivation for this change is to support batches with mixed sequences. For example, we may have a backend sampler configured for sequence 0, while sequence 1 in the same batch uses CPU sampling. This was not supported in the initial implementation. This issue manifested in llama-server with the webui: decoding with backend samplers would work initially, but after changing to CPU sampling, a slot (sequence) could still be using a backend sampler. This meant that logits in output_reserve would not be allocated, resulting in an error. The solution in this commit inspects the batch to determine which sampling modes are needed and allocates buffers accordingly. However, there is a known inefficiency: when we have intermixed backend/CPU samplers in the same batch, we currently copy all logits to the host, even for sequences using backend samplers. Added test_backend_cpu_mixed_batch to verify correct behavior with mixed backend/CPU samplers in a single batch, including dynamic sampler switching between decode calls. * squash! sampling : support intermixed backend/cpu samplers Add check that logits is not null which is can happen for embeddings. * squash! sampling : support intermixed backend/cpu samplers Fix llama-save-load-state which currently fails by handling the case when batch.logits is nullptr (like when loading state) by allocating space for all outputs as CPU logits. * refactor : simplify and improve memory management * Add initial version for top-p sampling As we only support static graphs for the time and we don't know the size of the output of top-p, we have to do value-scaling same as for min-p operator. Further improvements can be applied to the unit-test (i.e. check for equivalence of top_p happening on backend with top_p happening on cpu) and also by constructing candidates and sorting those as opposed to reversing the sort of the logits (this would be arange + get_rows instead of argsort + get_rows) * sampling : use logits directly for min-p filtering * sampling : simplify * llama : simplify * llama : cleanup + naming * llama : call backend_init once * llama : reserve graphs with samplers * llama : naming * cont : naming * sampling : lower log level for output buffer reallocations [no ci] This commit changes the logging level for output buffer reallocations in the llama_context::output_reserve function from INFO to DEBUG. The motivation for this is that it currently logs to info and when enabling verbose logging for llama-cli this will get mixed with the output, for example: ```console What is the capital of Sweden?output_reserve: reallocating output buffer from size 0.58 MiB to 1.74 MiB 1. Stockholm 2\. Helsinki Based are the options 1. Stockholm Explanation: Stockholm is the capital of ... ``` * Fix backend_top_p_sampler softmax(softmax) will return uniform distribution, so we should not return the softmax but the logits instead. * Factor out `ggml_sort` into its own function * Make backend's top_p sampler inclusive In addition to match the algorithm proposed in the original [paper](https://arxiv.org/abs/1904.09751), this resolves the edge-case where `max_p is > top_p` for a single logit, where the mask would otherwise be empty (and we thus sample from the whole vocabulary with equal likelihood) * common : simplify sampler chain initialization * sampling : do not create empty samplers * sampling : fix top_p empty condition * examples : remove outdated backend sampling section This commit removes the outdated section about using backend samplers from the README.md file in the examples/batched. * sampling : fix backend temp sampler for zero temperature This commit fixes the implementation of the temperature-based sampler for the case when the temperature is set to zero. This now correctly selects the most probable token by masking out all other tokens in the logits. * CUDA: Move cccl fetch to after cuda has been enabled in CMakeLists.txt This will allow cccl to set build flags for the CUDA compiler, required e.g. for MSVC compat, see also https://github.com/NVIDIA/cccl/pull/6791 * CUDA: Use standard-compliant preprocessor for MSVC builds Workarounds of https://github.com/NVIDIA/cccl/pull/6791 will not be backported to CCCL 3.2, only the diagnostics/error messages will: https://github.com/NVIDIA/cccl/pull/6827 * CUDA: Update CCCL's rc candidate * squash! sampling : fix backend temp sampler for zero temperature This modifies the parent commit to simply return the most probably token instead of masking the logits. * sampling : implement temp_ext_backend sampling This commit implements the apply function for the extended temperature sampling. * sampling : minor cleanup * sampling : stop short if backend sampler sampled a token This commit modifies the graph building logic to immediately continue when a token has already been sampled by the backend sampler. It also updates the test for backend temporary sampling to include top-k and distribution samplers in the chain to verify that they are not producing any logits (they are not run). * Revert "sampling : stop short if backend sampler sampled a token" This reverts commit87b2719eca. * sampling : fix backend temp sampling to use logits masking * sampling : simplify temp sampling * sampling : remove redundant calls to ggml_build_forward_expand * sampling : check backend support during init * cont : keep backend sampling disabled for now * sampling : fix outputs and device checks * sampling : fix candidates logic * Add perf-tests for CUMSUM * Readd `cub::DeviceScan::InclusiveSum`-based CumSum For single rows and large columns doing a for-loop over the function `cub::DeviceScan::InclusiveSum` offered by CUB outperforms the `cumsum_cub_kernel` where `cub::BlockScan` is used. Numbers before this change Backend 1/3: CUDA0 Device description: NVIDIA RTX 6000 Ada Generation Device memory: 48510 MB (48039 MB free) CUMSUM(type=f32,ne=[128,128,4,4]): 311258 runs - 3.26 us/run - 2048 kB/run - 599.76 GB/s CUMSUM(type=f32,ne=[2048,16,5,4]): 229390 runs - 4.40 us/run - 5120 kB/run - 1110.23 GB/s CUMSUM(type=f32,ne=[20000,10,4,1]): 37583 runs - 29.63 us/run - 6250 kB/run - 201.18 GB/s CUMSUM(type=f32,ne=[128,1,1,1]): 892819 runs - 1.12 us/run - 1 kB/run - 0.85 GB/s CUMSUM(type=f32,ne=[1024,1,1,1]): 450505 runs - 2.25 us/run - 8 kB/run - 3.39 GB/s CUMSUM(type=f32,ne=[4096,1,1,1]): 155629 runs - 6.61 us/run - 32 kB/run - 4.62 GB/s CUMSUM(type=f32,ne=[8192,1,1,1]): 81910 runs - 12.60 us/run - 64 kB/run - 4.85 GB/s CUMSUM(type=f32,ne=[16384,1,1,1]): 49146 runs - 23.99 us/run - 128 kB/run - 5.09 GB/s CUMSUM(type=f32,ne=[32768,1,1,1]): 24573 runs - 47.10 us/run - 256 kB/run - 5.18 GB/s CUMSUM(type=f32,ne=[65536,1,1,1]): 16382 runs - 93.57 us/run - 512 kB/run - 5.22 GB/s CUMSUM(type=f32,ne=[131072,1,1,1]): 8191 runs - 184.79 us/run - 1024 kB/run - 5.29 GB/s CUMSUM(type=f32,ne=[200000,1,1,1]): 8191 runs - 280.43 us/run - 1562 kB/run - 5.31 GB/s CUMSUM(type=f32,ne=[2000000,1,1,1]): 2148 runs - 2771.23 us/run - 15625 kB/run - 5.38 GB/s CUMSUM(type=f32,ne=[128,4,1,1]): 458696 runs - 2.21 us/run - 4 kB/run - 1.73 GB/s CUMSUM(type=f32,ne=[1024,4,1,1]): 360404 runs - 2.82 us/run - 32 kB/run - 10.83 GB/s CUMSUM(type=f32,ne=[4096,4,1,1]): 147438 runs - 7.12 us/run - 128 kB/run - 17.15 GB/s CUMSUM(type=f32,ne=[8192,4,1,1]): 81910 runs - 12.90 us/run - 256 kB/run - 18.92 GB/s CUMSUM(type=f32,ne=[16384,4,1,1]): 49146 runs - 24.32 us/run - 512 kB/run - 20.08 GB/s CUMSUM(type=f32,ne=[32768,4,1,1]): 24573 runs - 47.28 us/run - 1024 kB/run - 20.66 GB/s CUMSUM(type=f32,ne=[65536,4,1,1]): 16382 runs - 93.21 us/run - 2048 kB/run - 20.96 GB/s CUMSUM(type=f32,ne=[131072,4,1,1]): 8191 runs - 185.04 us/run - 4096 kB/run - 21.11 GB/s CUMSUM(type=f32,ne=[200000,4,1,1]): 5369 runs - 282.08 us/run - 6250 kB/run - 21.13 GB/s CUMSUM(type=f32,ne=[2000000,4,1,1]): 537 runs - 2806.46 us/run - 62500 kB/run - 21.26 GB/s CUMSUM(type=f32,ne=[128,8,1,1]): 458696 runs - 2.20 us/run - 8 kB/run - 3.47 GB/s CUMSUM(type=f32,ne=[1024,8,1,1]): 360404 runs - 2.82 us/run - 64 kB/run - 21.66 GB/s CUMSUM(type=f32,ne=[4096,8,1,1]): 147438 runs - 7.12 us/run - 256 kB/run - 34.28 GB/s CUMSUM(type=f32,ne=[8192,8,1,1]): 81910 runs - 12.90 us/run - 512 kB/run - 37.84 GB/s CUMSUM(type=f32,ne=[16384,8,1,1]): 49146 runs - 24.32 us/run - 1024 kB/run - 40.15 GB/s CUMSUM(type=f32,ne=[32768,8,1,1]): 24573 runs - 47.28 us/run - 2048 kB/run - 41.31 GB/s CUMSUM(type=f32,ne=[65536,8,1,1]): 16382 runs - 93.20 us/run - 4096 kB/run - 41.92 GB/s CUMSUM(type=f32,ne=[131072,8,1,1]): 8194 runs - 185.05 us/run - 8192 kB/run - 42.22 GB/s CUMSUM(type=f32,ne=[200000,8,1,1]): 5370 runs - 282.15 us/run - 12500 kB/run - 42.26 GB/s CUMSUM(type=f32,ne=[2000000,8,1,1]): 269 runs - 4067.61 us/run - 125000 kB/run - 29.36 GB/s CUMSUM(type=f32,ne=[128,16,1,1]): 303067 runs - 3.32 us/run - 16 kB/run - 4.60 GB/s CUMSUM(type=f32,ne=[1024,16,1,1]): 303067 runs - 3.32 us/run - 128 kB/run - 36.76 GB/s CUMSUM(type=f32,ne=[4096,16,1,1]): 147438 runs - 7.17 us/run - 512 kB/run - 68.13 GB/s CUMSUM(type=f32,ne=[8192,16,1,1]): 81910 runs - 12.90 us/run - 1024 kB/run - 75.68 GB/s CUMSUM(type=f32,ne=[16384,16,1,1]): 49146 runs - 24.33 us/run - 2048 kB/run - 80.28 GB/s CUMSUM(type=f32,ne=[32768,16,1,1]): 24573 runs - 47.30 us/run - 4096 kB/run - 82.59 GB/s CUMSUM(type=f32,ne=[65536,16,1,1]): 12291 runs - 93.24 us/run - 8192 kB/run - 83.80 GB/s CUMSUM(type=f32,ne=[131072,16,1,1]): 6147 runs - 185.07 us/run - 16384 kB/run - 84.45 GB/s CUMSUM(type=f32,ne=[200000,16,1,1]): 4029 runs - 282.40 us/run - 25000 kB/run - 84.46 GB/s CUMSUM(type=f32,ne=[2000000,16,1,1]): 270 runs - 4118.40 us/run - 250000 kB/run - 58.11 GB/s Backend CUDA0: OK Backend 2/3: CUDA1 Device description: NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition Device memory: 97250 MB (96677 MB free) CUMSUM(type=f32,ne=[128,128,4,4]): 368595 runs - 2.73 us/run - 2048 kB/run - 715.83 GB/s CUMSUM(type=f32,ne=[2048,16,5,4]): 216282 runs - 4.72 us/run - 5120 kB/run - 1035.32 GB/s CUMSUM(type=f32,ne=[20000,10,4,1]): 32214 runs - 34.33 us/run - 6250 kB/run - 173.64 GB/s CUMSUM(type=f32,ne=[128,1,1,1]): 810909 runs - 1.24 us/run - 1 kB/run - 0.77 GB/s CUMSUM(type=f32,ne=[1024,1,1,1]): 401359 runs - 2.52 us/run - 8 kB/run - 3.03 GB/s CUMSUM(type=f32,ne=[4096,1,1,1]): 139247 runs - 7.44 us/run - 32 kB/run - 4.10 GB/s CUMSUM(type=f32,ne=[8192,1,1,1]): 73719 runs - 14.27 us/run - 64 kB/run - 4.28 GB/s CUMSUM(type=f32,ne=[16384,1,1,1]): 40955 runs - 27.24 us/run - 128 kB/run - 4.48 GB/s CUMSUM(type=f32,ne=[32768,1,1,1]): 24573 runs - 53.46 us/run - 256 kB/run - 4.57 GB/s CUMSUM(type=f32,ne=[65536,1,1,1]): 16382 runs - 105.29 us/run - 512 kB/run - 4.64 GB/s CUMSUM(type=f32,ne=[131072,1,1,1]): 8191 runs - 210.15 us/run - 1024 kB/run - 4.65 GB/s CUMSUM(type=f32,ne=[200000,1,1,1]): 8191 runs - 318.22 us/run - 1562 kB/run - 4.68 GB/s CUMSUM(type=f32,ne=[2000000,1,1,1]): 2148 runs - 3142.23 us/run - 15625 kB/run - 4.74 GB/s CUMSUM(type=f32,ne=[128,4,1,1]): 303067 runs - 3.34 us/run - 4 kB/run - 1.14 GB/s CUMSUM(type=f32,ne=[1024,4,1,1]): 253921 runs - 4.03 us/run - 32 kB/run - 7.58 GB/s CUMSUM(type=f32,ne=[4096,4,1,1]): 122865 runs - 8.20 us/run - 128 kB/run - 14.89 GB/s CUMSUM(type=f32,ne=[8192,4,1,1]): 73719 runs - 14.96 us/run - 256 kB/run - 16.32 GB/s CUMSUM(type=f32,ne=[16384,4,1,1]): 40955 runs - 28.66 us/run - 512 kB/run - 17.04 GB/s CUMSUM(type=f32,ne=[32768,4,1,1]): 24573 runs - 54.21 us/run - 1024 kB/run - 18.01 GB/s CUMSUM(type=f32,ne=[65536,4,1,1]): 16382 runs - 106.49 us/run - 2048 kB/run - 18.34 GB/s CUMSUM(type=f32,ne=[131072,4,1,1]): 8191 runs - 210.88 us/run - 4096 kB/run - 18.52 GB/s CUMSUM(type=f32,ne=[200000,4,1,1]): 5369 runs - 321.77 us/run - 6250 kB/run - 18.53 GB/s CUMSUM(type=f32,ne=[2000000,4,1,1]): 537 runs - 3191.79 us/run - 62500 kB/run - 18.69 GB/s CUMSUM(type=f32,ne=[128,8,1,1]): 376786 runs - 2.67 us/run - 8 kB/run - 2.86 GB/s CUMSUM(type=f32,ne=[1024,8,1,1]): 245730 runs - 4.10 us/run - 64 kB/run - 14.90 GB/s CUMSUM(type=f32,ne=[4096,8,1,1]): 122865 runs - 8.20 us/run - 256 kB/run - 29.79 GB/s CUMSUM(type=f32,ne=[8192,8,1,1]): 65528 runs - 16.38 us/run - 512 kB/run - 29.82 GB/s CUMSUM(type=f32,ne=[16384,8,1,1]): 40955 runs - 28.69 us/run - 1024 kB/run - 34.04 GB/s CUMSUM(type=f32,ne=[32768,8,1,1]): 24573 runs - 55.28 us/run - 2048 kB/run - 35.33 GB/s CUMSUM(type=f32,ne=[65536,8,1,1]): 16382 runs - 108.50 us/run - 4096 kB/run - 36.00 GB/s CUMSUM(type=f32,ne=[131072,8,1,1]): 8194 runs - 213.75 us/run - 8192 kB/run - 36.55 GB/s CUMSUM(type=f32,ne=[200000,8,1,1]): 5370 runs - 326.31 us/run - 12500 kB/run - 36.54 GB/s CUMSUM(type=f32,ne=[2000000,8,1,1]): 538 runs - 3252.68 us/run - 125000 kB/run - 36.72 GB/s CUMSUM(type=f32,ne=[128,16,1,1]): 303067 runs - 3.32 us/run - 16 kB/run - 4.60 GB/s CUMSUM(type=f32,ne=[1024,16,1,1]): 253921 runs - 4.06 us/run - 128 kB/run - 30.09 GB/s CUMSUM(type=f32,ne=[4096,16,1,1]): 122865 runs - 8.20 us/run - 512 kB/run - 59.57 GB/s CUMSUM(type=f32,ne=[8192,16,1,1]): 65528 runs - 16.38 us/run - 1024 kB/run - 59.63 GB/s CUMSUM(type=f32,ne=[16384,16,1,1]): 40955 runs - 28.69 us/run - 2048 kB/run - 68.09 GB/s CUMSUM(type=f32,ne=[32768,16,1,1]): 24573 runs - 55.28 us/run - 4096 kB/run - 70.67 GB/s CUMSUM(type=f32,ne=[65536,16,1,1]): 12291 runs - 108.50 us/run - 8192 kB/run - 72.02 GB/s CUMSUM(type=f32,ne=[131072,16,1,1]): 6147 runs - 213.60 us/run - 16384 kB/run - 73.17 GB/s CUMSUM(type=f32,ne=[200000,16,1,1]): 4029 runs - 326.04 us/run - 25000 kB/run - 73.15 GB/s CUMSUM(type=f32,ne=[2000000,16,1,1]): 270 runs - 5458.69 us/run - 250000 kB/run - 43.84 GB/s ---- Numbers after: Backend 1/3: CUDA0 Device description: NVIDIA RTX 6000 Ada Generation Device memory: 48510 MB (48039 MB free) CUMSUM(type=f32,ne=[128,128,4,4]): 311258 runs - 3.25 us/run - 2048 kB/run - 601.62 GB/s CUMSUM(type=f32,ne=[2048,16,5,4]): 229390 runs - 4.40 us/run - 5120 kB/run - 1110.14 GB/s CUMSUM(type=f32,ne=[20000,10,4,1]): 37583 runs - 29.67 us/run - 6250 kB/run - 200.89 GB/s CUMSUM(type=f32,ne=[128,1,1,1]): 892819 runs - 1.12 us/run - 1 kB/run - 0.85 GB/s CUMSUM(type=f32,ne=[1024,1,1,1]): 458696 runs - 2.21 us/run - 8 kB/run - 3.45 GB/s CUMSUM(type=f32,ne=[4096,1,1,1]): 376786 runs - 2.66 us/run - 32 kB/run - 11.46 GB/s CUMSUM(type=f32,ne=[8192,1,1,1]): 393168 runs - 2.59 us/run - 64 kB/run - 23.57 GB/s CUMSUM(type=f32,ne=[16384,1,1,1]): 393168 runs - 2.59 us/run - 128 kB/run - 47.15 GB/s CUMSUM(type=f32,ne=[32768,1,1,1]): 376786 runs - 2.69 us/run - 256 kB/run - 90.69 GB/s CUMSUM(type=f32,ne=[65536,1,1,1]): 327640 runs - 3.06 us/run - 512 kB/run - 159.65 GB/s CUMSUM(type=f32,ne=[131072,1,1,1]): 311258 runs - 3.28 us/run - 1024 kB/run - 297.77 GB/s CUMSUM(type=f32,ne=[200000,1,1,1]): 270303 runs - 3.74 us/run - 1562 kB/run - 398.14 GB/s CUMSUM(type=f32,ne=[2000000,1,1,1]): 137472 runs - 7.35 us/run - 15625 kB/run - 2026.94 GB/s CUMSUM(type=f32,ne=[128,4,1,1]): 876437 runs - 1.14 us/run - 4 kB/run - 3.33 GB/s CUMSUM(type=f32,ne=[1024,4,1,1]): 442314 runs - 2.28 us/run - 32 kB/run - 13.39 GB/s CUMSUM(type=f32,ne=[4096,4,1,1]): 155629 runs - 6.69 us/run - 128 kB/run - 18.24 GB/s CUMSUM(type=f32,ne=[8192,4,1,1]): 81910 runs - 12.53 us/run - 256 kB/run - 19.49 GB/s CUMSUM(type=f32,ne=[16384,4,1,1]): 49146 runs - 24.18 us/run - 512 kB/run - 20.20 GB/s CUMSUM(type=f32,ne=[32768,4,1,1]): 65528 runs - 15.34 us/run - 1024 kB/run - 63.66 GB/s CUMSUM(type=f32,ne=[65536,4,1,1]): 73719 runs - 14.76 us/run - 2048 kB/run - 132.35 GB/s CUMSUM(type=f32,ne=[131072,4,1,1]): 65528 runs - 16.01 us/run - 4096 kB/run - 244.07 GB/s CUMSUM(type=f32,ne=[200000,4,1,1]): 64428 runs - 16.51 us/run - 6250 kB/run - 360.97 GB/s CUMSUM(type=f32,ne=[2000000,4,1,1]): 33831 runs - 29.59 us/run - 62500 kB/run - 2016.08 GB/s CUMSUM(type=f32,ne=[128,8,1,1]): 868246 runs - 1.16 us/run - 8 kB/run - 6.59 GB/s CUMSUM(type=f32,ne=[1024,8,1,1]): 442314 runs - 2.28 us/run - 64 kB/run - 26.76 GB/s CUMSUM(type=f32,ne=[4096,8,1,1]): 155629 runs - 6.69 us/run - 256 kB/run - 36.48 GB/s CUMSUM(type=f32,ne=[8192,8,1,1]): 81910 runs - 12.53 us/run - 512 kB/run - 38.97 GB/s CUMSUM(type=f32,ne=[16384,8,1,1]): 49146 runs - 24.17 us/run - 1024 kB/run - 40.41 GB/s CUMSUM(type=f32,ne=[32768,8,1,1]): 24573 runs - 47.53 us/run - 2048 kB/run - 41.10 GB/s CUMSUM(type=f32,ne=[65536,8,1,1]): 16382 runs - 61.25 us/run - 4096 kB/run - 63.77 GB/s CUMSUM(type=f32,ne=[131072,8,1,1]): 32776 runs - 31.79 us/run - 8192 kB/run - 245.82 GB/s CUMSUM(type=f32,ne=[200000,8,1,1]): 32220 runs - 32.90 us/run - 12500 kB/run - 362.35 GB/s CUMSUM(type=f32,ne=[2000000,8,1,1]): 6725 runs - 151.99 us/run - 125000 kB/run - 785.77 GB/s CUMSUM(type=f32,ne=[128,16,1,1]): 851864 runs - 1.18 us/run - 16 kB/run - 12.97 GB/s CUMSUM(type=f32,ne=[1024,16,1,1]): 442314 runs - 2.30 us/run - 128 kB/run - 53.13 GB/s CUMSUM(type=f32,ne=[4096,16,1,1]): 155629 runs - 6.68 us/run - 512 kB/run - 73.13 GB/s CUMSUM(type=f32,ne=[8192,16,1,1]): 81910 runs - 12.68 us/run - 1024 kB/run - 77.00 GB/s CUMSUM(type=f32,ne=[16384,16,1,1]): 40955 runs - 24.56 us/run - 2048 kB/run - 79.53 GB/s CUMSUM(type=f32,ne=[32768,16,1,1]): 24573 runs - 47.52 us/run - 4096 kB/run - 82.21 GB/s CUMSUM(type=f32,ne=[65536,16,1,1]): 12291 runs - 93.44 us/run - 8192 kB/run - 83.62 GB/s CUMSUM(type=f32,ne=[131072,16,1,1]): 16392 runs - 63.36 us/run - 16384 kB/run - 246.68 GB/s CUMSUM(type=f32,ne=[200000,16,1,1]): 16116 runs - 65.25 us/run - 25000 kB/run - 365.53 GB/s CUMSUM(type=f32,ne=[2000000,16,1,1]): 3375 runs - 304.46 us/run - 250000 kB/run - 785.98 GB/s Backend CUDA0: OK Backend 2/3: CUDA1 Device description: NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition Device memory: 97250 MB (96677 MB free) CUMSUM(type=f32,ne=[128,128,4,4]): 376786 runs - 2.69 us/run - 2048 kB/run - 727.04 GB/s CUMSUM(type=f32,ne=[2048,16,5,4]): 216282 runs - 4.64 us/run - 5120 kB/run - 1053.30 GB/s CUMSUM(type=f32,ne=[20000,10,4,1]): 32214 runs - 34.21 us/run - 6250 kB/run - 174.27 GB/s CUMSUM(type=f32,ne=[128,1,1,1]): 819100 runs - 1.22 us/run - 1 kB/run - 0.78 GB/s CUMSUM(type=f32,ne=[1024,1,1,1]): 409550 runs - 2.47 us/run - 8 kB/run - 3.09 GB/s CUMSUM(type=f32,ne=[4096,1,1,1]): 303067 runs - 3.31 us/run - 32 kB/run - 9.21 GB/s CUMSUM(type=f32,ne=[8192,1,1,1]): 237539 runs - 4.33 us/run - 64 kB/run - 14.08 GB/s CUMSUM(type=f32,ne=[16384,1,1,1]): 237539 runs - 4.33 us/run - 128 kB/run - 28.17 GB/s CUMSUM(type=f32,ne=[32768,1,1,1]): 188393 runs - 5.37 us/run - 256 kB/run - 45.47 GB/s CUMSUM(type=f32,ne=[65536,1,1,1]): 188393 runs - 5.41 us/run - 512 kB/run - 90.20 GB/s CUMSUM(type=f32,ne=[131072,1,1,1]): 188393 runs - 5.41 us/run - 1024 kB/run - 180.41 GB/s CUMSUM(type=f32,ne=[200000,1,1,1]): 188393 runs - 5.41 us/run - 1562 kB/run - 275.27 GB/s CUMSUM(type=f32,ne=[2000000,1,1,1]): 128880 runs - 7.76 us/run - 15625 kB/run - 1920.33 GB/s CUMSUM(type=f32,ne=[128,4,1,1]): 802718 runs - 1.26 us/run - 4 kB/run - 3.03 GB/s CUMSUM(type=f32,ne=[1024,4,1,1]): 401359 runs - 2.51 us/run - 32 kB/run - 12.18 GB/s CUMSUM(type=f32,ne=[4096,4,1,1]): 139247 runs - 7.51 us/run - 128 kB/run - 16.26 GB/s CUMSUM(type=f32,ne=[8192,4,1,1]): 73719 runs - 14.17 us/run - 256 kB/run - 17.23 GB/s CUMSUM(type=f32,ne=[16384,4,1,1]): 40955 runs - 27.37 us/run - 512 kB/run - 17.84 GB/s CUMSUM(type=f32,ne=[32768,4,1,1]): 40955 runs - 26.33 us/run - 1024 kB/run - 37.10 GB/s CUMSUM(type=f32,ne=[65536,4,1,1]): 40955 runs - 26.19 us/run - 2048 kB/run - 74.59 GB/s CUMSUM(type=f32,ne=[131072,4,1,1]): 40955 runs - 26.35 us/run - 4096 kB/run - 148.26 GB/s CUMSUM(type=f32,ne=[200000,4,1,1]): 42952 runs - 24.18 us/run - 6250 kB/run - 246.51 GB/s CUMSUM(type=f32,ne=[2000000,4,1,1]): 32757 runs - 31.01 us/run - 62500 kB/run - 1923.68 GB/s CUMSUM(type=f32,ne=[128,8,1,1]): 786336 runs - 1.28 us/run - 8 kB/run - 5.95 GB/s CUMSUM(type=f32,ne=[1024,8,1,1]): 393168 runs - 2.57 us/run - 64 kB/run - 23.73 GB/s CUMSUM(type=f32,ne=[4096,8,1,1]): 131056 runs - 7.67 us/run - 256 kB/run - 31.82 GB/s CUMSUM(type=f32,ne=[8192,8,1,1]): 73719 runs - 14.43 us/run - 512 kB/run - 33.84 GB/s CUMSUM(type=f32,ne=[16384,8,1,1]): 40955 runs - 27.90 us/run - 1024 kB/run - 35.01 GB/s CUMSUM(type=f32,ne=[32768,8,1,1]): 24573 runs - 54.63 us/run - 2048 kB/run - 35.75 GB/s CUMSUM(type=f32,ne=[65536,8,1,1]): 16382 runs - 72.24 us/run - 4096 kB/run - 54.08 GB/s CUMSUM(type=f32,ne=[131072,8,1,1]): 20485 runs - 52.66 us/run - 8192 kB/run - 148.37 GB/s CUMSUM(type=f32,ne=[200000,8,1,1]): 21480 runs - 48.00 us/run - 12500 kB/run - 248.42 GB/s CUMSUM(type=f32,ne=[2000000,8,1,1]): 16140 runs - 61.99 us/run - 125000 kB/run - 1926.51 GB/s CUMSUM(type=f32,ne=[128,16,1,1]): 786336 runs - 1.28 us/run - 16 kB/run - 11.90 GB/s CUMSUM(type=f32,ne=[1024,16,1,1]): 393168 runs - 2.57 us/run - 128 kB/run - 47.57 GB/s CUMSUM(type=f32,ne=[4096,16,1,1]): 131056 runs - 7.65 us/run - 512 kB/run - 63.83 GB/s CUMSUM(type=f32,ne=[8192,16,1,1]): 73719 runs - 14.42 us/run - 1024 kB/run - 67.74 GB/s CUMSUM(type=f32,ne=[16384,16,1,1]): 40955 runs - 27.87 us/run - 2048 kB/run - 70.09 GB/s CUMSUM(type=f32,ne=[32768,16,1,1]): 24573 runs - 54.54 us/run - 4096 kB/run - 71.63 GB/s CUMSUM(type=f32,ne=[65536,16,1,1]): 12291 runs - 107.53 us/run - 8192 kB/run - 72.66 GB/s CUMSUM(type=f32,ne=[131072,16,1,1]): 10245 runs - 105.10 us/run - 16384 kB/run - 148.70 GB/s CUMSUM(type=f32,ne=[200000,16,1,1]): 10744 runs - 95.36 us/run - 25000 kB/run - 250.11 GB/s CUMSUM(type=f32,ne=[2000000,16,1,1]): 5400 runs - 186.97 us/run - 250000 kB/run - 1279.90 GB/s * sampling : expand support (wip) * tests : fix memory leaks * cont : fixes * tests : check temp back to 0.0 * sampling : fix top-p * sampling : handle n_probs case * server : handle unsupported cases * metal : print node names for debugging * ggml : remove redundant src in ggml_cast * ggml-alloc : fix reuse-parent logic for misaligned sizes * Revert "ggml : remove redundant src in ggml_cast" This reverts commit62d1b0082d. * CUDA: Add Cooperative-Groups-based parallelization of ncols in softmax Old implementation parallelizes rows across SMs, which does not fit the needs of backend-sampling (where we have ncols >> nrows and thus want to parallelize ncols across SMs) * Add TODOs to and adjust heuristics of row-wise soft_max in CUDA Heuristics were selected based on the following numbers: ``` -- Before Backend 1/2: CUDA0 Device description: NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition Device memory: 97250 MB (96691 MB free) SOFT_MAX(type=f32,ne=[4096,4096,5,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 2236 runs - 450.34 us/run - 655360 kB/run - 1401.20 GB/s SOFT_MAX(type=f32,ne=[12888,256,5,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 17748 runs - 56.80 us/run - 128880 kB/run - 2168.19 GB/s SOFT_MAX(type=f32,ne=[77,4096,5,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 57204 runs - 18.35 us/run - 12320 kB/run - 640.57 GB/s SOFT_MAX(type=f32,ne=[1024,1024,10,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 9840 runs - 102.46 us/run - 81920 kB/run - 763.45 GB/s SOFT_MAX(type=f32,ne=[77,1024,10,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 98064 runs - 10.25 us/run - 6160 kB/run - 573.43 GB/s SOFT_MAX(type=f32,ne=[256,256,20,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 98310 runs - 10.25 us/run - 10240 kB/run - 953.20 GB/s SOFT_MAX(type=f32,ne=[64,64,20,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 172011 runs - 5.99 us/run - 640 kB/run - 101.84 GB/s SOFT_MAX(type=f32,ne=[77,64,20,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 172011 runs - 5.97 us/run - 770 kB/run - 123.02 GB/s SOFT_MAX(type=f32,ne=[8192,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 172011 runs - 6.00 us/run - 64 kB/run - 10.16 GB/s SOFT_MAX(type=f32,ne=[8192,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 163820 runs - 6.12 us/run - 256 kB/run - 39.91 GB/s SOFT_MAX(type=f32,ne=[8192,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 147438 runs - 6.88 us/run - 1024 kB/run - 141.92 GB/s SOFT_MAX(type=f32,ne=[16384,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 122865 runs - 8.20 us/run - 128 kB/run - 14.89 GB/s SOFT_MAX(type=f32,ne=[16384,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 114674 runs - 8.87 us/run - 512 kB/run - 55.06 GB/s SOFT_MAX(type=f32,ne=[16384,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 98292 runs - 10.24 us/run - 2048 kB/run - 190.82 GB/s SOFT_MAX(type=f32,ne=[32768,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 49146 runs - 21.37 us/run - 256 kB/run - 11.43 GB/s SOFT_MAX(type=f32,ne=[32768,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 49146 runs - 22.54 us/run - 1024 kB/run - 43.33 GB/s SOFT_MAX(type=f32,ne=[32768,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 49146 runs - 23.92 us/run - 4096 kB/run - 163.32 GB/s SOFT_MAX(type=f32,ne=[65536,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 32764 runs - 38.94 us/run - 512 kB/run - 12.54 GB/s SOFT_MAX(type=f32,ne=[65536,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 24573 runs - 41.94 us/run - 2048 kB/run - 46.57 GB/s SOFT_MAX(type=f32,ne=[65536,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 24582 runs - 43.09 us/run - 8192 kB/run - 181.32 GB/s SOFT_MAX(type=f32,ne=[131072,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 16382 runs - 74.56 us/run - 1024 kB/run - 13.10 GB/s SOFT_MAX(type=f32,ne=[131072,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 16382 runs - 79.85 us/run - 4096 kB/run - 48.92 GB/s SOFT_MAX(type=f32,ne=[131072,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 12294 runs - 82.41 us/run - 16384 kB/run - 189.64 GB/s SOFT_MAX(type=f32,ne=[262144,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 8191 runs - 145.16 us/run - 2048 kB/run - 13.46 GB/s SOFT_MAX(type=f32,ne=[262144,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 8194 runs - 155.46 us/run - 8192 kB/run - 50.26 GB/s SOFT_MAX(type=f32,ne=[262144,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 7175 runs - 160.70 us/run - 32768 kB/run - 194.56 GB/s SOFT_MAX(type=f32,ne=[524288,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 8191 runs - 285.81 us/run - 4096 kB/run - 13.67 GB/s SOFT_MAX(type=f32,ne=[524288,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 4098 runs - 306.91 us/run - 16384 kB/run - 50.92 GB/s SOFT_MAX(type=f32,ne=[524288,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 3591 runs - 317.06 us/run - 65536 kB/run - 197.32 GB/s -- After Backend 1/2: CUDA0 Device description: NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition Device memory: 97250 MB (96691 MB free) SOFT_MAX(type=f32,ne=[4096,4096,5,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 2236 runs - 450.67 us/run - 655360 kB/run - 1400.15 GB/s SOFT_MAX(type=f32,ne=[12888,256,5,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 17748 runs - 56.97 us/run - 128880 kB/run - 2161.50 GB/s SOFT_MAX(type=f32,ne=[77,4096,5,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 57204 runs - 18.35 us/run - 12320 kB/run - 640.36 GB/s SOFT_MAX(type=f32,ne=[1024,1024,10,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 9840 runs - 102.46 us/run - 81920 kB/run - 763.42 GB/s SOFT_MAX(type=f32,ne=[77,1024,10,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 98064 runs - 10.25 us/run - 6160 kB/run - 573.43 GB/s SOFT_MAX(type=f32,ne=[256,256,20,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 98310 runs - 10.25 us/run - 10240 kB/run - 953.21 GB/s SOFT_MAX(type=f32,ne=[64,64,20,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 147438 runs - 7.00 us/run - 640 kB/run - 87.26 GB/s SOFT_MAX(type=f32,ne=[77,64,20,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 147438 runs - 6.99 us/run - 770 kB/run - 105.05 GB/s SOFT_MAX(type=f32,ne=[8192,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 172011 runs - 6.02 us/run - 64 kB/run - 10.13 GB/s SOFT_MAX(type=f32,ne=[8192,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 163820 runs - 6.12 us/run - 256 kB/run - 39.87 GB/s SOFT_MAX(type=f32,ne=[8192,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 147438 runs - 6.91 us/run - 1024 kB/run - 141.40 GB/s SOFT_MAX(type=f32,ne=[16384,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 122865 runs - 8.20 us/run - 128 kB/run - 14.89 GB/s SOFT_MAX(type=f32,ne=[16384,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 114674 runs - 8.79 us/run - 512 kB/run - 55.54 GB/s SOFT_MAX(type=f32,ne=[16384,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 98292 runs - 10.24 us/run - 2048 kB/run - 190.82 GB/s SOFT_MAX(type=f32,ne=[32768,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 131056 runs - 8.11 us/run - 256 kB/run - 30.12 GB/s SOFT_MAX(type=f32,ne=[32768,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 49146 runs - 22.54 us/run - 1024 kB/run - 43.33 GB/s SOFT_MAX(type=f32,ne=[32768,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 49146 runs - 23.32 us/run - 4096 kB/run - 167.50 GB/s SOFT_MAX(type=f32,ne=[65536,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 122865 runs - 8.19 us/run - 512 kB/run - 59.63 GB/s SOFT_MAX(type=f32,ne=[65536,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 40955 runs - 24.59 us/run - 2048 kB/run - 79.43 GB/s SOFT_MAX(type=f32,ne=[65536,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 24582 runs - 43.21 us/run - 8192 kB/run - 180.84 GB/s SOFT_MAX(type=f32,ne=[131072,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 122865 runs - 8.19 us/run - 1024 kB/run - 119.25 GB/s SOFT_MAX(type=f32,ne=[131072,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 40955 runs - 24.59 us/run - 4096 kB/run - 158.87 GB/s SOFT_MAX(type=f32,ne=[131072,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 12294 runs - 82.37 us/run - 16384 kB/run - 189.74 GB/s SOFT_MAX(type=f32,ne=[262144,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 122865 runs - 8.20 us/run - 2048 kB/run - 238.28 GB/s SOFT_MAX(type=f32,ne=[262144,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 36873 runs - 28.66 us/run - 8192 kB/run - 272.61 GB/s SOFT_MAX(type=f32,ne=[262144,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 9225 runs - 108.51 us/run - 32768 kB/run - 288.13 GB/s SOFT_MAX(type=f32,ne=[524288,1,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 98292 runs - 10.24 us/run - 4096 kB/run - 381.65 GB/s SOFT_MAX(type=f32,ne=[524288,4,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 32784 runs - 31.74 us/run - 16384 kB/run - 492.43 GB/s SOFT_MAX(type=f32,ne=[524288,16,1,1],mask=0,sinks=0,m_prec=f32,nr23=[1,1],scale=1.000000,max_bias=0.000000,inplace=0): 8721 runs - 121.20 us/run - 65536 kB/run - 516.19 GB/s ``` * Fix compiler warnings by casting `const` away * llama : require backend samplers to be of type llama_sampler_chain * sampling : use host buffer type for inputs * Try fixing HIP build errors by adding corresponding #defines Will likely have to disable for MUSA as I didn't find any docs online * Fix launch logic when supports_cooperative_launch=false * Disable cooperative groups for musa Didn't find any doc online, so I don't even know if they support this * server : reconnect the backend_sampling setting in the WebUI * graph : make the compute graph constant with respect to active samplers * batch : fix sequence id ownage * graph : respect sampler order for graph reuse * HIP/MUSA: fix build for backend sampling * sampling : optimize logit_bias sampler * cont : fix build * sampling : generic ggml op support detection * sampling : fix greedy * tests : run backend sampler tests always on the CPU * Apply suggestions from code review Co-authored-by: Johannes Gäßler <johannesg@5d6.de> * webui : fix lint * Fix data-race in `soft_max_f32_parallelize_cols_single_row` By using `tmp_vals` to store both max values and exponential accumulator there was a potential data-race, where the exponential accumulator for a given CTA may have written to `tmp_vals` before all others CTAs have read the max value from it. To avoid a third g.sync(), an additional temporary data-storage was added. Given that there are syncs in place after writing to gmem, it is guaranteed that the previous values for sums/max were read by all CTAs now. * Apply automated code-formating to softmax.cu * llama : clarify backend_accept/backend_set_input comments [no ci] * llama : fix typo in comment [no ci] * tests : use smart pointers for backend samplers * tests : use smart pointers for model and context * tests : remove vocab member from test_model_context Also includes some minor cleanups related to nullptr checks. * tests : extract batch info update to separate method * tests : fix batch token position tracking in test_backend_sampler.cpp * tests : add --device option support to backend sampler tests This commit adds support for specifying a device to run the test on. * common : disable backend sampling when grammar is involved * Fix different RNG-states between backend-sampling and llama-sampling By default, we perform a warm-up step where the ggml_cgraph is computed once. For backend-sampling, this graph contains the sampler, and thus the RNG state of the backend's dist sampler is advanced once. Solution to this is to reset the samplers after the warmup has finished * Make backend dist sampler use same rnd's as dist sampler We sample in double precision and cast to float to match rnd numbers of llama_dampler_dist which uses double precision (sampling from std::uniform_real_distribution<double> and std::uniform_real_distribution<float> with same rng will produce different sequences). * Update CCCL version to v3.2.0-rc2 * Build with CCCL 3.2 for CUDA backends Gives best perf for backend-sampling on CUDA. Flag can be removed once CCCL 3.2 is bundled within CTK and that CTK version is used in llama.cpp * tests : revert server test changes (no longer needed) * ggml : include cub/cub.cuh instead of block_scan.cuh This commit updates the include directive in cumsum.cu to use cub/cub.cuh instead of cub/block/block_scan.cuh. The motivation of this change is that without it compilation fails with the following error: ```console /llama.cpp/ggml/src/ggml-cuda/cumsum.cu(196): error: name followed by "::" must be a class or namespace name cub::DeviceScan::InclusiveSum(nullptr, ^ /llama.cpp/ggml/src/ggml-cuda/cumsum.cu(207): error: name followed by "::" must be a class or namespace name cub::DeviceScan::InclusiveSum((void *) tmp_alloc.get(), tmp_size, src, dst, ne, stream); ^ 2 errors detected in the compilation of "/llama.cpp/ggml/src/ggml-cuda/cumsum.cu". gmake[2]: *** [ggml/src/ggml-cuda/CMakeFiles/ggml-cuda.dir/build.make:317: ggml/src/ggml-cuda/CMakeFiles/ggml-cuda.dir/cumsum.cu.o] Error 2 ``` Commit83b3b1c271("cuda: optimize cumsum cub path (#18362)") updated the include directive replacing device_scan.cuh which is causing this issue. This commit uses cub/cub.cuh umbrella header which is consistent with other files in the ggml-cuda directory like mean.cu, sum.cu, etc. * arg : add shorthand for --backend-sampling * ci : add server workflow with backend sampling * sampling : fix reshapes * server : remove printfs * sampling : zero-initialize input buffers * minor : add comments + some cleanup * llama : assert at most one output token per sequence * tests : add more top_k tests * CUDA: Fix non-determinism of CUB-based Top-K DeviceTopK::MaxPairs is an iterative algorithm, where `d_keys_out` is written after every iteration. As a consequence, it must not overlap with `d_keys_in`, or otherwise undefined behavior occurs (keys are no longer unique in d_keys_in and may map to different values between iterations) * CUDA: Optimize index of top_k_cub By using the fancy [`counting_iterator`](https://nvidia.github.io/cccl/thrust/api/classthrust_1_1counting__iterator.html#classthrust_1_1counting__iterator) exposed by CCCL, we can avoid materializing the index to GPU memory, saving VRAM + 1 kernel invocation * Apply code-formatting to top-k.cu * CUDA: Remove obsolete temp_keys from CUB Since we use cuda::discard_iterator to avoid writing out the keys, we can directly pass in src instead of copying it to `temp_keys` * minor : cleanup, TODOs, etc. --------- Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> Co-authored-by: Oliver Simons <osimons@nvidia.com> Co-authored-by: Johannes Gäßler <johannesg@5d6.de>
2275 lines
75 KiB
C++
2275 lines
75 KiB
C++
#include "llama-graph.h"
|
|
|
|
#include "llama-impl.h"
|
|
#include "llama-batch.h"
|
|
#include "llama-cparams.h"
|
|
|
|
#include "llama-kv-cache.h"
|
|
#include "llama-kv-cache-iswa.h"
|
|
#include "llama-memory-hybrid.h"
|
|
#include "llama-memory-recurrent.h"
|
|
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <unordered_set>
|
|
|
|
void llm_graph_input_embd::set_input(const llama_ubatch * ubatch) {
|
|
if (ubatch->token) {
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
|
|
ggml_backend_tensor_set(tokens, ubatch->token, 0, n_tokens*ggml_element_size(tokens));
|
|
}
|
|
|
|
if (ubatch->embd) {
|
|
const int64_t n_embd = embd->ne[0];
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
|
|
ggml_backend_tensor_set(embd, ubatch->embd, 0, n_tokens*n_embd*ggml_element_size(embd));
|
|
}
|
|
}
|
|
|
|
bool llm_graph_input_embd::can_reuse(const llm_graph_params & params) {
|
|
bool res = true;
|
|
|
|
res &= (!tokens && !params.ubatch.token) || (tokens && tokens->ne[0] == params.ubatch.n_tokens);
|
|
res &= (!embd && !params.ubatch.embd) || (embd && embd->ne[1] == params.ubatch.n_tokens);
|
|
|
|
return res;
|
|
}
|
|
|
|
void llm_graph_input_pos::set_input(const llama_ubatch * ubatch) {
|
|
if (ubatch->pos && pos) {
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
|
|
if (ubatch->token && n_pos_per_embd == 4) {
|
|
// in case we're using M-RoPE with text tokens, convert the 1D positions to 4D
|
|
// the 3 first dims are the same, and 4th dim is all 0
|
|
std::vector<llama_pos> pos_data(n_tokens*n_pos_per_embd);
|
|
// copy the first dimension
|
|
for (int i = 0; i < n_tokens; ++i) {
|
|
pos_data[ i] = ubatch->pos[i];
|
|
pos_data[ n_tokens + i] = ubatch->pos[i];
|
|
pos_data[2 * n_tokens + i] = ubatch->pos[i];
|
|
pos_data[3 * n_tokens + i] = 0; // 4th dim is 0
|
|
}
|
|
ggml_backend_tensor_set(pos, pos_data.data(), 0, pos_data.size()*ggml_element_size(pos));
|
|
} else {
|
|
ggml_backend_tensor_set(pos, ubatch->pos, 0, n_tokens*n_pos_per_embd*ggml_element_size(pos));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool llm_graph_input_pos::can_reuse(const llm_graph_params & params) {
|
|
bool res = true;
|
|
|
|
res &= pos->ne[0] == params.ubatch.n_tokens*n_pos_per_embd;
|
|
|
|
return res;
|
|
}
|
|
|
|
void llm_graph_input_attn_temp::set_input(const llama_ubatch * ubatch) {
|
|
if (ubatch->pos && attn_scale) {
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
|
|
GGML_ASSERT(f_attn_temp_scale != 0.0f);
|
|
GGML_ASSERT(n_attn_temp_floor_scale != 0);
|
|
|
|
std::vector<float> attn_scale_data(n_tokens, 0.0f);
|
|
for (int i = 0; i < n_tokens; ++i) {
|
|
const float pos = ubatch->pos[i];
|
|
attn_scale_data[i] = std::log(
|
|
std::floor((pos + f_attn_temp_offset) / n_attn_temp_floor_scale) + 1.0
|
|
) * f_attn_temp_scale + 1.0;
|
|
}
|
|
|
|
ggml_backend_tensor_set(attn_scale, attn_scale_data.data(), 0, n_tokens*ggml_element_size(attn_scale));
|
|
}
|
|
}
|
|
|
|
void llm_graph_input_pos_bucket::set_input(const llama_ubatch * ubatch) {
|
|
if (pos_bucket) {
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(pos_bucket->buffer));
|
|
GGML_ASSERT(!ubatch->equal_seqs()); // TODO: use ubatch->n_seqs instead of failing
|
|
|
|
int32_t * data = (int32_t *) pos_bucket->data;
|
|
|
|
for (int h = 0; h < 1; ++h) {
|
|
for (int j = 0; j < n_tokens; ++j) {
|
|
for (int i = 0; i < n_tokens; ++i) {
|
|
data[h*(n_tokens*n_tokens) + j*n_tokens + i] = llama_relative_position_bucket(ubatch->pos[i], ubatch->pos[j], hparams.n_rel_attn_bkts, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void llm_graph_input_pos_bucket_kv::set_input(const llama_ubatch * ubatch) {
|
|
if (pos_bucket) {
|
|
mctx->set_input_pos_bucket(pos_bucket, ubatch);
|
|
}
|
|
}
|
|
|
|
void llm_graph_input_out_ids::set_input(const llama_ubatch * ubatch) {
|
|
GGML_ASSERT(out_ids);
|
|
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(out_ids->buffer));
|
|
int32_t * data = (int32_t *) out_ids->data;
|
|
|
|
if (n_outputs == n_tokens) {
|
|
for (int i = 0; i < n_tokens; ++i) {
|
|
data[i] = i;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
GGML_ASSERT(ubatch->output);
|
|
|
|
int n_outputs = 0;
|
|
|
|
for (int i = 0; i < n_tokens; ++i) {
|
|
if (ubatch->output[i]) {
|
|
data[n_outputs++] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool llm_graph_input_out_ids::can_reuse(const llm_graph_params & params) {
|
|
bool res = true;
|
|
|
|
res &= n_outputs == params.n_outputs;
|
|
|
|
return res;
|
|
}
|
|
|
|
void llm_graph_input_mean::set_input(const llama_ubatch * ubatch) {
|
|
if (cparams.embeddings && cparams.pooling_type == LLAMA_POOLING_TYPE_MEAN) {
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
const int64_t n_seq_tokens = ubatch->n_seq_tokens;
|
|
const int64_t n_seqs_unq = ubatch->n_seqs_unq;
|
|
|
|
GGML_ASSERT(mean);
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(mean->buffer));
|
|
|
|
float * data = (float *) mean->data;
|
|
memset(mean->data, 0, n_tokens*n_seqs_unq*ggml_element_size(mean));
|
|
|
|
std::vector<uint64_t> sums(n_seqs_unq, 0);
|
|
for (int i = 0; i < n_tokens; i += n_seq_tokens) {
|
|
for (int s = 0; s < ubatch->n_seq_id[i]; ++s) {
|
|
const llama_seq_id seq_id = ubatch->seq_id[i][s];
|
|
const int32_t seq_idx = ubatch->seq_idx[seq_id];
|
|
|
|
sums[seq_idx] += ubatch->n_seq_tokens;
|
|
}
|
|
}
|
|
|
|
std::vector<float> div(n_seqs_unq, 0.0f);
|
|
for (int s = 0; s < n_seqs_unq; ++s) {
|
|
const uint64_t sum = sums[s];
|
|
if (sum > 0) {
|
|
div[s] = 1.0f/float(sum);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < n_tokens; i += n_seq_tokens) {
|
|
for (int s = 0; s < ubatch->n_seq_id[i]; ++s) {
|
|
const llama_seq_id seq_id = ubatch->seq_id[i][s];
|
|
const int32_t seq_idx = ubatch->seq_idx[seq_id];
|
|
|
|
for (int j = 0; j < n_seq_tokens; ++j) {
|
|
data[seq_idx*n_tokens + i + j] = div[seq_idx];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void llm_graph_input_cls::set_input(const llama_ubatch * ubatch) {
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
const int64_t n_seqs_unq = ubatch->n_seqs_unq;
|
|
|
|
if (cparams.embeddings && (
|
|
cparams.pooling_type == LLAMA_POOLING_TYPE_CLS ||
|
|
cparams.pooling_type == LLAMA_POOLING_TYPE_RANK ||
|
|
cparams.pooling_type == LLAMA_POOLING_TYPE_LAST
|
|
)) {
|
|
GGML_ASSERT(cls);
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(cls->buffer));
|
|
|
|
uint32_t * data = (uint32_t *) cls->data;
|
|
memset(cls->data, 0, n_seqs_unq*ggml_element_size(cls));
|
|
|
|
std::vector<int> target_pos(n_seqs_unq, -1);
|
|
std::vector<int> target_row(n_seqs_unq, -1);
|
|
|
|
const bool last = (
|
|
cparams.pooling_type == LLAMA_POOLING_TYPE_LAST ||
|
|
(cparams.pooling_type == LLAMA_POOLING_TYPE_RANK && arch == LLM_ARCH_QWEN3) // qwen3 reranking & embedding models use last token
|
|
);
|
|
|
|
for (int i = 0; i < n_tokens; ++i) {
|
|
const llama_pos pos = ubatch->pos[i];
|
|
|
|
for (int s = 0; s < ubatch->n_seq_id[i]; ++s) {
|
|
const llama_seq_id seq_id = ubatch->seq_id[i][s];
|
|
const int32_t seq_idx = ubatch->seq_idx[seq_id];
|
|
|
|
if (
|
|
(target_pos[seq_idx] == -1) ||
|
|
( last && pos >= target_pos[seq_idx]) ||
|
|
(!last && pos < target_pos[seq_idx])
|
|
) {
|
|
target_pos[seq_idx] = pos;
|
|
target_row[seq_idx] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int s = 0; s < n_seqs_unq; ++s) {
|
|
if (target_row[s] >= 0) {
|
|
data[s] = target_row[s];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void llm_graph_input_rs::set_input(const llama_ubatch * ubatch) {
|
|
GGML_UNUSED(ubatch);
|
|
|
|
const int64_t n_rs = mctx->get_n_rs();
|
|
|
|
if (s_copy) {
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(s_copy->buffer));
|
|
int32_t * data = (int32_t *) s_copy->data;
|
|
|
|
// assuming copy destinations ALWAYS happen ONLY on the cells between head and head+n
|
|
for (uint32_t i = 0; i < n_rs; ++i) {
|
|
data[i] = mctx->s_copy(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool llm_graph_input_rs::can_reuse(const llm_graph_params & params) {
|
|
const auto * mctx = static_cast<const llama_memory_recurrent_context *>(params.mctx);
|
|
|
|
this->mctx = mctx;
|
|
|
|
bool res = true;
|
|
|
|
res &= s_copy->ne[0] == mctx->get_n_rs();
|
|
|
|
res &= s_copy_main->ne[0] == params.ubatch.n_seqs;
|
|
res &= s_copy_extra->ne[0] == mctx->get_n_rs() - params.ubatch.n_seqs;
|
|
|
|
res &= head == mctx->get_head();
|
|
res &= rs_z == mctx->get_rs_z();
|
|
|
|
return res;
|
|
}
|
|
|
|
void llm_graph_input_cross_embd::set_input(const llama_ubatch * ubatch) {
|
|
GGML_UNUSED(ubatch);
|
|
|
|
if (cross_embd && !cross->v_embd.empty()) {
|
|
assert(cross_embd->type == GGML_TYPE_F32);
|
|
|
|
ggml_backend_tensor_set(cross_embd, cross->v_embd.data(), 0, ggml_nbytes(cross_embd));
|
|
}
|
|
}
|
|
|
|
static void print_mask(const float * data, int64_t n_tokens, int64_t n_kv, int64_t n_swa, llama_swa_type swa_type) {
|
|
LLAMA_LOG_DEBUG("%s: === Attention mask ===\n", __func__);
|
|
const char * swa_type_str = "unknown";
|
|
|
|
switch (swa_type) {
|
|
case LLAMA_SWA_TYPE_NONE: swa_type_str = "LLAMA_SWA_TYPE_NONE"; break;
|
|
case LLAMA_SWA_TYPE_STANDARD: swa_type_str = "LLAMA_SWA_TYPE_STANDARD"; break;
|
|
case LLAMA_SWA_TYPE_CHUNKED: swa_type_str = "LLAMA_SWA_TYPE_CHUNKED"; break;
|
|
case LLAMA_SWA_TYPE_SYMMETRIC: swa_type_str = "LLAMA_SWA_TYPE_SYMMETRIC"; break;
|
|
};
|
|
|
|
LLAMA_LOG_DEBUG("%s: n_swa : %d, n_kv: %d, swq_type: %s\n", __func__, (int)n_swa, (int)n_kv, swa_type_str);
|
|
LLAMA_LOG_DEBUG("%s: '0' = can attend, '∞' = masked\n", __func__);
|
|
LLAMA_LOG_DEBUG("%s: Rows = query tokens, Columns = key/value tokens\n\n", __func__);
|
|
|
|
LLAMA_LOG_DEBUG(" ");
|
|
for (int j = 0; j < std::min((int64_t)20, n_kv); ++j) {
|
|
LLAMA_LOG_DEBUG("%2d", j);
|
|
}
|
|
LLAMA_LOG_DEBUG("\n");
|
|
|
|
for (int i = 0; i < std::min((int64_t)20, n_tokens); ++i) {
|
|
LLAMA_LOG_DEBUG(" %2d ", i);
|
|
for (int j = 0; j < std::min((int64_t)20, n_kv); ++j) {
|
|
float val = data[i * n_kv + j];
|
|
if (val == -INFINITY) {
|
|
LLAMA_LOG_DEBUG(" ∞");
|
|
} else {
|
|
LLAMA_LOG_DEBUG(" 0");
|
|
}
|
|
}
|
|
LLAMA_LOG_DEBUG("\n");
|
|
}
|
|
}
|
|
|
|
void llm_graph_input_attn_no_cache::set_input(const llama_ubatch * ubatch) {
|
|
const int64_t n_kv = ubatch->n_tokens;
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
|
|
const auto fill_mask = [&](float * data, int n_swa, llama_swa_type swa_type) {
|
|
for (int h = 0; h < 1; ++h) {
|
|
for (int i1 = 0; i1 < n_tokens; ++i1) {
|
|
const llama_seq_id s1 = ubatch->seq_id[i1][0];
|
|
const llama_pos p1 = ubatch->pos[i1];
|
|
|
|
const uint64_t idst = h*(n_kv*n_tokens) + i1*n_kv;
|
|
|
|
for (int i0 = 0; i0 < n_tokens; ++i0) {
|
|
const llama_seq_id s0 = ubatch->seq_id[i0][0];
|
|
const llama_pos p0 = ubatch->pos[i0];
|
|
|
|
// mask different sequences
|
|
if (s0 != s1) {
|
|
continue;
|
|
}
|
|
|
|
// mask future tokens
|
|
if (cparams.causal_attn && p0 > p1) {
|
|
continue;
|
|
}
|
|
|
|
// apply SWA if any
|
|
if (llama_hparams::is_masked_swa(n_swa, swa_type, p0, p1)) {
|
|
continue;
|
|
}
|
|
|
|
data[idst + i0] = hparams.use_alibi ? -std::abs(p0 - p1) : 0.0f;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
{
|
|
GGML_ASSERT(self_kq_mask);
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(self_kq_mask->buffer));
|
|
|
|
float * data = (float *) self_kq_mask->data;
|
|
|
|
std::fill(data, data + ggml_nelements(self_kq_mask), -INFINITY);
|
|
|
|
fill_mask(data, 0, LLAMA_SWA_TYPE_NONE);
|
|
|
|
if (debug) {
|
|
print_mask(data, n_tokens, n_kv, 0, LLAMA_SWA_TYPE_NONE);
|
|
}
|
|
}
|
|
|
|
if (hparams.swa_type != LLAMA_SWA_TYPE_NONE) {
|
|
GGML_ASSERT(self_kq_mask_swa);
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(self_kq_mask_swa->buffer));
|
|
|
|
float * data = (float *) self_kq_mask_swa->data;
|
|
|
|
std::fill(data, data + ggml_nelements(self_kq_mask_swa), -INFINITY);
|
|
|
|
fill_mask(data, hparams.n_swa, hparams.swa_type);
|
|
|
|
if (debug) {
|
|
print_mask(data, n_tokens, n_kv, hparams.n_swa, hparams.swa_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
void llm_graph_input_attn_kv::set_input(const llama_ubatch * ubatch) {
|
|
mctx->set_input_k_idxs(self_k_idxs, ubatch);
|
|
mctx->set_input_v_idxs(self_v_idxs, ubatch);
|
|
|
|
mctx->set_input_kq_mask(self_kq_mask, ubatch, cparams.causal_attn);
|
|
}
|
|
|
|
bool llm_graph_input_attn_kv::can_reuse(const llm_graph_params & params) {
|
|
const auto * mctx = static_cast<const llama_kv_cache_context *>(params.mctx);
|
|
|
|
this->mctx = mctx;
|
|
|
|
bool res = true;
|
|
|
|
res &= self_k_idxs->ne[0] == params.ubatch.n_tokens;
|
|
//res &= self_v_idxs->ne[0] == params.ubatch.n_tokens; // TODO: need to move this to the unified cache and check there
|
|
|
|
res &= self_kq_mask->ne[0] == mctx->get_n_kv();
|
|
res &= self_kq_mask->ne[1] == params.ubatch.n_tokens;
|
|
|
|
return res;
|
|
}
|
|
|
|
void llm_graph_input_attn_kv_iswa::set_input(const llama_ubatch * ubatch) {
|
|
mctx->get_base()->set_input_k_idxs(self_k_idxs, ubatch);
|
|
mctx->get_base()->set_input_v_idxs(self_v_idxs, ubatch);
|
|
|
|
mctx->get_base()->set_input_kq_mask(self_kq_mask, ubatch, cparams.causal_attn);
|
|
|
|
mctx->get_swa()->set_input_k_idxs(self_k_idxs_swa, ubatch);
|
|
mctx->get_swa()->set_input_v_idxs(self_v_idxs_swa, ubatch);
|
|
|
|
mctx->get_swa()->set_input_kq_mask(self_kq_mask_swa, ubatch, cparams.causal_attn);
|
|
}
|
|
|
|
bool llm_graph_input_attn_kv_iswa::can_reuse(const llm_graph_params & params) {
|
|
const auto * mctx = static_cast<const llama_kv_cache_iswa_context *>(params.mctx);
|
|
|
|
this->mctx = mctx;
|
|
|
|
bool res = true;
|
|
|
|
res &= self_k_idxs->ne[0] == params.ubatch.n_tokens;
|
|
//res &= self_v_idxs->ne[0] == params.ubatch.n_tokens; // TODO: need to move this to the unified cache and check there
|
|
|
|
res &= self_k_idxs_swa->ne[0] == params.ubatch.n_tokens;
|
|
//res &= self_v_idxs_swa->ne[0] == params.ubatch.n_tokens; // TODO: need to move this to the unified cache and check there
|
|
|
|
res &= self_kq_mask->ne[0] == mctx->get_base()->get_n_kv();
|
|
res &= self_kq_mask->ne[1] == params.ubatch.n_tokens;
|
|
|
|
res &= self_kq_mask_swa->ne[0] == mctx->get_swa()->get_n_kv();
|
|
res &= self_kq_mask_swa->ne[1] == params.ubatch.n_tokens;
|
|
|
|
return res;
|
|
}
|
|
|
|
void llm_graph_input_attn_cross::set_input(const llama_ubatch * ubatch) {
|
|
GGML_ASSERT(cross_kq_mask);
|
|
|
|
const int64_t n_enc = cross_kq_mask->ne[0];
|
|
const int64_t n_tokens = ubatch->n_tokens;
|
|
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(cross_kq_mask->buffer));
|
|
GGML_ASSERT(!ubatch->equal_seqs()); // TODO: use ubatch->n_seqs instead of failing
|
|
|
|
float * data = (float *) cross_kq_mask->data;
|
|
|
|
for (int h = 0; h < 1; ++h) {
|
|
for (int i = 0; i < n_tokens; ++i) {
|
|
for (int j = 0; j < n_enc; ++j) {
|
|
float f = -INFINITY;
|
|
|
|
for (int s = 0; s < ubatch->n_seq_id[i]; ++s) {
|
|
const llama_seq_id seq_id = ubatch->seq_id[i][s];
|
|
|
|
if (cross->seq_ids_enc[j].find(seq_id) != cross->seq_ids_enc[j].end()) {
|
|
f = 0.0f;
|
|
}
|
|
}
|
|
|
|
data[h*(n_enc*n_tokens) + i*n_enc + j] = f;
|
|
}
|
|
}
|
|
|
|
for (int i = n_tokens; i < n_tokens; ++i) {
|
|
for (int j = 0; j < n_enc; ++j) {
|
|
data[h*(n_enc*n_tokens) + i*n_enc + j] = -INFINITY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void llm_graph_input_mem_hybrid::set_input(const llama_ubatch * ubatch) {
|
|
mctx->get_attn()->set_input_k_idxs(inp_attn->self_k_idxs, ubatch);
|
|
mctx->get_attn()->set_input_v_idxs(inp_attn->self_v_idxs, ubatch);
|
|
|
|
mctx->get_attn()->set_input_kq_mask(inp_attn->self_kq_mask, ubatch, cparams.causal_attn);
|
|
|
|
const int64_t n_rs = mctx->get_recr()->get_n_rs();
|
|
|
|
if (inp_rs->s_copy) {
|
|
GGML_ASSERT(ggml_backend_buffer_is_host(inp_rs->s_copy->buffer));
|
|
int32_t * data = (int32_t *) inp_rs->s_copy->data;
|
|
|
|
// assuming copy destinations ALWAYS happen ONLY on the cells between head and head+n
|
|
for (uint32_t i = 0; i < n_rs; ++i) {
|
|
data[i] = mctx->get_recr()->s_copy(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool llm_graph_input_mem_hybrid::can_reuse(const llm_graph_params & params) {
|
|
const auto * mctx = static_cast<const llama_memory_hybrid_context *>(params.mctx);
|
|
|
|
this->mctx = mctx;
|
|
|
|
bool res = true;
|
|
|
|
res &= inp_attn->self_k_idxs->ne[0] == params.ubatch.n_tokens;
|
|
//res &= inp_attn->self_v_idxs->ne[0] == params.ubatch.n_tokens; // TODO: need to move this to the unified cache and check there
|
|
|
|
res &= inp_attn->self_kq_mask->ne[0] == mctx->get_attn()->get_n_kv();
|
|
res &= inp_attn->self_kq_mask->ne[1] == params.ubatch.n_tokens;
|
|
|
|
res &= inp_rs->s_copy->ne[0] == mctx->get_recr()->get_n_rs();
|
|
|
|
res &= inp_rs->s_copy_main->ne[0] == params.ubatch.n_seqs;
|
|
res &= inp_rs->s_copy_extra->ne[0] == mctx->get_recr()->get_n_rs() - params.ubatch.n_seqs;
|
|
|
|
res &= inp_rs->head == mctx->get_recr()->get_head();
|
|
res &= inp_rs->rs_z == mctx->get_recr()->get_rs_z();
|
|
|
|
return res;
|
|
}
|
|
|
|
void llm_graph_input_sampling::set_input(const llama_ubatch * ubatch) {
|
|
// set the inputs only for the active samplers in the current ubatch
|
|
std::unordered_set<llama_seq_id> active_samplers;
|
|
for (uint32_t i = 0; i < ubatch->n_tokens; i++) {
|
|
if (ubatch->output[i]) {
|
|
llama_seq_id seq_id = ubatch->seq_id[i][0];
|
|
active_samplers.insert(seq_id);
|
|
}
|
|
}
|
|
|
|
for (auto seq_id : active_samplers) {
|
|
if (samplers.find(seq_id) == samplers.end()) {
|
|
continue;
|
|
}
|
|
|
|
auto & sampler = samplers[seq_id];
|
|
|
|
if (sampler->iface->backend_set_input) {
|
|
sampler->iface->backend_set_input(sampler);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool llm_graph_input_sampling::can_reuse(const llm_graph_params & params) {
|
|
if (samplers.size() != params.samplers.size()) {
|
|
return false;
|
|
}
|
|
|
|
for (const auto & [seq_id, sampler] : params.samplers) {
|
|
if (samplers[seq_id] != sampler) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// llm_graph_result
|
|
//
|
|
|
|
llm_graph_result::llm_graph_result(int64_t max_nodes) : max_nodes(max_nodes) {
|
|
reset();
|
|
|
|
const char * LLAMA_GRAPH_RESULT_DEBUG = getenv("LLAMA_GRAPH_RESULT_DEBUG");
|
|
debug = LLAMA_GRAPH_RESULT_DEBUG ? atoi(LLAMA_GRAPH_RESULT_DEBUG) : 0;
|
|
}
|
|
|
|
int64_t llm_graph_result::get_max_nodes() const {
|
|
return max_nodes;
|
|
}
|
|
|
|
void llm_graph_result::reset() {
|
|
t_tokens = nullptr;
|
|
t_logits = nullptr;
|
|
t_embd = nullptr;
|
|
t_embd_pooled = nullptr;
|
|
t_sampled.clear();
|
|
t_sampled_probs.clear();
|
|
t_sampled_logits.clear();
|
|
t_candidates.clear();
|
|
|
|
params = {};
|
|
|
|
inputs.clear();
|
|
|
|
buf_compute_meta.resize(ggml_tensor_overhead()*max_nodes + ggml_graph_overhead_custom(max_nodes, false));
|
|
|
|
ggml_init_params params = {
|
|
/*.mem_size =*/ buf_compute_meta.size(),
|
|
/*.mem_buffer =*/ buf_compute_meta.data(),
|
|
/*.no_alloc =*/ true,
|
|
};
|
|
|
|
ctx_compute.reset(ggml_init(params));
|
|
|
|
gf = ggml_new_graph_custom(ctx_compute.get(), max_nodes, false);
|
|
}
|
|
|
|
void llm_graph_result::set_inputs(const llama_ubatch * ubatch) {
|
|
for (auto & input : inputs) {
|
|
input->set_input(ubatch);
|
|
}
|
|
}
|
|
|
|
void llm_graph_result::set_outputs() {
|
|
if (t_logits != nullptr) {
|
|
ggml_set_output(t_logits);
|
|
}
|
|
if (t_embd != nullptr) {
|
|
ggml_set_output(t_embd);
|
|
}
|
|
if (t_embd_pooled != nullptr) {
|
|
ggml_set_output(t_embd_pooled);
|
|
}
|
|
for (auto & [seq_id, t] : t_sampled) {
|
|
if (t != nullptr) {
|
|
ggml_set_output(t);
|
|
}
|
|
}
|
|
for (auto & [seq_id, t] : t_sampled_probs) {
|
|
if (t != nullptr) {
|
|
ggml_set_output(t);
|
|
}
|
|
}
|
|
for (auto & [seq_id, t] : t_sampled_logits) {
|
|
if (t != nullptr) {
|
|
ggml_set_output(t);
|
|
}
|
|
}
|
|
for (auto & [seq_id, t] : t_candidates) {
|
|
if (t != nullptr) {
|
|
ggml_set_output(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool llm_graph_result::can_reuse(const llm_graph_params & params) {
|
|
if (!this->params.allow_reuse(params)) {
|
|
if (debug > 1) {
|
|
LLAMA_LOG_DEBUG("%s: cannot reuse graph due to incompatible graph parameters\n", __func__);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (debug > 1) {
|
|
LLAMA_LOG_DEBUG("%s: checking compatibility of %d inputs:\n", __func__, (int) inputs.size());
|
|
}
|
|
|
|
bool res = true;
|
|
|
|
for (auto & input : inputs) {
|
|
const bool cur = input->can_reuse(params);
|
|
|
|
if (debug > 1) {
|
|
LLAMA_LOG_DEBUG("%s: can_reuse = %d\n", "placeholder", cur);
|
|
}
|
|
|
|
res = res && cur;
|
|
}
|
|
|
|
if (debug > 0) {
|
|
LLAMA_LOG_DEBUG("%s: can reuse graph = %d\n", __func__, res);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
llm_graph_input_i * llm_graph_result::add_input(llm_graph_input_ptr input) {
|
|
inputs.emplace_back(std::move(input));
|
|
return inputs.back().get();
|
|
}
|
|
|
|
void llm_graph_result::set_params(const llm_graph_params & params) {
|
|
this->params = params;
|
|
}
|
|
|
|
//
|
|
// llm_graph_context
|
|
//
|
|
|
|
llm_graph_context::llm_graph_context(const llm_graph_params & params) :
|
|
arch (params.arch),
|
|
hparams (params.hparams),
|
|
cparams (params.cparams),
|
|
ubatch (params.ubatch),
|
|
n_embd (hparams.n_embd),
|
|
n_layer (hparams.n_layer),
|
|
n_rot (hparams.n_rot),
|
|
n_ctx (cparams.n_ctx),
|
|
n_head (hparams.n_head()),
|
|
n_head_kv (hparams.n_head_kv()),
|
|
n_embd_head_k (hparams.n_embd_head_k),
|
|
n_embd_k_gqa (hparams.n_embd_k_gqa()),
|
|
n_embd_head_v (hparams.n_embd_head_v),
|
|
n_embd_v_gqa (hparams.n_embd_v_gqa()),
|
|
n_expert (hparams.n_expert),
|
|
n_expert_used (cparams.warmup ? hparams.n_expert : hparams.n_expert_used),
|
|
freq_base (cparams.rope_freq_base),
|
|
freq_scale (cparams.rope_freq_scale),
|
|
ext_factor (cparams.yarn_ext_factor),
|
|
attn_factor (cparams.yarn_attn_factor),
|
|
beta_fast (cparams.yarn_beta_fast),
|
|
beta_slow (cparams.yarn_beta_slow),
|
|
norm_eps (hparams.f_norm_eps),
|
|
norm_rms_eps (hparams.f_norm_rms_eps),
|
|
n_tokens (ubatch.n_tokens),
|
|
n_outputs (params.n_outputs),
|
|
n_ctx_orig (cparams.n_ctx_orig_yarn),
|
|
pooling_type (cparams.pooling_type),
|
|
rope_type (hparams.rope_type),
|
|
sched (params.sched),
|
|
backend_cpu (params.backend_cpu),
|
|
cvec (params.cvec),
|
|
loras (params.loras),
|
|
mctx (params.mctx),
|
|
cross (params.cross),
|
|
samplers (params.samplers),
|
|
cb_func (params.cb),
|
|
res (params.res),
|
|
ctx0 (res->get_ctx()),
|
|
gf (res->get_gf()) {
|
|
res->set_params(params);
|
|
}
|
|
|
|
void llm_graph_context::cb(ggml_tensor * cur, const char * name, int il) const {
|
|
if (cb_func) {
|
|
cb_func(ubatch, cur, name, il);
|
|
}
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_cvec(
|
|
ggml_tensor * cur,
|
|
int il) const {
|
|
return cvec->apply_to(ctx0, cur, il);
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_lora_mm(
|
|
ggml_tensor * w,
|
|
ggml_tensor * cur) const {
|
|
ggml_tensor * res = ggml_mul_mat(ctx0, w, cur);
|
|
|
|
for (const auto & lora : *loras) {
|
|
llama_adapter_lora_weight * lw = lora.first->get_weight(w);
|
|
if (lw == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
const float adapter_scale = lora.second;
|
|
const float scale = lw->get_scale(lora.first->alpha, adapter_scale);
|
|
|
|
ggml_tensor * ab_cur = ggml_mul_mat(
|
|
ctx0, lw->b,
|
|
ggml_mul_mat(ctx0, lw->a, cur)
|
|
);
|
|
|
|
ab_cur = ggml_scale(ctx0, ab_cur, scale);
|
|
res = ggml_add(ctx0, res, ab_cur);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_lora_mm_id(
|
|
ggml_tensor * w, // ggml_tensor * as
|
|
ggml_tensor * cur, // ggml_tensor * b
|
|
ggml_tensor * ids) const {
|
|
ggml_tensor * res = ggml_mul_mat_id(ctx0, w, cur, ids);
|
|
for (const auto & lora : *loras) {
|
|
llama_adapter_lora_weight * lw = lora.first->get_weight(w);
|
|
if (lw == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
const float alpha = lora.first->alpha;
|
|
const float rank = (float) lw->b->ne[0];
|
|
const float scale = alpha ? lora.second * alpha / rank : lora.second;
|
|
|
|
ggml_tensor * ab_cur = ggml_mul_mat_id(
|
|
ctx0, lw->b,
|
|
ggml_mul_mat_id(ctx0, lw->a, cur, ids),
|
|
ids
|
|
);
|
|
|
|
ab_cur = ggml_scale(ctx0, ab_cur, scale);
|
|
res = ggml_add(ctx0, res, ab_cur);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_norm(
|
|
ggml_tensor * cur,
|
|
ggml_tensor * mw,
|
|
ggml_tensor * mb,
|
|
llm_norm_type type,
|
|
int il) const {
|
|
switch (type) {
|
|
case LLM_NORM: cur = ggml_norm (ctx0, cur, hparams.f_norm_eps); break;
|
|
case LLM_NORM_RMS: cur = ggml_rms_norm(ctx0, cur, hparams.f_norm_rms_eps); break;
|
|
case LLM_NORM_GROUP:
|
|
{
|
|
cur = ggml_reshape_3d(ctx0, cur, cur->ne[0], 1, cur->ne[1]);
|
|
cur = ggml_group_norm(ctx0, cur, hparams.n_norm_groups, hparams.f_norm_group_eps);
|
|
cur = ggml_reshape_2d(ctx0, cur, cur->ne[0], cur->ne[2]);
|
|
} break;
|
|
}
|
|
|
|
if (mw || mb) {
|
|
cb(cur, "norm", il);
|
|
}
|
|
|
|
if (mw) {
|
|
cur = ggml_mul(ctx0, cur, mw);
|
|
if (mb) {
|
|
cb(cur, "norm_w", il);
|
|
}
|
|
}
|
|
|
|
if (mb) {
|
|
cur = ggml_add(ctx0, cur, mb);
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_ffn(
|
|
ggml_tensor * cur,
|
|
ggml_tensor * up,
|
|
ggml_tensor * up_b,
|
|
ggml_tensor * up_s,
|
|
ggml_tensor * gate,
|
|
ggml_tensor * gate_b,
|
|
ggml_tensor * gate_s,
|
|
ggml_tensor * down,
|
|
ggml_tensor * down_b,
|
|
ggml_tensor * down_s,
|
|
ggml_tensor * act_scales,
|
|
llm_ffn_op_type type_op,
|
|
llm_ffn_gate_type type_gate,
|
|
int il) const {
|
|
ggml_tensor * tmp = up ? build_lora_mm(up, cur) : cur;
|
|
cb(tmp, "ffn_up", il);
|
|
|
|
if (up_b) {
|
|
tmp = ggml_add(ctx0, tmp, up_b);
|
|
cb(tmp, "ffn_up_b", il);
|
|
}
|
|
|
|
if (up_s) {
|
|
tmp = ggml_mul(ctx0, tmp, up_s);
|
|
cb(tmp, "ffn_up_s", il);
|
|
}
|
|
|
|
if (gate) {
|
|
switch (type_gate) {
|
|
case LLM_FFN_SEQ:
|
|
{
|
|
cur = build_lora_mm(gate, tmp);
|
|
cb(cur, "ffn_gate", il);
|
|
} break;
|
|
case LLM_FFN_PAR:
|
|
{
|
|
cur = build_lora_mm(gate, cur);
|
|
cb(cur, "ffn_gate", il);
|
|
} break;
|
|
}
|
|
|
|
if (gate_b) {
|
|
cur = ggml_add(ctx0, cur, gate_b);
|
|
cb(cur, "ffn_gate_b", il);
|
|
}
|
|
|
|
if (gate_s) {
|
|
cur = ggml_mul(ctx0, cur, gate_s);
|
|
cb(cur, "ffn_gate_s", il);
|
|
}
|
|
|
|
} else {
|
|
cur = tmp;
|
|
}
|
|
|
|
switch (type_op) {
|
|
case LLM_FFN_SILU:
|
|
if (gate && type_gate == LLM_FFN_PAR) {
|
|
cur = ggml_swiglu_split(ctx0, cur, tmp);
|
|
cb(cur, "ffn_swiglu", il);
|
|
type_gate = LLM_FFN_SEQ;
|
|
} else {
|
|
cur = ggml_silu(ctx0, cur);
|
|
cb(cur, "ffn_silu", il);
|
|
} break;
|
|
case LLM_FFN_GELU:
|
|
if (gate && type_gate == LLM_FFN_PAR) {
|
|
cur = ggml_geglu_split(ctx0, cur, tmp);
|
|
cb(cur, "ffn_geglu", il);
|
|
type_gate = LLM_FFN_SEQ;
|
|
} else {
|
|
cur = ggml_gelu(ctx0, cur);
|
|
cb(cur, "ffn_gelu", il);
|
|
if (act_scales != NULL) {
|
|
cur = ggml_div(ctx0, cur, act_scales);
|
|
cb(cur, "ffn_act", il);
|
|
}
|
|
} break;
|
|
case LLM_FFN_RELU:
|
|
if (gate && type_gate == LLM_FFN_PAR) {
|
|
cur = ggml_reglu_split(ctx0, cur, tmp);
|
|
cb(cur, "ffn_reglu", il);
|
|
type_gate = LLM_FFN_SEQ;
|
|
} else {
|
|
cur = ggml_relu(ctx0, cur);
|
|
cb(cur, "ffn_relu", il);
|
|
} break;
|
|
case LLM_FFN_RELU_SQR:
|
|
{
|
|
cur = ggml_relu(ctx0, cur);
|
|
cb(cur, "ffn_relu", il);
|
|
|
|
cur = ggml_sqr(ctx0, cur);
|
|
cb(cur, "ffn_sqr(relu)", il);
|
|
} break;
|
|
case LLM_FFN_SWIGLU:
|
|
{
|
|
cur = ggml_swiglu(ctx0, cur);
|
|
cb(cur, "ffn_swiglu", il);
|
|
} break;
|
|
case LLM_FFN_GEGLU:
|
|
{
|
|
cur = ggml_geglu(ctx0, cur);
|
|
cb(cur, "ffn_geglu", il);
|
|
} break;
|
|
case LLM_FFN_REGLU:
|
|
{
|
|
cur = ggml_reglu(ctx0, cur);
|
|
cb(cur, "ffn_reglu", il);
|
|
} break;
|
|
default:
|
|
GGML_ABORT("fatal error");
|
|
}
|
|
|
|
if (gate && type_gate == LLM_FFN_PAR) {
|
|
cur = ggml_mul(ctx0, cur, tmp);
|
|
cb(cur, "ffn_gate_par", il);
|
|
}
|
|
|
|
if (down) {
|
|
cur = build_lora_mm(down, cur);
|
|
if (arch == LLM_ARCH_GLM4 || arch == LLM_ARCH_GLM4_MOE) {
|
|
// GLM4 and GLM4_MOE seem to have numerical issues with half-precision accumulators
|
|
ggml_mul_mat_set_prec(cur, GGML_PREC_F32);
|
|
}
|
|
}
|
|
|
|
if (down_b) {
|
|
cb(cur, "ffn_down", il);
|
|
}
|
|
|
|
if (down_b) {
|
|
cur = ggml_add(ctx0, cur, down_b);
|
|
}
|
|
|
|
if (down_s) {
|
|
cur = ggml_mul(ctx0, cur, down_s);
|
|
cb(cur, "ffn_down_s", il);
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_moe_ffn(
|
|
ggml_tensor * cur,
|
|
ggml_tensor * gate_inp,
|
|
ggml_tensor * up_exps,
|
|
ggml_tensor * gate_exps,
|
|
ggml_tensor * down_exps,
|
|
ggml_tensor * exp_probs_b,
|
|
int64_t n_expert,
|
|
int64_t n_expert_used,
|
|
llm_ffn_op_type type_op,
|
|
bool norm_w,
|
|
bool scale_w,
|
|
float w_scale,
|
|
llama_expert_gating_func_type gating_op,
|
|
int il,
|
|
ggml_tensor * probs_in) const {
|
|
return build_moe_ffn(
|
|
cur,
|
|
gate_inp, /* gate_inp_b */ nullptr,
|
|
up_exps, /* up_exps_b */ nullptr,
|
|
gate_exps, /* gate_exps_b */ nullptr,
|
|
down_exps, /* down_exps_b */ nullptr,
|
|
exp_probs_b,
|
|
n_expert,
|
|
n_expert_used,
|
|
type_op,
|
|
norm_w,
|
|
scale_w,
|
|
w_scale,
|
|
gating_op,
|
|
il,
|
|
probs_in
|
|
);
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_moe_ffn(
|
|
ggml_tensor * cur,
|
|
ggml_tensor * gate_inp,
|
|
ggml_tensor * gate_inp_b,
|
|
ggml_tensor * up_exps,
|
|
ggml_tensor * up_exps_b,
|
|
ggml_tensor * gate_exps,
|
|
ggml_tensor * gate_exps_b,
|
|
ggml_tensor * down_exps,
|
|
ggml_tensor * down_exps_b,
|
|
ggml_tensor * exp_probs_b,
|
|
int64_t n_expert,
|
|
int64_t n_expert_used,
|
|
llm_ffn_op_type type_op,
|
|
bool norm_w,
|
|
bool scale_w,
|
|
float w_scale,
|
|
llama_expert_gating_func_type gating_op,
|
|
int il,
|
|
ggml_tensor * probs_in) const {
|
|
const int64_t n_embd = cur->ne[0];
|
|
const int64_t n_tokens = cur->ne[1];
|
|
const bool weight_before_ffn = arch == LLM_ARCH_LLAMA4; // for llama4, we apply the sigmoid-ed weights before the FFN
|
|
|
|
ggml_tensor * logits = nullptr;
|
|
|
|
if (probs_in == nullptr) {
|
|
logits = build_lora_mm(gate_inp, cur); // [n_expert, n_tokens]
|
|
cb(logits, "ffn_moe_logits", il);
|
|
} else {
|
|
logits = probs_in;
|
|
}
|
|
|
|
if (gate_inp_b) {
|
|
logits = ggml_add(ctx0, logits, gate_inp_b);
|
|
cb(logits, "ffn_moe_logits_biased", il);
|
|
}
|
|
|
|
ggml_tensor * probs = nullptr;
|
|
switch (gating_op) {
|
|
case LLAMA_EXPERT_GATING_FUNC_TYPE_SOFTMAX:
|
|
{
|
|
probs = ggml_soft_max(ctx0, logits); // [n_expert, n_tokens]
|
|
} break;
|
|
case LLAMA_EXPERT_GATING_FUNC_TYPE_SIGMOID:
|
|
{
|
|
probs = ggml_sigmoid(ctx0, logits); // [n_expert, n_tokens]
|
|
} break;
|
|
case LLAMA_EXPERT_GATING_FUNC_TYPE_SOFTMAX_WEIGHT:
|
|
{
|
|
probs = logits; // [n_expert, n_tokens]
|
|
} break;
|
|
default:
|
|
GGML_ABORT("fatal error");
|
|
}
|
|
cb(probs, "ffn_moe_probs", il);
|
|
|
|
// add experts selection bias - introduced in DeepSeek V3
|
|
// leave probs unbiased as it's later used to get expert weights
|
|
ggml_tensor * selection_probs = probs;
|
|
if (exp_probs_b != nullptr) {
|
|
selection_probs = ggml_add(ctx0, probs, exp_probs_b);
|
|
cb(selection_probs, "ffn_moe_probs_biased", il);
|
|
}
|
|
|
|
// llama4 doesn't have exp_probs_b, and sigmoid is only used after top_k
|
|
// see: https://github.com/meta-llama/llama-models/blob/699a02993512fb36936b1b0741e13c06790bcf98/models/llama4/moe.py#L183-L198
|
|
if (arch == LLM_ARCH_LLAMA4) {
|
|
selection_probs = logits;
|
|
}
|
|
|
|
if (arch == LLM_ARCH_GROVEMOE) {
|
|
selection_probs = ggml_sigmoid(ctx0, logits); // [n_expert, n_tokens]
|
|
cb(selection_probs, "ffn_moe_probs_biased", il);
|
|
}
|
|
|
|
// select top n_group_used expert groups
|
|
// https://huggingface.co/deepseek-ai/DeepSeek-V3/blob/e815299b0bcbac849fa540c768ef21845365c9eb/modeling_deepseek.py#L440-L457
|
|
if (hparams.n_expert_groups > 1 && n_tokens > 0) {
|
|
const int64_t n_exp_per_group = n_expert / hparams.n_expert_groups;
|
|
|
|
// organize experts into n_expert_groups
|
|
ggml_tensor * selection_groups = ggml_reshape_3d(ctx0, selection_probs, n_exp_per_group, hparams.n_expert_groups, n_tokens); // [n_exp_per_group, n_expert_groups, n_tokens]
|
|
|
|
ggml_tensor * group_scores = ggml_argsort_top_k(ctx0, selection_groups, 2); // [2, n_expert_groups, n_tokens]
|
|
group_scores = ggml_get_rows(ctx0, ggml_reshape_4d(ctx0, selection_groups, 1, selection_groups->ne[0], selection_groups->ne[1], selection_groups->ne[2]), group_scores); // [1, 2, n_expert_groups, n_tokens]
|
|
|
|
// get top n_group_used expert groups
|
|
group_scores = ggml_sum_rows(ctx0, ggml_reshape_3d(ctx0, group_scores, group_scores->ne[1], group_scores->ne[2], group_scores->ne[3])); // [1, n_expert_groups, n_tokens]
|
|
group_scores = ggml_reshape_2d(ctx0, group_scores, group_scores->ne[1], group_scores->ne[2]); // [n_expert_groups, n_tokens]
|
|
|
|
ggml_tensor * expert_groups = ggml_argsort_top_k(ctx0, group_scores, hparams.n_group_used); // [n_group_used, n_tokens]
|
|
cb(expert_groups, "ffn_moe_group_topk", il);
|
|
|
|
// mask out the other groups
|
|
selection_probs = ggml_get_rows(ctx0, selection_groups, expert_groups); // [n_exp_per_group, n_group_used, n_tokens]
|
|
selection_probs = ggml_set_rows(ctx0, ggml_fill(ctx0, selection_groups, -INFINITY), selection_probs, expert_groups); // [n_exp_per_group, n_expert_groups, n_tokens]
|
|
selection_probs = ggml_reshape_2d(ctx0, selection_probs, n_expert, n_tokens); // [n_expert, n_tokens]
|
|
cb(selection_probs, "ffn_moe_probs_masked", il);
|
|
}
|
|
|
|
// select experts
|
|
ggml_tensor * selected_experts = ggml_argsort_top_k(ctx0, selection_probs, n_expert_used); // [n_expert_used, n_tokens]
|
|
cb(selected_experts->src[0], "ffn_moe_argsort", il);
|
|
cb(selected_experts, "ffn_moe_topk", il);
|
|
|
|
if (arch == LLM_ARCH_GROVEMOE && n_expert != hparams.n_expert) {
|
|
// TODO: Use scalar div instead when/if implemented
|
|
ggml_tensor * f_sel = ggml_cast(ctx0, selected_experts, GGML_TYPE_F32);
|
|
selected_experts = ggml_cast(ctx0, ggml_scale(ctx0, f_sel, 1.0f / float(hparams.n_group_experts)), GGML_TYPE_I32);
|
|
probs = ggml_reshape_3d(ctx0, probs, 1, hparams.n_expert, n_tokens);
|
|
} else {
|
|
probs = ggml_reshape_3d(ctx0, probs, 1, n_expert, n_tokens);
|
|
}
|
|
|
|
ggml_tensor * weights = ggml_get_rows(ctx0, probs, selected_experts); // [1, n_expert_used, n_tokens]
|
|
cb(weights, "ffn_moe_weights", il);
|
|
|
|
|
|
if (gating_op == LLAMA_EXPERT_GATING_FUNC_TYPE_SOFTMAX_WEIGHT) {
|
|
weights = ggml_reshape_2d(ctx0, weights, n_expert_used, n_tokens);
|
|
weights = ggml_soft_max(ctx0, weights); // [n_expert_used, n_tokens]
|
|
weights = ggml_reshape_3d(ctx0, weights, 1, n_expert_used, n_tokens);
|
|
cb(weights, "ffn_moe_weights_softmax", il);
|
|
}
|
|
|
|
if (norm_w) {
|
|
weights = ggml_reshape_2d(ctx0, weights, n_expert_used, n_tokens);
|
|
|
|
ggml_tensor * weights_sum = ggml_sum_rows(ctx0, weights); // [1, n_tokens]
|
|
cb(weights_sum, "ffn_moe_weights_sum", il);
|
|
|
|
// Avoid division by zero, clamp to smallest number representable by F16
|
|
weights_sum = ggml_clamp(ctx0, weights_sum, 6.103515625e-5, INFINITY);
|
|
cb(weights_sum, "ffn_moe_weights_sum_clamped", il);
|
|
|
|
weights = ggml_div(ctx0, weights, weights_sum); // [n_expert_used, n_tokens]
|
|
cb(weights, "ffn_moe_weights_norm", il);
|
|
|
|
weights = ggml_reshape_3d(ctx0, weights, 1, n_expert_used, n_tokens);
|
|
}
|
|
if (scale_w) {
|
|
weights = ggml_scale(ctx0, weights, w_scale);
|
|
cb(weights, "ffn_moe_weights_scaled", il);
|
|
}
|
|
|
|
//call early so that topk-moe can be used
|
|
ggml_build_forward_expand(gf, weights);
|
|
|
|
cur = ggml_reshape_3d(ctx0, cur, n_embd, 1, n_tokens);
|
|
|
|
if (weight_before_ffn) {
|
|
// repeat cur to [n_embd, n_expert_used, n_tokens]
|
|
ggml_tensor * repeated = ggml_repeat_4d(ctx0, cur, n_embd, n_expert_used, n_tokens, 1);
|
|
cur = ggml_mul(ctx0, repeated, weights);
|
|
cb(cur, "ffn_moe_weighted", il);
|
|
}
|
|
|
|
ggml_tensor * up = build_lora_mm_id(up_exps, cur, selected_experts); // [n_ff, n_expert_used, n_tokens]
|
|
cb(up, "ffn_moe_up", il);
|
|
|
|
if (up_exps_b) {
|
|
up = ggml_add_id(ctx0, up, up_exps_b, selected_experts);
|
|
cb(up, "ffn_moe_up_biased", il);
|
|
}
|
|
|
|
ggml_tensor * experts = nullptr;
|
|
if (gate_exps) {
|
|
cur = build_lora_mm_id(gate_exps, cur, selected_experts); // [n_ff, n_expert_used, n_tokens]
|
|
cb(cur, "ffn_moe_gate", il);
|
|
} else {
|
|
cur = up;
|
|
}
|
|
|
|
if (gate_exps_b) {
|
|
cur = ggml_add_id(ctx0, cur, gate_exps_b, selected_experts);
|
|
cb(cur, "ffn_moe_gate_biased", il);
|
|
}
|
|
|
|
switch (type_op) {
|
|
case LLM_FFN_SILU:
|
|
if (gate_exps) {
|
|
cur = ggml_swiglu_split(ctx0, cur, up);
|
|
cb(cur, "ffn_moe_swiglu", il);
|
|
} else {
|
|
cur = ggml_silu(ctx0, cur);
|
|
cb(cur, "ffn_moe_silu", il);
|
|
} break;
|
|
case LLM_FFN_GELU:
|
|
if (gate_exps) {
|
|
cur = ggml_geglu_split(ctx0, cur, up);
|
|
cb(cur, "ffn_moe_geglu", il);
|
|
} else {
|
|
cur = ggml_gelu(ctx0, cur);
|
|
cb(cur, "ffn_moe_gelu", il);
|
|
} break;
|
|
case LLM_FFN_SWIGLU_OAI_MOE:
|
|
{
|
|
// TODO: move to hparams?
|
|
constexpr float alpha = 1.702f;
|
|
constexpr float limit = 7.0f;
|
|
cur = ggml_swiglu_oai(ctx0, cur, up, alpha, limit);
|
|
cb(cur, "ffn_moe_swiglu_oai", il);
|
|
} break;
|
|
case LLM_FFN_RELU:
|
|
if (gate_exps) {
|
|
cur = ggml_reglu_split(ctx0, cur, up);
|
|
cb(cur, "ffn_moe_reglu", il);
|
|
} else {
|
|
cur = ggml_relu(ctx0, cur);
|
|
cb(cur, "ffn_moe_relu", il);
|
|
} break;
|
|
case LLM_FFN_RELU_SQR:
|
|
if (gate_exps) {
|
|
// TODO: add support for gated squared relu
|
|
GGML_ABORT("fatal error: gated squared relu not implemented");
|
|
} else {
|
|
cur = ggml_relu(ctx0, cur);
|
|
cur = ggml_sqr(ctx0, cur);
|
|
cb(cur, "ffn_moe_relu_sqr", il);
|
|
} break;
|
|
default:
|
|
GGML_ABORT("fatal error");
|
|
}
|
|
|
|
experts = build_lora_mm_id(down_exps, cur, selected_experts); // [n_embd, n_expert_used, n_tokens]
|
|
cb(experts, "ffn_moe_down", il);
|
|
|
|
if (down_exps_b) {
|
|
experts = ggml_add_id(ctx0, experts, down_exps_b, selected_experts);
|
|
cb(experts, "ffn_moe_down_biased", il);
|
|
}
|
|
|
|
if (!weight_before_ffn) {
|
|
experts = ggml_mul(ctx0, experts, weights);
|
|
cb(cur, "ffn_moe_weighted", il);
|
|
}
|
|
|
|
ggml_tensor * cur_experts[LLAMA_MAX_EXPERTS] = { nullptr };
|
|
|
|
assert(n_expert_used > 0);
|
|
|
|
// order the views before the adds
|
|
for (uint32_t i = 0; i < hparams.n_expert_used; ++i) {
|
|
cur_experts[i] = ggml_view_2d(ctx0, experts, n_embd, n_tokens, experts->nb[2], i*experts->nb[1]);
|
|
|
|
ggml_build_forward_expand(gf, cur_experts[i]);
|
|
}
|
|
|
|
// aggregate experts
|
|
// note: here we explicitly use hparams.n_expert_used instead of n_expert_used
|
|
// to avoid potentially a large number of add nodes during warmup
|
|
// ref: https://github.com/ggml-org/llama.cpp/pull/14753
|
|
ggml_tensor * moe_out = cur_experts[0];
|
|
|
|
for (uint32_t i = 1; i < hparams.n_expert_used; ++i) {
|
|
moe_out = ggml_add(ctx0, moe_out, cur_experts[i]);
|
|
}
|
|
|
|
if (hparams.n_expert_used == 1) {
|
|
// avoid returning a non-contiguous tensor
|
|
moe_out = ggml_cont(ctx0, moe_out);
|
|
}
|
|
|
|
cb(moe_out, "ffn_moe_out", il);
|
|
|
|
return moe_out;
|
|
}
|
|
|
|
// input embeddings with optional lora
|
|
ggml_tensor * llm_graph_context::build_inp_embd(ggml_tensor * tok_embd) const {
|
|
const int64_t n_embd = hparams.n_embd_inp();
|
|
|
|
auto inp = std::make_unique<llm_graph_input_embd>();
|
|
|
|
ggml_tensor * cur = nullptr;
|
|
|
|
if (ubatch.token) {
|
|
inp->tokens = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, ubatch.n_tokens);
|
|
//cb(inp->tokens, "inp_tokens", -1);
|
|
ggml_set_input(inp->tokens);
|
|
res->t_tokens = inp->tokens;
|
|
|
|
cur = ggml_get_rows(ctx0, tok_embd, inp->tokens);
|
|
|
|
// apply lora for embedding tokens if needed
|
|
for (const auto & lora : *loras) {
|
|
llama_adapter_lora_weight * lw = lora.first->get_weight(tok_embd);
|
|
if (lw == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
const float adapter_scale = lora.second;
|
|
const float scale = lw->get_scale(lora.first->alpha, adapter_scale);
|
|
|
|
ggml_tensor * inpL_delta = ggml_scale(ctx0, ggml_mul_mat(
|
|
ctx0, lw->b, // non-transposed lora_b
|
|
ggml_get_rows(ctx0, lw->a, inp->tokens)
|
|
), scale);
|
|
|
|
cur = ggml_add(ctx0, cur, inpL_delta);
|
|
}
|
|
} else {
|
|
inp->embd = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_embd, ubatch.n_tokens);
|
|
ggml_set_input(inp->embd);
|
|
|
|
cur = inp->embd;
|
|
}
|
|
|
|
// For Granite architecture
|
|
if (hparams.f_embedding_scale != 0.0f) {
|
|
cur = ggml_scale(ctx0, cur, hparams.f_embedding_scale);
|
|
}
|
|
|
|
cb(cur, "inp_embd", -1);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_inp_pos() const {
|
|
auto inp = std::make_unique<llm_graph_input_pos>(hparams.n_pos_per_embd());
|
|
|
|
auto & cur = inp->pos;
|
|
|
|
cur = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, (int64_t)n_tokens*hparams.n_pos_per_embd());
|
|
ggml_set_input(cur);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_inp_attn_scale() const {
|
|
auto inp = std::make_unique<llm_graph_input_attn_temp>(hparams.n_attn_temp_floor_scale, hparams.f_attn_temp_scale, hparams.f_attn_temp_offset);
|
|
|
|
auto & cur = inp->attn_scale;
|
|
|
|
// this need to be 1x1xN for broadcasting
|
|
cur = ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, 1, 1, n_tokens);
|
|
ggml_set_input(cur);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_inp_out_ids() const {
|
|
// note: when all tokens are output, we could skip this optimization to spare the ggml_get_rows() calls,
|
|
// but this would make the graph topology depend on the number of output tokens, which can interere with
|
|
// features that require constant topology such as pipline parallelism
|
|
// ref: https://github.com/ggml-org/llama.cpp/pull/14275#issuecomment-2987424471
|
|
//if (n_outputs < n_tokens) {
|
|
// return nullptr;
|
|
//}
|
|
|
|
auto inp = std::make_unique<llm_graph_input_out_ids>(hparams, cparams, n_outputs);
|
|
|
|
auto & cur = inp->out_ids;
|
|
|
|
cur = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, n_outputs);
|
|
ggml_set_input(cur);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_inp_mean() const {
|
|
auto inp = std::make_unique<llm_graph_input_mean>(cparams);
|
|
|
|
auto & cur = inp->mean;
|
|
|
|
cur = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_tokens, ubatch.n_seqs_unq);
|
|
ggml_set_input(cur);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_inp_cls() const {
|
|
auto inp = std::make_unique<llm_graph_input_cls>(cparams, arch);
|
|
|
|
auto & cur = inp->cls;
|
|
|
|
cur = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, ubatch.n_seqs_unq);
|
|
ggml_set_input(cur);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_inp_cross_embd() const {
|
|
auto inp = std::make_unique<llm_graph_input_cross_embd>(cross);
|
|
|
|
auto & cur = inp->cross_embd;
|
|
|
|
// if we have the output embeddings from the encoder, use them directly
|
|
// TODO: needs more work to be correct, for now just use the tensor shape
|
|
//if (cross->t_embd) {
|
|
// cur = ggml_view_tensor(ctx0, cross->t_embd);
|
|
|
|
// return cur;
|
|
//}
|
|
|
|
const auto n_embd = !cross->v_embd.empty() ? cross->n_embd : hparams.n_embd_inp();
|
|
const auto n_enc = !cross->v_embd.empty() ? cross->n_enc : hparams.n_ctx_train;
|
|
|
|
cur = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_embd, n_enc);
|
|
ggml_set_input(cur);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_inp_pos_bucket_enc() const {
|
|
auto inp = std::make_unique<llm_graph_input_pos_bucket>(hparams);
|
|
|
|
auto & cur = inp->pos_bucket;
|
|
|
|
cur = ggml_new_tensor_2d(ctx0, GGML_TYPE_I32, n_tokens, n_tokens);
|
|
ggml_set_input(cur);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_inp_pos_bucket_dec() const {
|
|
const auto * mctx_cur = static_cast<const llama_kv_cache_context *>(mctx);
|
|
|
|
auto inp = std::make_unique<llm_graph_input_pos_bucket_kv>(hparams, mctx_cur);
|
|
|
|
const auto n_kv = mctx_cur->get_n_kv();
|
|
|
|
auto & cur = inp->pos_bucket;
|
|
|
|
cur = ggml_new_tensor_2d(ctx0, GGML_TYPE_I32, n_kv, n_tokens);
|
|
ggml_set_input(cur);
|
|
|
|
res->add_input(std::move(inp));
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_pos_bias(ggml_tensor * pos_bucket, ggml_tensor * attn_rel_b) const {
|
|
ggml_tensor * pos_bucket_1d = ggml_reshape_1d(ctx0, pos_bucket, pos_bucket->ne[0] * pos_bucket->ne[1]);
|
|
cb(pos_bucket_1d, "pos_bucket_1d", -1);
|
|
|
|
ggml_tensor * pos_bias = ggml_get_rows(ctx0, attn_rel_b, pos_bucket_1d);
|
|
|
|
pos_bias = ggml_reshape_3d(ctx0, pos_bias, pos_bias->ne[0], pos_bucket->ne[0], pos_bucket->ne[1]);
|
|
pos_bias = ggml_permute (ctx0, pos_bias, 2, 0, 1, 3);
|
|
pos_bias = ggml_cont (ctx0, pos_bias);
|
|
|
|
cb(pos_bias, "pos_bias", -1);
|
|
|
|
return pos_bias;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_attn_mha(
|
|
ggml_tensor * q,
|
|
ggml_tensor * k,
|
|
ggml_tensor * v,
|
|
ggml_tensor * kq_b,
|
|
ggml_tensor * kq_mask,
|
|
ggml_tensor * sinks,
|
|
ggml_tensor * v_mla,
|
|
float kq_scale,
|
|
int il) const {
|
|
const bool v_trans = v->nb[1] > v->nb[2];
|
|
|
|
// split the batch into streams if needed
|
|
const auto n_stream = k->ne[3];
|
|
|
|
q = ggml_view_4d(ctx0, q, q->ne[0], q->ne[1], q->ne[2]/n_stream, n_stream, q->nb[1], q->nb[2], q->nb[3]/n_stream, 0);
|
|
|
|
q = ggml_permute(ctx0, q, 0, 2, 1, 3);
|
|
k = ggml_permute(ctx0, k, 0, 2, 1, 3);
|
|
v = ggml_permute(ctx0, v, 0, 2, 1, 3);
|
|
|
|
ggml_tensor * cur;
|
|
|
|
if (cparams.flash_attn && kq_b == nullptr) {
|
|
GGML_ASSERT(kq_b == nullptr && "Flash attention does not support KQ bias yet");
|
|
|
|
if (v_trans) {
|
|
v = ggml_transpose(ctx0, v);
|
|
}
|
|
|
|
// this can happen when KV cache is not used (e.g. an embedding model with non-causal attn)
|
|
if (k->type == GGML_TYPE_F32) {
|
|
k = ggml_cast(ctx0, k, GGML_TYPE_F16);
|
|
}
|
|
|
|
if (v->type == GGML_TYPE_F32) {
|
|
v = ggml_cast(ctx0, v, GGML_TYPE_F16);
|
|
}
|
|
|
|
cur = ggml_flash_attn_ext(ctx0, q, k, v, kq_mask, kq_scale, hparams.f_max_alibi_bias,
|
|
hparams.attn_soft_cap ? hparams.f_attn_logit_softcapping : 0.0f);
|
|
cb(cur, LLAMA_TENSOR_NAME_FATTN, il);
|
|
|
|
ggml_flash_attn_ext_add_sinks(cur, sinks);
|
|
ggml_flash_attn_ext_set_prec (cur, GGML_PREC_F32);
|
|
|
|
if (v_mla) {
|
|
#if 0
|
|
// v_mla can be applied as a matrix-vector multiplication with broadcasting across dimension 3 == n_tokens.
|
|
// However, the code is optimized for dimensions 0 and 1 being large, so this is ineffient.
|
|
cur = ggml_reshape_4d(ctx0, cur, v_mla->ne[0], 1, n_head, n_tokens);
|
|
cur = ggml_mul_mat(ctx0, v_mla, cur);
|
|
#else
|
|
// It's preferable to do the calculation as a matrix-matrix multiplication with n_tokens in dimension 1.
|
|
// The permutations are noops and only change how the tensor data is interpreted.
|
|
cur = ggml_permute(ctx0, cur, 0, 2, 1, 3);
|
|
cur = ggml_mul_mat(ctx0, v_mla, cur);
|
|
cb(cur, "fattn_mla", il);
|
|
cur = ggml_permute(ctx0, cur, 0, 2, 1, 3);
|
|
cur = ggml_cont(ctx0, cur); // Needed because ggml_reshape_2d expects contiguous inputs.
|
|
#endif
|
|
}
|
|
|
|
cur = ggml_reshape_2d(ctx0, cur, cur->ne[0]*cur->ne[1], cur->ne[2]*cur->ne[3]);
|
|
} else {
|
|
ggml_tensor * kq = ggml_mul_mat(ctx0, k, q);
|
|
cb(kq, "kq", il);
|
|
|
|
// note: this op tends to require high floating point range
|
|
// while for some models F16 is enough, for others it is not, so we default to F32 here
|
|
ggml_mul_mat_set_prec(kq, GGML_PREC_F32);
|
|
|
|
if (arch == LLM_ARCH_GROK) {
|
|
// need to do the following:
|
|
// multiply by attn_output_multiplier
|
|
// and then :
|
|
// kq = 30 * tanh(kq / 30)
|
|
// before the softmax below
|
|
|
|
kq = ggml_tanh(ctx0, ggml_scale(ctx0, kq, hparams.f_attn_out_scale / hparams.f_attn_logit_softcapping));
|
|
cb(kq, "kq_tanh", il);
|
|
kq = ggml_scale(ctx0, kq, hparams.f_attn_logit_softcapping);
|
|
cb(kq, "kq_scaled", il);
|
|
}
|
|
|
|
if (hparams.attn_soft_cap) {
|
|
kq = ggml_scale(ctx0, kq, 1.0f / hparams.f_attn_logit_softcapping);
|
|
cb(kq, "kq_scaled_1", il);
|
|
kq = ggml_tanh (ctx0, kq);
|
|
cb(kq, "kq_tanh", il);
|
|
kq = ggml_scale(ctx0, kq, hparams.f_attn_logit_softcapping);
|
|
cb(kq, "kq_scaled_2", il);
|
|
}
|
|
|
|
if (kq_b) {
|
|
kq = ggml_add(ctx0, kq, kq_b);
|
|
cb(kq, "kq_plus_kq_b", il);
|
|
}
|
|
|
|
kq = ggml_soft_max_ext(ctx0, kq, kq_mask, kq_scale, hparams.f_max_alibi_bias);
|
|
ggml_soft_max_add_sinks(kq, sinks);
|
|
cb(kq, "kq_soft_max", il);
|
|
|
|
if (!v_trans) {
|
|
// note: avoid this branch
|
|
v = ggml_cont(ctx0, ggml_transpose(ctx0, v));
|
|
cb(v, "v_cont", il);
|
|
}
|
|
|
|
ggml_tensor * kqv = ggml_mul_mat(ctx0, v, kq);
|
|
cb(kqv, "kqv", il);
|
|
|
|
// for MLA with the absorption optimization, we need to "decompress" from MQA back to MHA
|
|
if (v_mla) {
|
|
kqv = ggml_mul_mat(ctx0, v_mla, kqv);
|
|
cb(kqv, "kqv_mla", il);
|
|
}
|
|
|
|
cur = ggml_permute(ctx0, kqv, 0, 2, 1, 3);
|
|
|
|
// recombine streams
|
|
cur = ggml_cont_2d(ctx0, cur, cur->ne[0]*cur->ne[1], cur->ne[2]*cur->ne[3]);
|
|
|
|
if (!cparams.offload_kqv) {
|
|
// all nodes between the KV store and the attention output are run on the CPU
|
|
ggml_backend_sched_set_tensor_backend(sched, cur, backend_cpu);
|
|
}
|
|
}
|
|
|
|
ggml_build_forward_expand(gf, cur);
|
|
|
|
return cur;
|
|
}
|
|
|
|
llm_graph_input_attn_no_cache * llm_graph_context::build_attn_inp_no_cache() const {
|
|
auto inp = std::make_unique<llm_graph_input_attn_no_cache>(hparams, cparams);
|
|
|
|
// note: there is no KV cache, so the number of KV values is equal to the number of tokens in the batch
|
|
inp->self_kq_mask = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, n_tokens, n_tokens, 1, 1);
|
|
ggml_set_input(inp->self_kq_mask);
|
|
|
|
inp->self_kq_mask_cnv = cparams.flash_attn ? ggml_cast(ctx0, inp->self_kq_mask, GGML_TYPE_F16) : inp->self_kq_mask;
|
|
|
|
if (hparams.swa_type != LLAMA_SWA_TYPE_NONE) {
|
|
inp->self_kq_mask_swa = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, n_tokens, n_tokens, 1, 1);
|
|
ggml_set_input(inp->self_kq_mask_swa);
|
|
|
|
inp->self_kq_mask_swa_cnv = cparams.flash_attn ? ggml_cast(ctx0, inp->self_kq_mask_swa, GGML_TYPE_F16) : inp->self_kq_mask_swa;
|
|
} else {
|
|
inp->self_kq_mask_swa = nullptr;
|
|
inp->self_kq_mask_swa_cnv = nullptr;
|
|
}
|
|
|
|
return (llm_graph_input_attn_no_cache *) res->add_input(std::move(inp));
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_attn(
|
|
llm_graph_input_attn_no_cache * inp,
|
|
ggml_tensor * wo,
|
|
ggml_tensor * wo_b,
|
|
ggml_tensor * q_cur,
|
|
ggml_tensor * k_cur,
|
|
ggml_tensor * v_cur,
|
|
ggml_tensor * kq_b,
|
|
ggml_tensor * sinks,
|
|
ggml_tensor * v_mla,
|
|
float kq_scale,
|
|
int il) const {
|
|
GGML_UNUSED(n_tokens);
|
|
|
|
// these nodes are added to the graph together so that they are not reordered
|
|
// by doing so, the number of splits in the graph is reduced
|
|
ggml_build_forward_expand(gf, q_cur);
|
|
ggml_build_forward_expand(gf, k_cur);
|
|
ggml_build_forward_expand(gf, v_cur);
|
|
|
|
const bool is_swa = hparams.is_swa(il);
|
|
|
|
const auto & kq_mask = is_swa ? inp->get_kq_mask_swa() : inp->get_kq_mask();
|
|
|
|
// [TAG_NO_CACHE_PAD]
|
|
// TODO: if ubatch.equal_seqs() == true, we can split the three tensors below into ubatch.n_seqs_unq streams
|
|
// but it might not be worth it: https://github.com/ggml-org/llama.cpp/pull/15636
|
|
//assert(!ubatch.equal_seqs() || (k_cur->ne[3] == 1 && k_cur->ne[3] == ubatch.n_seqs_unq));
|
|
|
|
ggml_tensor * q = q_cur;
|
|
ggml_tensor * k = k_cur;
|
|
ggml_tensor * v = v_cur;
|
|
|
|
ggml_tensor * cur = build_attn_mha(q, k, v, kq_b, kq_mask, sinks, v_mla, kq_scale, il);
|
|
cb(cur, "kqv_out", il);
|
|
|
|
if (wo) {
|
|
cur = build_lora_mm(wo, cur);
|
|
}
|
|
|
|
if (wo_b) {
|
|
//cb(cur, "kqv_wo", il);
|
|
}
|
|
|
|
if (wo_b) {
|
|
cur = ggml_add(ctx0, cur, wo_b);
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
static std::unique_ptr<llm_graph_input_attn_kv> build_attn_inp_kv_impl(
|
|
ggml_context * ctx0,
|
|
const llama_ubatch & ubatch,
|
|
const llama_hparams & hparams,
|
|
const llama_cparams & cparams,
|
|
const llama_kv_cache_context * mctx_cur) {
|
|
|
|
auto inp = std::make_unique<llm_graph_input_attn_kv>(hparams, cparams, mctx_cur);
|
|
|
|
{
|
|
GGML_ASSERT(hparams.swa_type == LLAMA_SWA_TYPE_NONE && "Use llama_kv_cache_iswa for SWA");
|
|
|
|
const auto n_kv = mctx_cur->get_n_kv();
|
|
const auto n_tokens = ubatch.n_tokens;
|
|
const auto n_stream = cparams.kv_unified ? 1 : ubatch.n_seqs_unq;
|
|
|
|
inp->self_k_idxs = mctx_cur->build_input_k_idxs(ctx0, ubatch);
|
|
inp->self_v_idxs = mctx_cur->build_input_v_idxs(ctx0, ubatch);
|
|
|
|
inp->self_kq_mask = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, n_kv, n_tokens/n_stream, 1, n_stream);
|
|
ggml_set_input(inp->self_kq_mask);
|
|
|
|
inp->self_kq_mask_cnv = cparams.flash_attn ? ggml_cast(ctx0, inp->self_kq_mask, GGML_TYPE_F16) : inp->self_kq_mask;
|
|
}
|
|
|
|
return inp;
|
|
}
|
|
|
|
llm_graph_input_attn_kv * llm_graph_context::build_attn_inp_kv() const {
|
|
const auto * mctx_cur = static_cast<const llama_kv_cache_context *>(mctx);
|
|
|
|
auto inp = build_attn_inp_kv_impl(ctx0, ubatch, hparams, cparams, mctx_cur);
|
|
|
|
return (llm_graph_input_attn_kv *) res->add_input(std::move(inp));
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_attn(
|
|
llm_graph_input_attn_kv * inp,
|
|
ggml_tensor * wo,
|
|
ggml_tensor * wo_b,
|
|
ggml_tensor * q_cur,
|
|
ggml_tensor * k_cur,
|
|
ggml_tensor * v_cur,
|
|
ggml_tensor * kq_b,
|
|
ggml_tensor * sinks,
|
|
ggml_tensor * v_mla,
|
|
float kq_scale,
|
|
int il) const {
|
|
// these nodes are added to the graph together so that they are not reordered
|
|
// by doing so, the number of splits in the graph is reduced
|
|
// expand k later to enable rope fusion which directly writes into k-v cache
|
|
ggml_build_forward_expand(gf, q_cur);
|
|
ggml_build_forward_expand(gf, v_cur);
|
|
ggml_build_forward_expand(gf, k_cur);
|
|
|
|
const auto * mctx_cur = inp->mctx;
|
|
|
|
// store to KV cache
|
|
{
|
|
const auto & k_idxs = inp->get_k_idxs();
|
|
const auto & v_idxs = inp->get_v_idxs();
|
|
|
|
ggml_build_forward_expand(gf, mctx_cur->cpy_k(ctx0, k_cur, k_idxs, il));
|
|
ggml_build_forward_expand(gf, mctx_cur->cpy_v(ctx0, v_cur, v_idxs, il));
|
|
}
|
|
|
|
const auto & kq_mask = inp->get_kq_mask();
|
|
|
|
ggml_tensor * q = q_cur;
|
|
ggml_tensor * k = mctx_cur->get_k(ctx0, il);
|
|
ggml_tensor * v = mctx_cur->get_v(ctx0, il);
|
|
|
|
ggml_tensor * cur = build_attn_mha(q, k, v, kq_b, kq_mask, sinks, v_mla, kq_scale, il);
|
|
cb(cur, "kqv_out", il);
|
|
|
|
if (wo) {
|
|
cur = build_lora_mm(wo, cur);
|
|
if (arch == LLM_ARCH_GLM4 || arch == LLM_ARCH_GLM4_MOE) {
|
|
// GLM4 and GLM4_MOE seem to have numerical issues with half-precision accumulators
|
|
ggml_mul_mat_set_prec(cur, GGML_PREC_F32);
|
|
}
|
|
}
|
|
|
|
if (wo_b) {
|
|
cur = ggml_add(ctx0, cur, wo_b);
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_attn(
|
|
llm_graph_input_attn_kv_iswa * inp,
|
|
ggml_tensor * wo,
|
|
ggml_tensor * wo_b,
|
|
ggml_tensor * q_cur,
|
|
ggml_tensor * k_cur,
|
|
ggml_tensor * v_cur,
|
|
ggml_tensor * kq_b,
|
|
ggml_tensor * sinks,
|
|
ggml_tensor * v_mla,
|
|
float kq_scale,
|
|
int il) const {
|
|
// these nodes are added to the graph together so that they are not reordered
|
|
// by doing so, the number of splits in the graph is reduced
|
|
ggml_build_forward_expand(gf, q_cur);
|
|
|
|
if (k_cur) {
|
|
ggml_build_forward_expand(gf, k_cur);
|
|
}
|
|
|
|
if (v_cur) {
|
|
ggml_build_forward_expand(gf, v_cur);
|
|
}
|
|
|
|
const auto * mctx_iswa = inp->mctx;
|
|
|
|
const bool is_swa = hparams.is_swa(il);
|
|
|
|
const auto * mctx_cur = is_swa ? mctx_iswa->get_swa() : mctx_iswa->get_base();
|
|
|
|
// optionally store to KV cache
|
|
if (k_cur) {
|
|
const auto & k_idxs = is_swa ? inp->get_k_idxs_swa() : inp->get_k_idxs();
|
|
|
|
ggml_build_forward_expand(gf, mctx_cur->cpy_k(ctx0, k_cur, k_idxs, il));
|
|
}
|
|
|
|
if (v_cur) {
|
|
const auto & v_idxs = is_swa ? inp->get_v_idxs_swa() : inp->get_v_idxs();
|
|
|
|
ggml_build_forward_expand(gf, mctx_cur->cpy_v(ctx0, v_cur, v_idxs, il));
|
|
}
|
|
|
|
const auto & kq_mask = is_swa ? inp->get_kq_mask_swa() : inp->get_kq_mask();
|
|
|
|
ggml_tensor * q = q_cur;
|
|
ggml_tensor * k = mctx_cur->get_k(ctx0, il);
|
|
ggml_tensor * v = mctx_cur->get_v(ctx0, il);
|
|
|
|
ggml_tensor * cur = build_attn_mha(q, k, v, kq_b, kq_mask, sinks, v_mla, kq_scale, il);
|
|
cb(cur, "kqv_out", il);
|
|
|
|
if (wo) {
|
|
cur = build_lora_mm(wo, cur);
|
|
}
|
|
|
|
if (wo_b) {
|
|
//cb(cur, "kqv_wo", il);
|
|
}
|
|
|
|
if (wo_b) {
|
|
cur = ggml_add(ctx0, cur, wo_b);
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
llm_graph_input_attn_cross * llm_graph_context::build_attn_inp_cross() const {
|
|
auto inp = std::make_unique<llm_graph_input_attn_cross>(cross);
|
|
|
|
const int32_t n_enc = !cross->v_embd.empty() ? cross->n_enc : hparams.n_ctx_train;
|
|
|
|
inp->cross_kq_mask = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, n_enc, n_tokens, 1, 1);
|
|
ggml_set_input(inp->cross_kq_mask);
|
|
|
|
inp->cross_kq_mask_cnv = cparams.flash_attn ? ggml_cast(ctx0, inp->cross_kq_mask, GGML_TYPE_F16) : inp->cross_kq_mask;
|
|
|
|
return (llm_graph_input_attn_cross *) res->add_input(std::move(inp));
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_attn(
|
|
llm_graph_input_attn_cross * inp,
|
|
ggml_tensor * wo,
|
|
ggml_tensor * wo_b,
|
|
ggml_tensor * q_cur,
|
|
ggml_tensor * k_cur,
|
|
ggml_tensor * v_cur,
|
|
ggml_tensor * kq_b,
|
|
ggml_tensor * sinks,
|
|
ggml_tensor * v_mla,
|
|
float kq_scale,
|
|
int il) const {
|
|
// these nodes are added to the graph together so that they are not reordered
|
|
// by doing so, the number of splits in the graph is reduced
|
|
ggml_build_forward_expand(gf, q_cur);
|
|
ggml_build_forward_expand(gf, k_cur);
|
|
ggml_build_forward_expand(gf, v_cur);
|
|
|
|
const auto & kq_mask = inp->get_kq_mask_cross();
|
|
|
|
ggml_tensor * q = q_cur;
|
|
ggml_tensor * k = k_cur;
|
|
ggml_tensor * v = v_cur;
|
|
|
|
ggml_tensor * cur = build_attn_mha(q, k, v, kq_b, kq_mask, sinks, v_mla, kq_scale, il);
|
|
cb(cur, "kqv_out", il);
|
|
|
|
if (wo) {
|
|
cur = build_lora_mm(wo, cur);
|
|
}
|
|
|
|
if (wo_b) {
|
|
//cb(cur, "kqv_wo", il);
|
|
}
|
|
|
|
if (wo_b) {
|
|
cur = ggml_add(ctx0, cur, wo_b);
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
// TODO: maybe separate the inner implementation into a separate function
|
|
// like with the non-sliding window equivalent
|
|
// once sliding-window hybrid caches are a thing.
|
|
llm_graph_input_attn_kv_iswa * llm_graph_context::build_attn_inp_kv_iswa() const {
|
|
const auto * mctx_cur = static_cast<const llama_kv_cache_iswa_context *>(mctx);
|
|
|
|
auto inp = std::make_unique<llm_graph_input_attn_kv_iswa>(hparams, cparams, mctx_cur);
|
|
|
|
const auto n_stream = cparams.kv_unified ? 1 : ubatch.n_seqs_unq;
|
|
|
|
{
|
|
const auto n_kv = mctx_cur->get_base()->get_n_kv();
|
|
|
|
inp->self_k_idxs = mctx_cur->get_base()->build_input_k_idxs(ctx0, ubatch);
|
|
inp->self_v_idxs = mctx_cur->get_base()->build_input_v_idxs(ctx0, ubatch);
|
|
|
|
inp->self_kq_mask = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, n_kv, n_tokens/n_stream, 1, n_stream);
|
|
ggml_set_input(inp->self_kq_mask);
|
|
ggml_set_name(inp->self_kq_mask, "self_kq_mask");
|
|
|
|
inp->self_kq_mask_cnv = cparams.flash_attn ? ggml_cast(ctx0, inp->self_kq_mask, GGML_TYPE_F16) : inp->self_kq_mask;
|
|
ggml_set_name(inp->self_kq_mask_cnv, "self_kq_mask_cnv");
|
|
}
|
|
|
|
{
|
|
GGML_ASSERT(hparams.swa_type != LLAMA_SWA_TYPE_NONE && "Use llama_kv_cache for non-SWA");
|
|
|
|
const auto n_kv = mctx_cur->get_swa()->get_n_kv();
|
|
|
|
inp->self_k_idxs_swa = mctx_cur->get_swa()->build_input_k_idxs(ctx0, ubatch);
|
|
inp->self_v_idxs_swa = mctx_cur->get_swa()->build_input_v_idxs(ctx0, ubatch);
|
|
|
|
inp->self_kq_mask_swa = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, n_kv, n_tokens/n_stream, 1, n_stream);
|
|
ggml_set_input(inp->self_kq_mask_swa);
|
|
ggml_set_name(inp->self_kq_mask_swa, "self_kq_mask_swa");
|
|
|
|
inp->self_kq_mask_swa_cnv = cparams.flash_attn ? ggml_cast(ctx0, inp->self_kq_mask_swa, GGML_TYPE_F16) : inp->self_kq_mask_swa;
|
|
ggml_set_name(inp->self_kq_mask_swa_cnv, "self_kq_mask_swa_cnv");
|
|
}
|
|
|
|
return (llm_graph_input_attn_kv_iswa *) res->add_input(std::move(inp));
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_rs(
|
|
ggml_tensor * s,
|
|
ggml_tensor * state_copy_main,
|
|
ggml_tensor * state_copy_extra,
|
|
int32_t state_size,
|
|
int32_t n_seqs,
|
|
uint32_t n_rs,
|
|
uint32_t rs_head,
|
|
uint32_t rs_size,
|
|
int32_t rs_zero,
|
|
const llm_graph_get_rows_fn & get_state_rows) const {
|
|
|
|
ggml_tensor * states = ggml_reshape_2d(ctx0, s, state_size, rs_size);
|
|
|
|
// Clear a single state which will then be copied to the other cleared states.
|
|
// Note that this is a no-op when the view is zero-sized.
|
|
ggml_tensor * state_zero = ggml_view_1d(ctx0, states, state_size*(rs_zero >= 0), rs_zero*states->nb[1]*(rs_zero >= 0));
|
|
ggml_build_forward_expand(gf, ggml_scale_inplace(ctx0, state_zero, 0));
|
|
|
|
// copy states
|
|
// NOTE: assuming the copy destinations are ALL contained between rs_head and rs_head + n_rs
|
|
// {state_size, rs_size} -> {state_size, n_seqs}
|
|
ggml_tensor * output_states = get_state_rows(ctx0, states, state_copy_main);
|
|
ggml_build_forward_expand(gf, output_states);
|
|
|
|
// copy extra states which won't be changed further (between n_seqs and n_rs)
|
|
ggml_tensor * states_extra = ggml_get_rows(ctx0, states, state_copy_extra);
|
|
ggml_build_forward_expand(gf,
|
|
ggml_cpy(ctx0,
|
|
states_extra,
|
|
ggml_view_1d(ctx0, s, state_size*(n_rs - n_seqs), (rs_head + n_seqs)*state_size*ggml_element_size(s))));
|
|
|
|
return output_states;
|
|
}
|
|
|
|
static std::unique_ptr<llm_graph_input_rs> build_rs_inp_impl(
|
|
ggml_context * ctx0,
|
|
const llama_ubatch & ubatch,
|
|
const llama_memory_recurrent_context * mctx_cur) {
|
|
|
|
auto inp = std::make_unique<llm_graph_input_rs>(mctx_cur);
|
|
|
|
const int64_t n_rs = mctx_cur->get_n_rs();
|
|
const int64_t n_seqs = ubatch.n_seqs;
|
|
|
|
inp->s_copy = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, n_rs);
|
|
ggml_set_input(inp->s_copy);
|
|
|
|
inp->s_copy_main = ggml_view_1d(ctx0, inp->s_copy, n_seqs, 0);
|
|
inp->s_copy_extra = ggml_view_1d(ctx0, inp->s_copy, n_rs - n_seqs, n_seqs * inp->s_copy->nb[0]);
|
|
|
|
inp->head = mctx_cur->get_head();
|
|
inp->rs_z = mctx_cur->get_rs_z();
|
|
|
|
return inp;
|
|
}
|
|
|
|
llm_graph_input_rs * llm_graph_context::build_rs_inp() const {
|
|
const auto * mctx_cur = static_cast<const llama_memory_recurrent_context *>(mctx);
|
|
|
|
auto inp = build_rs_inp_impl(ctx0, ubatch, mctx_cur);
|
|
|
|
return (llm_graph_input_rs *) res->add_input(std::move(inp));
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_rs(
|
|
llm_graph_input_rs * inp,
|
|
ggml_tensor * s,
|
|
int32_t state_size,
|
|
int32_t n_seqs,
|
|
const llm_graph_get_rows_fn & get_state_rows) const {
|
|
const auto * kv_state = inp->mctx;
|
|
|
|
return build_rs(s, inp->s_copy_main, inp->s_copy_extra, state_size, n_seqs,
|
|
kv_state->get_n_rs(), kv_state->get_head(), kv_state->get_size(), kv_state->get_rs_z(),
|
|
get_state_rows);
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_rwkv_token_shift_load(
|
|
llm_graph_input_rs * inp,
|
|
const llama_ubatch & ubatch,
|
|
int il) const {
|
|
const auto * mctx_cur = static_cast<const llama_memory_recurrent_context *>(mctx);
|
|
|
|
const auto token_shift_count = hparams.token_shift_count;
|
|
|
|
const int64_t n_seqs = ubatch.n_seqs;
|
|
|
|
ggml_tensor * token_shift_all = mctx_cur->get_r_l(il);
|
|
|
|
ggml_tensor * token_shift = build_rs(
|
|
inp, token_shift_all,
|
|
hparams.n_embd_r(), n_seqs);
|
|
|
|
token_shift = ggml_reshape_3d(ctx0, token_shift, hparams.n_embd, token_shift_count, n_seqs);
|
|
|
|
return token_shift;
|
|
}
|
|
|
|
ggml_tensor * llm_graph_context::build_rwkv_token_shift_store(
|
|
ggml_tensor * token_shift,
|
|
const llama_ubatch & ubatch,
|
|
int il) const {
|
|
const auto * mctx_cur = static_cast<const llama_memory_recurrent_context *>(mctx);
|
|
|
|
const auto token_shift_count = hparams.token_shift_count;
|
|
const auto n_embd = hparams.n_embd;
|
|
|
|
const int64_t n_seqs = ubatch.n_seqs;
|
|
|
|
const auto kv_head = mctx_cur->get_head();
|
|
|
|
return ggml_cpy(
|
|
ctx0,
|
|
ggml_view_1d(ctx0, token_shift, n_embd * n_seqs * token_shift_count, 0),
|
|
ggml_view_1d(ctx0, mctx_cur->get_r_l(il), hparams.n_embd_r()*n_seqs, hparams.n_embd_r()*kv_head*ggml_element_size(mctx_cur->get_r_l(il)))
|
|
);
|
|
}
|
|
|
|
llm_graph_input_mem_hybrid * llm_graph_context::build_inp_mem_hybrid() const {
|
|
const auto * mctx_cur = static_cast<const llama_memory_hybrid_context *>(mctx);
|
|
|
|
auto inp_rs = build_rs_inp_impl (ctx0, ubatch, mctx_cur->get_recr());
|
|
auto inp_attn = build_attn_inp_kv_impl(ctx0, ubatch, hparams, cparams, mctx_cur->get_attn());
|
|
|
|
auto inp = std::make_unique<llm_graph_input_mem_hybrid>(cparams, std::move(inp_attn), std::move(inp_rs), mctx_cur);
|
|
|
|
return (llm_graph_input_mem_hybrid *) res->add_input(std::move(inp));
|
|
}
|
|
|
|
void llm_graph_context::build_dense_out(
|
|
ggml_tensor * dense_2,
|
|
ggml_tensor * dense_3) const {
|
|
if (!cparams.embeddings || dense_2 == nullptr || dense_3 == nullptr) {
|
|
return;
|
|
}
|
|
ggml_tensor * cur = res->t_embd_pooled != nullptr ? res->t_embd_pooled : res->t_embd;
|
|
GGML_ASSERT(cur != nullptr && "missing t_embd_pooled/t_embd");
|
|
|
|
cur = ggml_mul_mat(ctx0, dense_2, cur);
|
|
cur = ggml_mul_mat(ctx0, dense_3, cur);
|
|
cb(cur, "result_embd_pooled", -1);
|
|
res->t_embd_pooled = cur;
|
|
ggml_build_forward_expand(gf, cur);
|
|
}
|
|
|
|
|
|
void llm_graph_context::build_pooling(
|
|
ggml_tensor * cls,
|
|
ggml_tensor * cls_b,
|
|
ggml_tensor * cls_out,
|
|
ggml_tensor * cls_out_b) const {
|
|
if (!cparams.embeddings) {
|
|
return;
|
|
}
|
|
|
|
ggml_tensor * inp = res->t_embd;
|
|
|
|
//// find result_norm tensor for input
|
|
//for (int i = ggml_graph_n_nodes(gf) - 1; i >= 0; --i) {
|
|
// inp = ggml_graph_node(gf, i);
|
|
// if (strcmp(inp->name, "result_norm") == 0 || strcmp(inp->name, "result_embd") == 0) {
|
|
// break;
|
|
// }
|
|
|
|
// inp = nullptr;
|
|
//}
|
|
|
|
GGML_ASSERT(inp != nullptr && "missing result_norm/result_embd tensor");
|
|
|
|
ggml_tensor * cur;
|
|
|
|
switch (pooling_type) {
|
|
case LLAMA_POOLING_TYPE_NONE:
|
|
{
|
|
cur = inp;
|
|
} break;
|
|
case LLAMA_POOLING_TYPE_MEAN:
|
|
{
|
|
ggml_tensor * inp_mean = build_inp_mean();
|
|
cur = ggml_mul_mat(ctx0, ggml_cont(ctx0, ggml_transpose(ctx0, inp)), inp_mean);
|
|
} break;
|
|
case LLAMA_POOLING_TYPE_CLS:
|
|
case LLAMA_POOLING_TYPE_LAST:
|
|
{
|
|
ggml_tensor * inp_cls = build_inp_cls();
|
|
cur = ggml_get_rows(ctx0, inp, inp_cls);
|
|
} break;
|
|
case LLAMA_POOLING_TYPE_RANK:
|
|
{
|
|
ggml_tensor * inp_cls = build_inp_cls();
|
|
cur = ggml_get_rows(ctx0, inp, inp_cls);
|
|
|
|
// classification head
|
|
// https://github.com/huggingface/transformers/blob/5af7d41e49bbfc8319f462eb45253dcb3863dfb7/src/transformers/models/roberta/modeling_roberta.py#L1566
|
|
if (cls) {
|
|
cur = ggml_mul_mat(ctx0, cls, cur);
|
|
if (cls_b) {
|
|
cur = ggml_add(ctx0, cur, cls_b);
|
|
}
|
|
cur = ggml_tanh(ctx0, cur);
|
|
}
|
|
|
|
// some models don't have `cls_out`, for example: https://huggingface.co/jinaai/jina-reranker-v1-tiny-en
|
|
// https://huggingface.co/jinaai/jina-reranker-v1-tiny-en/blob/cb5347e43979c3084a890e3f99491952603ae1b7/modeling_bert.py#L884-L896
|
|
// Single layer classification head (direct projection)
|
|
// https://github.com/huggingface/transformers/blob/f4fc42216cd56ab6b68270bf80d811614d8d59e4/src/transformers/models/bert/modeling_bert.py#L1476
|
|
if (cls_out) {
|
|
cur = ggml_mul_mat(ctx0, cls_out, cur);
|
|
if (cls_out_b) {
|
|
cur = ggml_add(ctx0, cur, cls_out_b);
|
|
}
|
|
}
|
|
|
|
// softmax for qwen3 reranker
|
|
if (arch == LLM_ARCH_QWEN3) {
|
|
cur = ggml_soft_max(ctx0, cur);
|
|
}
|
|
} break;
|
|
default:
|
|
{
|
|
GGML_ABORT("unknown pooling type");
|
|
}
|
|
}
|
|
|
|
cb(cur, "result_embd_pooled", -1);
|
|
res->t_embd_pooled = cur;
|
|
|
|
ggml_build_forward_expand(gf, cur);
|
|
}
|
|
|
|
void llm_graph_context::build_sampling() const {
|
|
if (samplers.empty() || !res->t_logits) {
|
|
return;
|
|
}
|
|
|
|
auto inp_sampling = std::make_unique<llm_graph_input_sampling>(samplers);
|
|
res->add_input(std::move(inp_sampling));
|
|
|
|
std::map<llama_seq_id, int32_t> seq_to_logit_row;
|
|
int32_t logit_row_idx = 0;
|
|
|
|
for (uint32_t i = 0; i < ubatch.n_tokens; i++) {
|
|
if (ubatch.output[i]) {
|
|
llama_seq_id seq_id = ubatch.seq_id[i][0];
|
|
seq_to_logit_row[seq_id] = logit_row_idx;
|
|
logit_row_idx++;
|
|
}
|
|
}
|
|
|
|
// res->t_logits will contain logits for all tokens that want the logits calculated (logits=1 or output=1)
|
|
GGML_ASSERT(res->t_logits != nullptr && "missing t_logits tensor");
|
|
|
|
// add a dummy row of logits
|
|
// this trick makes the graph static, regardless of which samplers are activated
|
|
// this is important in order to minimize graph reallocations
|
|
// TODO: use `ggml_build_forward_select()` when available (https://github.com/ggml-org/llama.cpp/pull/18550)
|
|
ggml_tensor * logits_t = ggml_pad(ctx0, res->t_logits, 0, 1, 0, 0);
|
|
|
|
for (const auto & [seq_id, sampler] : samplers) {
|
|
const auto it = seq_to_logit_row.find(seq_id);
|
|
|
|
// inactive samplers always work on the first row
|
|
const auto row_idx = seq_to_logit_row.find(seq_id) != seq_to_logit_row.end() ? it->second : 0;
|
|
|
|
ggml_tensor * logits_seq = ggml_view_1d(ctx0, logits_t, logits_t->ne[0], row_idx * logits_t->nb[1]);
|
|
ggml_format_name(logits_seq, "logits_seq_%d", seq_id);
|
|
|
|
struct llama_sampler_data data = {
|
|
/*.logits =*/ logits_seq,
|
|
/*.probs =*/ nullptr,
|
|
/*.sampled =*/ nullptr,
|
|
/*.candidates =*/ nullptr,
|
|
};
|
|
|
|
assert(sampler->iface->backend_apply);
|
|
sampler->iface->backend_apply(sampler, ctx0, gf, &data);
|
|
|
|
if (data.sampled != nullptr) {
|
|
res->t_sampled[seq_id] = data.sampled;
|
|
ggml_build_forward_expand(gf, data.sampled);
|
|
}
|
|
|
|
if (data.probs != nullptr) {
|
|
res->t_sampled_probs[seq_id] = data.probs;
|
|
ggml_build_forward_expand(gf, data.probs);
|
|
}
|
|
|
|
if (data.logits != nullptr) {
|
|
res->t_sampled_logits[seq_id] = data.logits;
|
|
ggml_build_forward_expand(gf, data.logits);
|
|
}
|
|
|
|
if (data.candidates != nullptr) {
|
|
res->t_candidates[seq_id] = data.candidates;
|
|
ggml_build_forward_expand(gf, data.candidates);
|
|
}
|
|
}
|
|
|
|
// TODO: Call llama_sampler_accept_ggml after all samplers have been applied.
|
|
/*
|
|
for (const auto & [seq_id, sampler] : samplers) {
|
|
if (auto it = res->t_sampled.find(seq_id); it != res->t_sampled.end()) {
|
|
ggml_tensor * selected_token = it->second;
|
|
if (selected_token != nullptr) {
|
|
llama_sampler_accept_ggml(sampler, ctx0, gf, selected_token);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
int32_t llama_relative_position_bucket(llama_pos x, llama_pos y, uint64_t n_buckets, bool bidirectional) {
|
|
// TODO move to hparams if a T5 variant appears that uses a different value
|
|
const int64_t max_distance = 128;
|
|
|
|
if (bidirectional) {
|
|
n_buckets >>= 1;
|
|
}
|
|
|
|
const int64_t max_exact = n_buckets >> 1;
|
|
|
|
int32_t relative_position = x - y;
|
|
int32_t relative_bucket = 0;
|
|
|
|
if (bidirectional) {
|
|
relative_bucket += (relative_position > 0) * n_buckets;
|
|
relative_position = std::abs(relative_position);
|
|
} else {
|
|
relative_position = -std::min<int32_t>(relative_position, 0);
|
|
}
|
|
|
|
int32_t relative_position_if_large = floorf(max_exact + logf(1.0 * relative_position / max_exact) * (n_buckets - max_exact) / log(1.0 * max_distance / max_exact));
|
|
relative_position_if_large = std::min<int32_t>(relative_position_if_large, n_buckets - 1);
|
|
relative_bucket += (relative_position < max_exact ? relative_position : relative_position_if_large);
|
|
|
|
return relative_bucket;
|
|
}
|