char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Aaron Teoargs: officially deprecate --mmap|mlock|dio (#28334)14a9d09f7

char/aliceai-t5-moe
99.0 KiB2503 linesraw
1#include <algorithm>
2#include <array>
3#include <cassert>
4#include <chrono>
5#include <cinttypes>
6#include <clocale>
7#include <cmath>
8#include <cstdio>
9#include <cstdlib>
10#include <cstring>
11#include <ctime>
12#include <iterator>
13#include <map>
14#include <numeric>
15#include <regex>
16#include <sstream>
17#include <string>
18#include <thread>
19#include <vector>
20#include <unordered_set>
21
22#include "arg.h"
23#include "build-info.h"
24#include "common.h"
25#include "download.h"
26#include "fit.h"
27#include "ggml.h"
28#include "llama.h"
29#include "log.h"
30
31#ifdef _WIN32
32#    define WIN32_LEAN_AND_MEAN
33#    ifndef NOMINMAX
34#        define NOMINMAX
35#    endif
36#    include <windows.h>
37#endif
38
39// utils
40static uint64_t get_time_ns() {
41    using clock = std::chrono::high_resolution_clock;
42    return std::chrono::nanoseconds(clock::now().time_since_epoch()).count();
43}
44
45static bool tensor_buft_override_equal(const llama_model_tensor_buft_override& a, const llama_model_tensor_buft_override& b) {
46    if (a.pattern != b.pattern) {
47        // cString comparison that may be null
48        if (a.pattern == nullptr || b.pattern == nullptr) {
49            return false;
50        }
51        if (strcmp(a.pattern, b.pattern) != 0) {
52            return false;
53        }
54    }
55    if (a.buft != b.buft) {
56        return false;
57    }
58    return true;
59}
60
61static bool vec_tensor_buft_override_equal(const std::vector<llama_model_tensor_buft_override>& a, const std::vector<llama_model_tensor_buft_override>& b) {
62    if (a.size() != b.size()) {
63        return false;
64    }
65    for (size_t i = 0; i < a.size(); i++) {
66        if (!tensor_buft_override_equal(a[i], b[i])) {
67            return false;
68        }
69    }
70    return true;
71}
72
73static bool vec_vec_tensor_buft_override_equal(const std::vector<std::vector<llama_model_tensor_buft_override>>& a, const std::vector<std::vector<llama_model_tensor_buft_override>>& b) {
74    if (a.size() != b.size()) {
75        return false;
76    }
77    for (size_t i = 0; i < a.size(); i++) {
78        if (!vec_tensor_buft_override_equal(a[i], b[i])) {
79            return false;
80        }
81    }
82    return true;
83}
84
85template <class T> static std::string join(const std::vector<T> & values, const std::string & delim) {
86    std::ostringstream str;
87    for (size_t i = 0; i < values.size(); i++) {
88        str << values[i];
89        if (i < values.size() - 1) {
90            str << delim;
91        }
92    }
93    return str.str();
94}
95
96template <typename T, typename F> static std::vector<std::string> transform_to_str(const std::vector<T> & values, F f) {
97    std::vector<std::string> str_values;
98    std::transform(values.begin(), values.end(), std::back_inserter(str_values), f);
99    return str_values;
100}
101
102template <typename T> static T avg(const std::vector<T> & v) {
103    if (v.empty()) {
104        return 0;
105    }
106    T sum = std::accumulate(v.begin(), v.end(), T(0));
107    return sum / (T) v.size();
108}
109
110template <typename T> static T stdev(const std::vector<T> & v) {
111    if (v.size() <= 1) {
112        return 0;
113    }
114    T mean   = avg(v);
115    T sq_sum = std::inner_product(v.begin(), v.end(), v.begin(), T(0));
116    T stdev  = std::sqrt(sq_sum / (T) (v.size() - 1) - mean * mean * (T) v.size() / (T) (v.size() - 1));
117    return stdev;
118}
119
120static std::string get_cpu_info() {
121    std::vector<std::string> cpu_list;
122    for (size_t i = 0; i < ggml_backend_dev_count(); i++) {
123        auto * dev      = ggml_backend_dev_get(i);
124        auto   dev_type = ggml_backend_dev_type(dev);
125        if (dev_type == GGML_BACKEND_DEVICE_TYPE_CPU || dev_type == GGML_BACKEND_DEVICE_TYPE_ACCEL) {
126            cpu_list.push_back(ggml_backend_dev_description(dev));
127        }
128    }
129    return join(cpu_list, ", ");
130}
131
132static std::string get_gpu_info() {
133    std::vector<std::string> gpu_list;
134    for (size_t i = 0; i < ggml_backend_dev_count(); i++) {
135        auto * dev      = ggml_backend_dev_get(i);
136        auto   dev_type = ggml_backend_dev_type(dev);
137        if (dev_type == GGML_BACKEND_DEVICE_TYPE_GPU || dev_type == GGML_BACKEND_DEVICE_TYPE_IGPU) {
138            gpu_list.push_back(ggml_backend_dev_description(dev));
139        }
140    }
141    return join(gpu_list, ", ");
142}
143
144static std::vector<ggml_backend_dev_t> parse_devices_arg(const std::string & value) {
145    std::vector<ggml_backend_dev_t> devices;
146    std::string                     trimmed = string_strip(value);
147    if (trimmed.empty()) {
148        throw std::invalid_argument("no devices specified");
149    }
150    if (trimmed == "auto") {
151        return devices;
152    }
153
154    auto dev_names = string_split<std::string>(trimmed, '/');
155    if (dev_names.size() == 1 && string_strip(dev_names[0]) == "none") {
156        devices.push_back(nullptr);
157        return devices;
158    }
159
160    for (auto & name : dev_names) {
161        std::string dev_name = string_strip(name);
162        if (dev_name.empty()) {
163            throw std::invalid_argument("invalid device specification");
164        }
165        auto * dev = ggml_backend_dev_by_name(dev_name.c_str());
166        if (!dev || ggml_backend_dev_type(dev) == GGML_BACKEND_DEVICE_TYPE_CPU) {
167            throw std::invalid_argument(string_format("invalid device: %s", dev_name.c_str()));
168        }
169        devices.push_back(dev);
170    }
171
172    devices.push_back(nullptr);
173    return devices;
174}
175
176static void register_rpc_server_list(const std::string & servers) {
177    auto rpc_servers = string_split<std::string>(servers, ',');
178    if (rpc_servers.empty()) {
179        throw std::invalid_argument("no RPC servers specified");
180    }
181
182    auto * rpc_reg = ggml_backend_reg_by_name("RPC");
183    if (!rpc_reg) {
184        throw std::invalid_argument("failed to find RPC backend");
185    }
186
187    using add_rpc_server_fn = ggml_backend_reg_t (*)(const char * endpoint);
188    auto * ggml_backend_rpc_add_server_fn = (add_rpc_server_fn) ggml_backend_reg_get_proc_address(rpc_reg, "ggml_backend_rpc_add_server");
189    if (!ggml_backend_rpc_add_server_fn) {
190        throw std::invalid_argument("failed to find RPC add server function");
191    }
192    for (const auto & server : rpc_servers) {
193        auto reg = ggml_backend_rpc_add_server_fn(server.c_str());
194        ggml_backend_register(reg);
195    }
196}
197
198static std::string devices_to_string(const std::vector<ggml_backend_dev_t> & devices) {
199    if (devices.empty()) {
200        return "auto";
201    }
202
203    if (devices.size() == 1 && devices[0] == nullptr) {
204        return "none";
205    }
206
207    std::vector<std::string> names;
208    for (auto * dev : devices) {
209        if (dev == nullptr) {
210            break;
211        }
212        names.push_back(ggml_backend_dev_name(dev));
213    }
214
215    return join(names, "/");
216}
217
218// command line params
219enum output_formats { NONE, CSV, JSON, JSONL, MARKDOWN, SQL };
220
221static const char * output_format_str(output_formats format) {
222    switch (format) {
223        case NONE:
224            return "none";
225        case CSV:
226            return "csv";
227        case JSON:
228            return "json";
229        case JSONL:
230            return "jsonl";
231        case MARKDOWN:
232            return "md";
233        case SQL:
234            return "sql";
235        default:
236            GGML_ABORT("invalid output format");
237    }
238}
239
240static bool output_format_from_str(const std::string & s, output_formats & format) {
241    if (s == "none") {
242        format = NONE;
243    } else if (s == "csv") {
244        format = CSV;
245    } else if (s == "json") {
246        format = JSON;
247    } else if (s == "jsonl") {
248        format = JSONL;
249    } else if (s == "md") {
250        format = MARKDOWN;
251    } else if (s == "sql") {
252        format = SQL;
253    } else {
254        return false;
255    }
256    return true;
257}
258
259static const char * split_mode_str(llama_split_mode mode) {
260    switch (mode) {
261        case LLAMA_SPLIT_MODE_NONE:
262            return "none";
263        case LLAMA_SPLIT_MODE_LAYER:
264            return "layer";
265        case LLAMA_SPLIT_MODE_ROW:
266            return "row";
267        case LLAMA_SPLIT_MODE_TENSOR:
268            return "tensor";
269        default:
270            GGML_ABORT("invalid split mode");
271    }
272}
273
274static const char * lazy_mode_str(llama_lazy_mode mode) {
275    switch (mode) {
276        case LLAMA_LAZY_MODE_OFF:
277            return "off";
278        case LLAMA_LAZY_MODE_AUTO:
279            return "auto";
280        case LLAMA_LAZY_MODE_ON:
281            return "on";
282        default:
283            GGML_ABORT("invalid lazy mode");
284    }
285}
286
287static std::string pair_str(const std::pair<int, int> & p) {
288    static char buf[32];
289    snprintf(buf, sizeof(buf), "%d,%d", p.first, p.second);
290    return buf;
291}
292
293static std::vector<int> parse_int_range(const std::string & s, bool allow_negative = false) {
294    // first[-last[(+|*)step]]
295    std::regex range_regex(allow_negative
296        ? R"(^(-?\d+)(?:-(\d+)(?:([\+|\*])(\d+))?)?(?:,|$))"
297        : R"(^(\d+)(?:-(\d+)(?:([\+|\*])(\d+))?)?(?:,|$))");
298
299    std::smatch match;
300    std::string::const_iterator search_start(s.cbegin());
301    std::vector<int> result;
302    while (std::regex_search(search_start, s.cend(), match, range_regex)) {
303        int  first = std::stoi(match[1]);
304        int  last  = match[2].matched ? std::stoi(match[2]) : first;
305        char op    = match[3].matched ? match[3].str()[0] : '+';
306        int  step  = match[4].matched ? std::stoi(match[4]) : 1;
307
308        for (int i = first; i <= last;) {
309            result.push_back(i);
310
311            int prev_i = i;
312
313            if (op == '+') {
314                i += step;
315            } else if (op == '*') {
316                i *= step;
317            } else {
318                throw std::invalid_argument("invalid range format");
319            }
320
321            if (i <= prev_i) {
322                throw std::invalid_argument("invalid range");
323            }
324        }
325        search_start = match.suffix().first;
326    }
327
328    if (search_start != s.cend()) {
329        throw std::invalid_argument("invalid range format");
330    }
331
332    return result;
333}
334
335struct cmd_params {
336    std::vector<std::string>         model;
337    std::vector<std::string>         hf_repo;
338    std::vector<std::string>         hf_file;
339    std::string                      hf_token;
340    bool                             offline;
341    std::vector<int>                 n_prompt;
342    std::vector<int>                 n_gen;
343    std::vector<std::pair<int, int>> n_pg;
344    std::vector<int>                 n_depth;
345    std::vector<int>                 n_batch;
346    std::vector<int>                 n_ubatch;
347    std::vector<ggml_type>           type_k;
348    std::vector<ggml_type>           type_v;
349    std::vector<int>                 n_threads;
350    std::vector<std::string>         cpu_mask;
351    std::vector<bool>                cpu_strict;
352    std::vector<int>                 poll;
353    std::vector<int>                 n_gpu_layers;
354    std::vector<int>                 n_cpu_moe;
355    std::vector<llama_split_mode>    split_mode;
356    std::vector<llama_load_mode>     load_mode;
357    std::vector<llama_lazy_mode>     lazy_mode;
358    std::vector<int>                 main_gpu;
359    std::vector<bool>                no_kv_offload;
360    std::vector<llama_flash_attn_type> flash_attn;
361    std::vector<std::vector<ggml_backend_dev_t>> devices;
362    std::vector<std::vector<float>>  tensor_split;
363    std::vector<std::vector<llama_model_tensor_buft_override>> tensor_buft_overrides;
364    std::vector<bool>                embeddings;
365    std::vector<bool>                no_op_offload;
366    std::vector<bool>                no_host;
367    std::vector<size_t>              fit_params_target;
368    std::vector<uint32_t>            fit_params_min_ctx;
369    ggml_numa_strategy               numa;
370    int                              reps;
371    ggml_sched_priority              prio;
372    int                              delay;
373    bool                             verbose;
374    bool                             progress;
375    bool                             no_warmup;
376    output_formats                   output_format;
377    output_formats                   output_format_stderr;
378};
379
380static const cmd_params cmd_params_defaults = {
381    /* model                */ { "models/7B/ggml-model-q4_0.gguf" },
382    /* hf_repo              */ {},
383    /* hf_file              */ {},
384    /* hf_token             */ "",
385    /* offline              */ false,
386    /* n_prompt             */ { 512 },
387    /* n_gen                */ { 128 },
388    /* n_pg                 */ {},
389    /* n_depth              */ { 0 },
390    /* n_batch              */ { 2048 },
391    /* n_ubatch             */ { 512 },
392    /* type_k               */ { GGML_TYPE_F16 },
393    /* type_v               */ { GGML_TYPE_F16 },
394    /* n_threads            */ { common_cpu_get_num_math() },
395    /* cpu_mask             */ { "0x0" },
396    /* cpu_strict           */ { false },
397    /* poll                 */ { 50 },
398    /* n_gpu_layers         */ { -1 },
399    /* n_cpu_moe            */ { 0 },
400    /* split_mode           */ { LLAMA_SPLIT_MODE_LAYER },
401    /* load_mode            */ { LLAMA_LOAD_MODE_AUTO },
402    /* lazy_mode            */ { LLAMA_LAZY_MODE_AUTO },
403    /* main_gpu             */ { 0 },
404    /* no_kv_offload        */ { false },
405    /* flash_attn           */ { LLAMA_FLASH_ATTN_TYPE_AUTO },
406    /* devices              */ { {} },
407    /* tensor_split         */ { std::vector<float>(llama_max_devices(), 0.0f) },
408    /* tensor_buft_overrides*/ { std::vector<llama_model_tensor_buft_override>{ { nullptr, nullptr } } },
409    /* embeddings           */ { false },
410    /* no_op_offload        */ { false },
411    /* no_host              */ { false },
412    /* fit_params_target    */ { 0 },
413    /* fit_params_min_ctx   */ { 0 },
414    /* numa                 */ GGML_NUMA_STRATEGY_DISABLED,
415    /* reps                 */ 5,
416    /* prio                 */ GGML_SCHED_PRIO_NORMAL,
417    /* delay                */ 0,
418    /* verbose              */ false,
419    /* progress             */ false,
420    /* no_warmup            */ false,
421    /* output_format        */ MARKDOWN,
422    /* output_format_stderr */ NONE,
423};
424
425static void print_usage(int /* argc */, char ** argv) {
426    printf("usage: %s [options]\n", argv[0]);
427    printf("\n");
428    printf("options:\n");
429    printf("  -h, --help\n");
430    printf("  --numa <distribute|isolate|numactl>         numa mode (default: disabled)\n");
431    printf("  -r, --repetitions <n>                       number of times to repeat each test (default: %d)\n", cmd_params_defaults.reps);
432    printf("  --prio <-1|0|1|2|3>                         process/thread priority (default: %d)\n", cmd_params_defaults.prio);
433    printf("  --delay <0...N> (seconds)                   delay between each test (default: %d)\n", cmd_params_defaults.delay);
434    printf("  -o, --output <csv|json|jsonl|md|sql>        output format printed to stdout (default: %s)\n", output_format_str(cmd_params_defaults.output_format));
435    printf("  -oe, --output-err <csv|json|jsonl|md|sql>   output format printed to stderr (default: %s)\n", output_format_str(cmd_params_defaults.output_format_stderr));
436    printf("  --list-devices                              list available devices and exit\n");
437    printf("  -v, --verbose                               verbose output\n");
438    printf("  --progress                                  print test progress indicators\n");
439    printf("  --no-warmup                                 skip warmup runs before benchmarking\n");
440    printf("  -fitt, --fit-target <MiB>                   fit model to device memory with this margin per device in MiB (default: off)\n");
441    printf("  -fitc, --fit-ctx <n>                        minimum ctx size for --fit-target (default: 4096)\n");
442    if (llama_supports_rpc()) {
443        printf("  -rpc, --rpc <rpc_servers>                   register RPC devices (comma separated)\n");
444    }
445    printf("\n");
446    printf("test parameters:\n");
447    printf("  -m, --model <filename>                            (default: %s)\n", join(cmd_params_defaults.model, ",").c_str());
448    printf("  -hf, -hfr, --hf-repo <user>/<model>[:quant]       Hugging Face model repository; quant is optional, case-insensitive\n");
449    printf("                                                    default to Q4_K_M, or falls back to the first file in the repo if Q4_K_M doesn't exist.\n");
450    printf("                                                    example: ggml-org/GLM-4.7-Flash-GGUF:Q4_K_M\n");
451    printf("                                                    (default: unused)\n");
452    printf("  -hff, --hf-file <file>                            Hugging Face model file. If specified, it will override the quant in --hf-repo\n");
453    printf("                                                    (default: unused)\n");
454    printf("  -hft, --hf-token <token>                          Hugging Face access token\n");
455    printf("                                                    (default: value from HF_TOKEN environment variable)\n");
456    printf("  --offline                                         Offline mode: forces use of cache, prevents network access\n");
457    printf("                                                    (default: disabled)\n");
458    printf("  -p, --n-prompt <n>                                (default: %s)\n", join(cmd_params_defaults.n_prompt, ",").c_str());
459    printf("  -n, --n-gen <n>                                   (default: %s)\n", join(cmd_params_defaults.n_gen, ",").c_str());
460    printf("  -pg <pp,tg>                                       (default: %s)\n", join(transform_to_str(cmd_params_defaults.n_pg, pair_str), ",").c_str());
461    printf("  -d, --n-depth <n>                                 (default: %s)\n", join(cmd_params_defaults.n_depth, ",").c_str());
462    printf("  -b, --batch-size <n>                              (default: %s)\n", join(cmd_params_defaults.n_batch, ",").c_str());
463    printf("  -ub, --ubatch-size <n>                            (default: %s)\n", join(cmd_params_defaults.n_ubatch, ",").c_str());
464    printf("  -ctk, --cache-type-k <t>                          (default: %s)\n", join(transform_to_str(cmd_params_defaults.type_k, ggml_type_name), ",").c_str());
465    printf("  -ctv, --cache-type-v <t>                          (default: %s)\n", join(transform_to_str(cmd_params_defaults.type_v, ggml_type_name), ",").c_str());
466    printf("  -t, --threads <n>                                 (default: %s)\n", join(cmd_params_defaults.n_threads, ",").c_str());
467    printf("  -C, --cpu-mask <hex,hex>                          (default: %s)\n", join(cmd_params_defaults.cpu_mask, ",").c_str());
468    printf("  --cpu-strict <0|1>                                (default: %s)\n", join(cmd_params_defaults.cpu_strict, ",").c_str());
469    printf("  --poll <0...100>                                  (default: %s)\n", join(cmd_params_defaults.poll, ",").c_str());
470    printf("  -ngl, --n-gpu-layers <n>                          (default: %s)\n", join(cmd_params_defaults.n_gpu_layers, ",").c_str());
471    printf("  -ncmoe, --n-cpu-moe <n>                           (default: %s)\n", join(cmd_params_defaults.n_cpu_moe, ",").c_str());
472    printf("  -sm, --split-mode <none|layer|row|tensor>         (default: %s)\n", join(transform_to_str(cmd_params_defaults.split_mode, split_mode_str), ",").c_str());
473    printf("  -mg, --main-gpu <i>                               (default: %s)\n", join(cmd_params_defaults.main_gpu, ",").c_str());
474    printf("  -nkvo, --no-kv-offload <0|1>                      (default: %s)\n", join(cmd_params_defaults.no_kv_offload, ",").c_str());
475    printf("  -fa, --flash-attn <on|off|auto>                   (default: %s)\n", join(transform_to_str(cmd_params_defaults.flash_attn, llama_flash_attn_type_name), ",").c_str());
476    printf("  -dev, --device <dev0/dev1/...>                    (default: auto)\n");
477    printf("  -lm, --load-mode <auto|none|mmap|mlock|mmap+mlock|dio> (default: %s)\n", join(transform_to_str(cmd_params_defaults.load_mode, llama_load_mode_name), ",").c_str());
478    printf("  -lzm, --lazy-mode <on|auto|off>                   (default: %s)\n", join(transform_to_str(cmd_params_defaults.lazy_mode, lazy_mode_str), ",").c_str());
479    printf("  -embd, --embeddings <0|1>                         (default: %s)\n", join(cmd_params_defaults.embeddings, ",").c_str());
480    printf("  -ts, --tensor-split <ts0/ts1/..>                  (default: 0)\n");
481    printf("  -ot --override-tensor <tensor name pattern>=<buffer type>;...\n");
482    printf("                                                    (default: disabled)\n");
483    printf("  -nopo, --no-op-offload <0|1>                      (default: 0)\n");
484    printf("  --no-host <0|1>                                   (default: %s)\n", join(cmd_params_defaults.no_host, ",").c_str());
485    printf("\n");
486    printf(
487        "Multiple values can be given for each parameter by separating them with ','\n"
488        "or by specifying the parameter multiple times. Ranges can be given as\n"
489        "'first-last' or 'first-last+step' or 'first-last*mult'.\n");
490}
491
492static ggml_type ggml_type_from_name(const std::string & s) {
493    if (s == "f16") {
494        return GGML_TYPE_F16;
495    }
496    if (s == "bf16") {
497        return GGML_TYPE_BF16;
498    }
499    if (s == "q8_0") {
500        return GGML_TYPE_Q8_0;
501    }
502    if (s == "q4_0") {
503        return GGML_TYPE_Q4_0;
504    }
505    if (s == "q4_1") {
506        return GGML_TYPE_Q4_1;
507    }
508    if (s == "q5_0") {
509        return GGML_TYPE_Q5_0;
510    }
511    if (s == "q5_1") {
512        return GGML_TYPE_Q5_1;
513    }
514    if (s == "iq4_nl") {
515        return GGML_TYPE_IQ4_NL;
516    }
517
518    return GGML_TYPE_COUNT;
519}
520
521static cmd_params parse_cmd_params(int argc, char ** argv) {
522    cmd_params        params;
523    std::string       arg;
524    bool              invalid_param = false;
525    const std::string arg_prefix    = "--";
526    const char        split_delim   = ',';
527
528    params.verbose              = cmd_params_defaults.verbose;
529    params.output_format        = cmd_params_defaults.output_format;
530    params.output_format_stderr = cmd_params_defaults.output_format_stderr;
531    params.reps                 = cmd_params_defaults.reps;
532    params.numa                 = cmd_params_defaults.numa;
533    params.prio                 = cmd_params_defaults.prio;
534    params.delay                = cmd_params_defaults.delay;
535    params.progress             = cmd_params_defaults.progress;
536    params.no_warmup            = cmd_params_defaults.no_warmup;
537    params.offline              = cmd_params_defaults.offline;
538
539    if (const char * env = getenv("HF_TOKEN")) {
540        params.hf_token = env;
541    }
542
543    for (int i = 1; i < argc; i++) {
544        arg = argv[i];
545        if (arg.compare(0, arg_prefix.size(), arg_prefix) == 0) {
546            std::replace(arg.begin(), arg.end(), '_', '-');
547        }
548
549        try {
550            if (arg == "-h" || arg == "--help") {
551                print_usage(argc, argv);
552                exit(0);
553            } else if (arg == "-m" || arg == "--model") {
554                if (++i >= argc) {
555                    invalid_param = true;
556                    break;
557                }
558                auto p = string_split<std::string>(argv[i], split_delim);
559                params.model.insert(params.model.end(), p.begin(), p.end());
560            } else if (arg == "-hf" || arg == "-hfr" || arg == "--hf-repo") {
561                if (++i >= argc) {
562                    invalid_param = true;
563                    break;
564                }
565                auto p = string_split<std::string>(argv[i], split_delim);
566                params.hf_repo.insert(params.hf_repo.end(), p.begin(), p.end());
567            } else if (arg == "-hff" || arg == "--hf-file") {
568                if (++i >= argc) {
569                    invalid_param = true;
570                    break;
571                }
572                auto p = string_split<std::string>(argv[i], split_delim);
573                params.hf_file.insert(params.hf_file.end(), p.begin(), p.end());
574            } else if (arg == "-hft" || arg == "--hf-token") {
575                if (++i >= argc) {
576                    invalid_param = true;
577                    break;
578                }
579                params.hf_token = argv[i];
580            } else if (arg == "--offline") {
581                params.offline = true;
582            } else if (arg == "-p" || arg == "--n-prompt") {
583                if (++i >= argc) {
584                    invalid_param = true;
585                    break;
586                }
587                auto p = parse_int_range(argv[i]);
588                params.n_prompt.insert(params.n_prompt.end(), p.begin(), p.end());
589            } else if (arg == "-n" || arg == "--n-gen") {
590                if (++i >= argc) {
591                    invalid_param = true;
592                    break;
593                }
594                auto p = parse_int_range(argv[i]);
595                params.n_gen.insert(params.n_gen.end(), p.begin(), p.end());
596            } else if (arg == "-pg") {
597                if (++i >= argc) {
598                    invalid_param = true;
599                    break;
600                }
601                auto p = string_split<std::string>(argv[i], ',');
602                if (p.size() != 2) {
603                    invalid_param = true;
604                    break;
605                }
606                params.n_pg.push_back({ std::stoi(p[0]), std::stoi(p[1]) });
607            } else if (arg == "-d" || arg == "--n-depth") {
608                if (++i >= argc) {
609                    invalid_param = true;
610                    break;
611                }
612                auto p = parse_int_range(argv[i]);
613                params.n_depth.insert(params.n_depth.end(), p.begin(), p.end());
614            } else if (arg == "-b" || arg == "--batch-size") {
615                if (++i >= argc) {
616                    invalid_param = true;
617                    break;
618                }
619                auto p = parse_int_range(argv[i]);
620                params.n_batch.insert(params.n_batch.end(), p.begin(), p.end());
621            } else if (arg == "-ub" || arg == "--ubatch-size") {
622                if (++i >= argc) {
623                    invalid_param = true;
624                    break;
625                }
626                auto p = parse_int_range(argv[i]);
627                params.n_ubatch.insert(params.n_ubatch.end(), p.begin(), p.end());
628            } else if (arg == "-ctk" || arg == "--cache-type-k") {
629                if (++i >= argc) {
630                    invalid_param = true;
631                    break;
632                }
633                auto p = string_split<std::string>(argv[i], split_delim);
634
635                std::vector<ggml_type> types;
636                for (const auto & t : p) {
637                    ggml_type gt = ggml_type_from_name(t);
638                    if (gt == GGML_TYPE_COUNT) {
639                        invalid_param = true;
640                        break;
641                    }
642                    types.push_back(gt);
643                }
644                if (invalid_param) {
645                    break;
646                }
647                params.type_k.insert(params.type_k.end(), types.begin(), types.end());
648            } else if (arg == "-ctv" || arg == "--cache-type-v") {
649                if (++i >= argc) {
650                    invalid_param = true;
651                    break;
652                }
653                auto p = string_split<std::string>(argv[i], split_delim);
654
655                std::vector<ggml_type> types;
656                for (const auto & t : p) {
657                    ggml_type gt = ggml_type_from_name(t);
658                    if (gt == GGML_TYPE_COUNT) {
659                        invalid_param = true;
660                        break;
661                    }
662                    types.push_back(gt);
663                }
664                if (invalid_param) {
665                    break;
666                }
667                params.type_v.insert(params.type_v.end(), types.begin(), types.end());
668            } else if (arg == "-dev" || arg == "--device") {
669                if (++i >= argc) {
670                    invalid_param = true;
671                    break;
672                }
673                auto combos = string_split<std::string>(argv[i], split_delim);
674                for (const auto & combo : combos) {
675                    try {
676                        params.devices.push_back(parse_devices_arg(combo));
677                    } catch (const std::exception & e) {
678                        fprintf(stderr, "error: %s\n", e.what());
679                        invalid_param = true;
680                        break;
681                    }
682                }
683                if (invalid_param) {
684                    break;
685                }
686            } else if (arg == "--list-devices") {
687                common_print_available_devices();
688                exit(0);
689            } else if (arg == "-t" || arg == "--threads") {
690                if (++i >= argc) {
691                    invalid_param = true;
692                    break;
693                }
694                auto p = parse_int_range(argv[i]);
695                params.n_threads.insert(params.n_threads.end(), p.begin(), p.end());
696            } else if (arg == "-C" || arg == "--cpu-mask") {
697                if (++i >= argc) {
698                    invalid_param = true;
699                    break;
700                }
701                auto p = string_split<std::string>(argv[i], split_delim);
702                params.cpu_mask.insert(params.cpu_mask.end(), p.begin(), p.end());
703            } else if (arg == "--cpu-strict") {
704                if (++i >= argc) {
705                    invalid_param = true;
706                    break;
707                }
708                auto p = string_split<bool>(argv[i], split_delim);
709                params.cpu_strict.insert(params.cpu_strict.end(), p.begin(), p.end());
710            } else if (arg == "--poll") {
711                if (++i >= argc) {
712                    invalid_param = true;
713                    break;
714                }
715                auto p = parse_int_range(argv[i]);
716                params.poll.insert(params.poll.end(), p.begin(), p.end());
717            } else if (arg == "-ngl" || arg == "--n-gpu-layers") {
718                if (++i >= argc) {
719                    invalid_param = true;
720                    break;
721                }
722                auto p = parse_int_range(argv[i], /*allow_negative=*/true);
723                params.n_gpu_layers.insert(params.n_gpu_layers.end(), p.begin(), p.end());
724            } else if (arg == "-ncmoe" || arg == "--n-cpu-moe") {
725                if (++i >= argc) {
726                    invalid_param = true;
727                    break;
728                }
729                auto p = parse_int_range(argv[i]);
730                params.n_cpu_moe.insert(params.n_cpu_moe.end(), p.begin(), p.end());
731            } else if (llama_supports_rpc() && (arg == "-rpc" || arg == "--rpc")) {
732                if (++i >= argc) {
733                    invalid_param = true;
734                    break;
735                }
736                try {
737                    register_rpc_server_list(argv[i]);
738                } catch (const std::exception & e) {
739                    fprintf(stderr, "error: %s\n", e.what());
740                    invalid_param = true;
741                    break;
742                }
743            } else if (arg == "-sm" || arg == "--split-mode") {
744                if (++i >= argc) {
745                    invalid_param = true;
746                    break;
747                }
748                auto p = string_split<std::string>(argv[i], split_delim);
749
750                std::vector<llama_split_mode> modes;
751                for (const auto & m : p) {
752                    llama_split_mode mode;
753                    if (m == "none") {
754                        mode = LLAMA_SPLIT_MODE_NONE;
755                    } else if (m == "layer") {
756                        mode = LLAMA_SPLIT_MODE_LAYER;
757                    } else if (m == "row") {
758                        mode = LLAMA_SPLIT_MODE_ROW;
759                    } else if (m == "tensor") {
760                        mode = LLAMA_SPLIT_MODE_TENSOR;
761                    } else {
762                        invalid_param = true;
763                        break;
764                    }
765                    modes.push_back(mode);
766                }
767                if (invalid_param) {
768                    break;
769                }
770                params.split_mode.insert(params.split_mode.end(), modes.begin(), modes.end());
771            } else if (arg == "-lm" || arg == "--load-mode") {
772                if (++i >= argc) {
773                    invalid_param = true;
774                    break;
775                }
776                auto p = string_split<std::string>(argv[i], split_delim);
777
778                std::vector<llama_load_mode> modes;
779                for (const auto & m : p) {
780                    llama_load_mode mode;
781                    if (m == "auto") {
782                        mode = LLAMA_LOAD_MODE_AUTO;
783                    } else if (m == "none") {
784                        mode = LLAMA_LOAD_MODE_NONE;
785                    } else if (m == "mmap") {
786                        mode = LLAMA_LOAD_MODE_MMAP;
787                    } else if (m == "mlock") {
788                        mode = LLAMA_LOAD_MODE_MLOCK;
789                    } else if (m == "mmap+mlock") {
790                        mode = LLAMA_LOAD_MODE_MMAP_MLOCK;
791                    } else if (m == "dio") {
792                        mode = LLAMA_LOAD_MODE_DIRECT_IO;
793                    } else {
794                        invalid_param = true;
795                        break;
796                    }
797                    modes.push_back(mode);
798                }
799                if (invalid_param) {
800                    break;
801                }
802                params.load_mode.insert(params.load_mode.end(), modes.begin(), modes.end());
803            } else if (arg == "-lzm" || arg == "--lazy-mode") {
804                if (++i >= argc) {
805                    invalid_param = true;
806                    break;
807                }
808                auto p = string_split<std::string>(argv[i], split_delim);
809
810                std::vector<llama_lazy_mode> modes;
811                for (const auto & m : p) {
812                    llama_lazy_mode mode;
813                    if (m == "on") {
814                        mode = LLAMA_LAZY_MODE_ON;
815                    } else if (m == "auto") {
816                        mode = LLAMA_LAZY_MODE_AUTO;
817                    } else if (m == "off") {
818                        mode = LLAMA_LAZY_MODE_OFF;
819                    } else {
820                        invalid_param = true;
821                        break;
822                    }
823                    modes.push_back(mode);
824                }
825                if (invalid_param) {
826                    break;
827                }
828                params.lazy_mode.insert(params.lazy_mode.end(), modes.begin(), modes.end());
829            } else if (arg == "-mg" || arg == "--main-gpu") {
830                if (++i >= argc) {
831                    invalid_param = true;
832                    break;
833                }
834                params.main_gpu = parse_int_range(argv[i]);
835            } else if (arg == "-nkvo" || arg == "--no-kv-offload") {
836                if (++i >= argc) {
837                    invalid_param = true;
838                    break;
839                }
840                auto p = string_split<bool>(argv[i], split_delim);
841                params.no_kv_offload.insert(params.no_kv_offload.end(), p.begin(), p.end());
842            } else if (arg == "--numa") {
843                if (++i >= argc) {
844                    invalid_param = true;
845                    break;
846                }
847                std::string value(argv[i]);
848                if (value == "distribute" || value == "") {
849                    params.numa = GGML_NUMA_STRATEGY_DISTRIBUTE;
850                } else if (value == "isolate") {
851                    params.numa = GGML_NUMA_STRATEGY_ISOLATE;
852                } else if (value == "numactl") {
853                    params.numa = GGML_NUMA_STRATEGY_NUMACTL;
854                } else {
855                    invalid_param = true;
856                    break;
857                }
858            } else if (arg == "-fa" || arg == "--flash-attn") {
859                if (++i >= argc) {
860                    invalid_param = true;
861                    break;
862                }
863                auto p = string_split<std::string>(argv[i], split_delim);
864
865                std::vector<llama_flash_attn_type> types;
866                for (const auto & v : p) {
867                    llama_flash_attn_type type;
868                    if (common_arg_utils::is_truthy(v)) {
869                        type = LLAMA_FLASH_ATTN_TYPE_ENABLED;
870                    } else if (common_arg_utils::is_falsey(v)) {
871                        type = LLAMA_FLASH_ATTN_TYPE_DISABLED;
872                    } else if (common_arg_utils::is_autoy(v)) {
873                        type = LLAMA_FLASH_ATTN_TYPE_AUTO;
874                    } else {
875                        invalid_param = true;
876                        break;
877                    }
878                    types.push_back(type);
879                }
880                if (invalid_param) {
881                    break;
882                }
883                params.flash_attn.insert(params.flash_attn.end(), types.begin(), types.end());
884            } else if (arg == "-embd" || arg == "--embeddings") {
885                if (++i >= argc) {
886                    invalid_param = true;
887                    break;
888                }
889                auto p = string_split<bool>(argv[i], split_delim);
890                params.embeddings.insert(params.embeddings.end(), p.begin(), p.end());
891            } else if (arg == "-nopo" || arg == "--no-op-offload") {
892                if (++i >= argc) {
893                    invalid_param = true;
894                    break;
895                }
896                auto p = string_split<bool>(argv[i], split_delim);
897                params.no_op_offload.insert(params.no_op_offload.end(), p.begin(), p.end());
898            } else if (arg == "--no-host") {
899                if (++i >= argc) {
900                    invalid_param = true;
901                    break;
902                }
903                auto p = string_split<bool>(argv[i], split_delim);
904                params.no_host.insert(params.no_host.end(), p.begin(), p.end());
905            } else if (arg == "-ts" || arg == "--tensor-split") {
906                if (++i >= argc) {
907                    invalid_param = true;
908                    break;
909                }
910                for (auto ts : string_split<std::string>(argv[i], split_delim)) {
911                    // split string by ; and /
912                    const std::regex           regex{ R"([;/]+)" };
913                    std::sregex_token_iterator it{ ts.begin(), ts.end(), regex, -1 };
914                    std::vector<std::string>   split_arg{ it, {} };
915                    GGML_ASSERT(split_arg.size() <= llama_max_devices());
916
917                    std::vector<float> tensor_split(llama_max_devices());
918                    for (size_t i = 0; i < llama_max_devices(); ++i) {
919                        if (i < split_arg.size()) {
920                            tensor_split[i] = std::stof(split_arg[i]);
921                        } else {
922                            tensor_split[i] = 0.0f;
923                        }
924                    }
925                    params.tensor_split.push_back(tensor_split);
926                }
927            } else if (arg == "-ot" || arg == "--override-tensor") {
928                if (++i >= argc) {
929                    invalid_param = true;
930                    break;
931                }
932                auto * value = argv[i];
933                /* static */ std::map<std::string, ggml_backend_buffer_type_t> buft_list;
934                if (buft_list.empty()) {
935                    // enumerate all the devices and add their buffer types to the list
936                    for (size_t i = 0; i < ggml_backend_dev_count(); ++i) {
937                        auto * dev = ggml_backend_dev_get(i);
938                        auto * buft = ggml_backend_dev_buffer_type(dev);
939                        if (buft) {
940                            buft_list[ggml_backend_buft_name(buft)] = buft;
941                        }
942                    }
943                }
944                auto override_group_span_len = std::strcspn(value, ",");
945                bool last_group = false;
946                do {
947                    if (override_group_span_len == 0) {
948                        // Adds an empty override-tensors for an empty span
949                        params.tensor_buft_overrides.push_back({{}});
950                        if (value[override_group_span_len] == '\0') {
951                            value = &value[override_group_span_len];
952                            last_group = true;
953                        } else {
954                            value = &value[override_group_span_len + 1];
955                            override_group_span_len = std::strcspn(value, ",");
956                        }
957                        continue;
958                    }
959                    // Stamps null terminators into the argv
960                    // value for this option to avoid the
961                    // memory leak present in the implementation
962                    // over in arg.cpp. Acceptable because we
963                    // only parse these args once in this program.
964                    auto * override_group = value;
965                    if (value[override_group_span_len] == '\0') {
966                        value = &value[override_group_span_len];
967                        last_group = true;
968                    } else {
969                        value[override_group_span_len] = '\0';
970                        value = &value[override_group_span_len + 1];
971                    }
972                    std::vector<llama_model_tensor_buft_override> group_tensor_buft_overrides{};
973                    auto override_span_len = std::strcspn(override_group, ";");
974                    while (override_span_len > 0) {
975                        auto * override = override_group;
976                        if (override_group[override_span_len] != '\0') {
977                            override_group[override_span_len] = '\0';
978                            override_group = &override_group[override_span_len + 1];
979                        } else {
980                            override_group = &override_group[override_span_len];
981                        }
982                        auto tensor_name_span_len = std::strcspn(override, "=");
983                        if (tensor_name_span_len >= override_span_len) {
984                            invalid_param = true;
985                            break;
986                        }
987                        override[tensor_name_span_len] = '\0';
988                        auto * tensor_name = override;
989                        auto * buffer_type = &override[tensor_name_span_len + 1];
990                        if (buft_list.find(buffer_type) == buft_list.end()) {
991                            printf("error: unrecognized buffer type '%s'\n", buffer_type);
992                            printf("Available buffer types:\n");
993                            for (const auto & it : buft_list) {
994                                printf("  %s\n", ggml_backend_buft_name(it.second));
995                            }
996                            invalid_param = true;
997                            break;
998                        }
999                        group_tensor_buft_overrides.push_back({tensor_name, buft_list.at(buffer_type)});
1000                        override_span_len = std::strcspn(override_group, ";");
1001                    }
1002                    if (invalid_param) {
1003                        break;
1004                    }
1005                    group_tensor_buft_overrides.push_back({nullptr,nullptr});
1006                    params.tensor_buft_overrides.push_back(group_tensor_buft_overrides);
1007                    override_group_span_len = std::strcspn(value, ",");
1008                } while (!last_group);
1009            } else if (arg == "-r" || arg == "--repetitions") {
1010                if (++i >= argc) {
1011                    invalid_param = true;
1012                    break;
1013                }
1014                params.reps = std::stoi(argv[i]);
1015            } else if (arg == "--prio") {
1016                if (++i >= argc) {
1017                    invalid_param = true;
1018                    break;
1019                }
1020                params.prio = (enum ggml_sched_priority) std::stoi(argv[i]);
1021            } else if (arg == "--delay") {
1022                if (++i >= argc) {
1023                    invalid_param = true;
1024                    break;
1025                }
1026                params.delay = std::stoi(argv[i]);
1027            } else if (arg == "-o" || arg == "--output") {
1028                if (++i >= argc) {
1029                    invalid_param = true;
1030                    break;
1031                }
1032                invalid_param = !output_format_from_str(argv[i], params.output_format);
1033            } else if (arg == "-oe" || arg == "--output-err") {
1034                if (++i >= argc) {
1035                    invalid_param = true;
1036                    break;
1037                }
1038                invalid_param = !output_format_from_str(argv[i], params.output_format_stderr);
1039            } else if (arg == "-v" || arg == "--verbose") {
1040                params.verbose = true;
1041            } else if (arg == "--progress") {
1042                params.progress = true;
1043            } else if (arg == "--no-warmup") {
1044                params.no_warmup = true;
1045            } else if (arg == "-fitt" || arg == "--fit-target") {
1046                if (++i >= argc) {
1047                    invalid_param = true;
1048                    break;
1049                }
1050                auto p = string_split<std::string>(argv[i], split_delim);
1051                for (const auto & v : p) {
1052                    params.fit_params_target.push_back(std::stoull(v));
1053                }
1054            } else if (arg == "-fitc" || arg == "--fit-ctx") {
1055                if (++i >= argc) {
1056                    invalid_param = true;
1057                    break;
1058                }
1059                auto p = string_split<std::string>(argv[i], split_delim);
1060                for (const auto & v : p) {
1061                    params.fit_params_min_ctx.push_back(std::stoul(v));
1062                }
1063            } else {
1064                invalid_param = true;
1065                break;
1066            }
1067        } catch (const std::exception & e) {
1068            fprintf(stderr, "error: %s\n", e.what());
1069            invalid_param = true;
1070            break;
1071        }
1072    }
1073
1074    if (invalid_param) {
1075        fprintf(stderr, "error: invalid parameter for argument: %s\n", arg.c_str());
1076        print_usage(argc, argv);
1077        exit(1);
1078    }
1079
1080    if (!params.hf_repo.empty()) {
1081        for (size_t i = 0; i < params.hf_repo.size(); i++) {
1082            common_params p;
1083            p.hf_token      = params.hf_token;
1084            p.offline       = params.offline;
1085            p.model.hf_repo = params.hf_repo[i];
1086            if (!params.hf_file.empty() && !params.hf_file[i].empty()) {
1087                p.model.hf_file = params.hf_file[i];
1088            }
1089
1090            // only the text model file is needed
1091            common_models_handler models_handler = common_models_handler_init(p, LLAMA_EXAMPLE_BENCH);
1092            common_models_handler_apply(models_handler, p);
1093            if (p.model.path.empty()) {
1094                fprintf(stderr, "error: failed to download model from HuggingFace\n");
1095                exit(1);
1096            }
1097
1098            params.model.push_back(p.model.path);
1099        }
1100    }
1101
1102    // set defaults
1103    if (params.model.empty()) {
1104        params.model = cmd_params_defaults.model;
1105    }
1106    if (params.n_prompt.empty()) {
1107        params.n_prompt = cmd_params_defaults.n_prompt;
1108    }
1109    if (params.n_gen.empty()) {
1110        params.n_gen = cmd_params_defaults.n_gen;
1111    }
1112    if (params.n_pg.empty()) {
1113        params.n_pg = cmd_params_defaults.n_pg;
1114    }
1115    if (params.n_depth.empty()) {
1116        params.n_depth = cmd_params_defaults.n_depth;
1117    }
1118    if (params.n_batch.empty()) {
1119        params.n_batch = cmd_params_defaults.n_batch;
1120    }
1121    if (params.n_ubatch.empty()) {
1122        params.n_ubatch = cmd_params_defaults.n_ubatch;
1123    }
1124    if (params.type_k.empty()) {
1125        params.type_k = cmd_params_defaults.type_k;
1126    }
1127    if (params.type_v.empty()) {
1128        params.type_v = cmd_params_defaults.type_v;
1129    }
1130    if (params.n_gpu_layers.empty()) {
1131        params.n_gpu_layers = cmd_params_defaults.n_gpu_layers;
1132    }
1133    if (params.n_cpu_moe.empty()) {
1134        params.n_cpu_moe = cmd_params_defaults.n_cpu_moe;
1135    }
1136    if (params.split_mode.empty()) {
1137        params.split_mode = cmd_params_defaults.split_mode;
1138    }
1139    if (params.load_mode.empty()) {
1140        params.load_mode = cmd_params_defaults.load_mode;
1141    }
1142    if (params.lazy_mode.empty()) {
1143        params.lazy_mode = cmd_params_defaults.lazy_mode;
1144    }
1145    if (params.main_gpu.empty()) {
1146        params.main_gpu = cmd_params_defaults.main_gpu;
1147    }
1148    if (params.no_kv_offload.empty()) {
1149        params.no_kv_offload = cmd_params_defaults.no_kv_offload;
1150    }
1151    if (params.flash_attn.empty()) {
1152        params.flash_attn = cmd_params_defaults.flash_attn;
1153    }
1154    if (params.devices.empty()) {
1155        params.devices = cmd_params_defaults.devices;
1156    }
1157    if (params.tensor_split.empty()) {
1158        params.tensor_split = cmd_params_defaults.tensor_split;
1159    }
1160    if (params.tensor_buft_overrides.empty()) {
1161        params.tensor_buft_overrides = cmd_params_defaults.tensor_buft_overrides;
1162    }
1163    if (params.embeddings.empty()) {
1164        params.embeddings = cmd_params_defaults.embeddings;
1165    }
1166    if (params.no_op_offload.empty()) {
1167        params.no_op_offload = cmd_params_defaults.no_op_offload;
1168    }
1169    if (params.no_host.empty()) {
1170        params.no_host = cmd_params_defaults.no_host;
1171    }
1172    if (params.n_threads.empty()) {
1173        params.n_threads = cmd_params_defaults.n_threads;
1174    }
1175    if (params.cpu_mask.empty()) {
1176        params.cpu_mask = cmd_params_defaults.cpu_mask;
1177    }
1178    if (params.cpu_strict.empty()) {
1179        params.cpu_strict = cmd_params_defaults.cpu_strict;
1180    }
1181    if (params.poll.empty()) {
1182        params.poll = cmd_params_defaults.poll;
1183    }
1184    if (params.fit_params_target.empty()) {
1185        params.fit_params_target = cmd_params_defaults.fit_params_target;
1186    }
1187    if (params.fit_params_min_ctx.empty()) {
1188        params.fit_params_min_ctx = cmd_params_defaults.fit_params_min_ctx;
1189    }
1190
1191    return params;
1192}
1193
1194struct cmd_params_instance {
1195    std::string        model;
1196    int                n_prompt;
1197    int                n_gen;
1198    int                n_depth;
1199    int                n_batch;
1200    int                n_ubatch;
1201    ggml_type          type_k;
1202    ggml_type          type_v;
1203    int                n_threads;
1204    std::string        cpu_mask;
1205    bool               cpu_strict;
1206    int                poll;
1207    int                n_gpu_layers;
1208    int                n_cpu_moe;
1209    llama_split_mode   split_mode;
1210    llama_load_mode    load_mode;
1211    llama_lazy_mode    lazy_mode;
1212    int                main_gpu;
1213    bool               no_kv_offload;
1214    llama_flash_attn_type flash_attn;
1215    std::vector<ggml_backend_dev_t> devices;
1216    std::vector<float> tensor_split;
1217    std::vector<llama_model_tensor_buft_override> tensor_buft_overrides;
1218    bool               embeddings;
1219    bool               no_op_offload;
1220    bool               no_host;
1221    size_t             fit_target;
1222    uint32_t           fit_min_ctx;
1223
1224    llama_model_params to_llama_mparams() const {
1225        llama_model_params mparams = llama_model_default_params();
1226
1227        mparams.n_gpu_layers = n_gpu_layers;
1228        if (!devices.empty()) {
1229            mparams.devices = const_cast<ggml_backend_dev_t *>(devices.data());
1230        }
1231        mparams.split_mode    = split_mode;
1232        mparams.load_mode     = load_mode;
1233        mparams.lazy_mode     = lazy_mode;
1234        mparams.main_gpu      = main_gpu;
1235        mparams.tensor_split  = tensor_split.data();
1236        mparams.no_host       = no_host;
1237
1238        if (n_cpu_moe <= 0) {
1239            if (tensor_buft_overrides.empty()) {
1240                mparams.tensor_buft_overrides = nullptr;
1241            } else {
1242                GGML_ASSERT(tensor_buft_overrides.back().pattern == nullptr &&
1243                            "Tensor buffer overrides not terminated with empty pattern");
1244                mparams.tensor_buft_overrides = tensor_buft_overrides.data();
1245            }
1246        } else {
1247            static std::vector<llama_model_tensor_buft_override> merged;
1248            static std::vector<std::string> patterns;
1249
1250            merged.clear();
1251            patterns.clear();
1252
1253            auto first = tensor_buft_overrides.begin();
1254            auto last  = tensor_buft_overrides.end();
1255            if (first != last && (last - 1)->pattern == nullptr) {
1256                --last;
1257            }
1258            merged.insert(merged.end(), first, last);
1259
1260            patterns.reserve((size_t) n_cpu_moe);
1261            merged.reserve(merged.size() + (size_t) n_cpu_moe + 1);
1262
1263            for (int i = 0; i < n_cpu_moe; ++i) {
1264                patterns.push_back(llm_ffn_block_regex(i, LLM_FFN_EXPS_REGEX));
1265                merged.push_back({ patterns.back().c_str(),
1266                                ggml_backend_cpu_buffer_type() });
1267            }
1268
1269            merged.push_back({ nullptr, nullptr });
1270
1271            mparams.tensor_buft_overrides = merged.data();
1272        }
1273
1274        return mparams;
1275    }
1276
1277    bool equal_mparams(const cmd_params_instance & other) const {
1278        return model == other.model && n_gpu_layers == other.n_gpu_layers && n_cpu_moe == other.n_cpu_moe &&
1279               split_mode == other.split_mode &&
1280               main_gpu == other.main_gpu && tensor_split == other.tensor_split &&
1281               load_mode == other.load_mode && lazy_mode == other.lazy_mode &&
1282               devices == other.devices && no_host == other.no_host &&
1283               vec_tensor_buft_override_equal(tensor_buft_overrides, other.tensor_buft_overrides);
1284    }
1285
1286    llama_context_params to_llama_cparams() const {
1287        llama_context_params cparams = llama_context_default_params();
1288
1289        cparams.n_ctx           = n_prompt + n_gen + n_depth;
1290        cparams.n_batch         = n_batch;
1291        cparams.n_ubatch        = n_ubatch;
1292        cparams.type_k          = type_k;
1293        cparams.type_v          = type_v;
1294        cparams.offload_kqv     = !no_kv_offload;
1295        cparams.flash_attn_type = flash_attn;
1296        cparams.embeddings      = embeddings;
1297        cparams.op_offload      = !no_op_offload;
1298        cparams.swa_full        = false;
1299
1300        return cparams;
1301    }
1302};
1303
1304static std::vector<cmd_params_instance> get_cmd_params_instances(const cmd_params & params) {
1305    std::vector<cmd_params_instance> instances;
1306
1307    // this ordering minimizes the number of times that each model needs to be reloaded
1308    // clang-format off
1309    for (const auto & m : params.model)
1310    for (const auto & fpt : params.fit_params_target)
1311    for (const auto & fpc : params.fit_params_min_ctx)
1312    for (const auto & nl : params.n_gpu_layers)
1313    for (const auto & ncmoe : params.n_cpu_moe)
1314    for (const auto & sm : params.split_mode)
1315    for (const auto & lm : params.load_mode)
1316    for (const auto & lzm : params.lazy_mode)
1317    for (const auto & mg : params.main_gpu)
1318    for (const auto & devs : params.devices)
1319    for (const auto & ts : params.tensor_split)
1320    for (const auto & ot : params.tensor_buft_overrides)
1321    for (const auto & noh : params.no_host)
1322    for (const auto & embd : params.embeddings)
1323    for (const auto & nopo : params.no_op_offload)
1324    for (const auto & nb : params.n_batch)
1325    for (const auto & nub : params.n_ubatch)
1326    for (const auto & tk : params.type_k)
1327    for (const auto & tv : params.type_v)
1328    for (const auto & nkvo : params.no_kv_offload)
1329    for (const auto & fa : params.flash_attn)
1330    for (const auto & nt : params.n_threads)
1331    for (const auto & cm : params.cpu_mask)
1332    for (const auto & cs : params.cpu_strict)
1333    for (const auto & nd : params.n_depth)
1334    for (const auto & pl : params.poll) {
1335        for (const auto & n_prompt : params.n_prompt) {
1336            if (n_prompt == 0) {
1337                continue;
1338            }
1339            cmd_params_instance instance = {
1340                /* .model                 = */ m,
1341                /* .n_prompt              = */ n_prompt,
1342                /* .n_gen                 = */ 0,
1343                /* .n_depth               = */ nd,
1344                /* .n_batch               = */ nb,
1345                /* .n_ubatch              = */ nub,
1346                /* .type_k                = */ tk,
1347                /* .type_v                = */ tv,
1348                /* .n_threads             = */ nt,
1349                /* .cpu_mask              = */ cm,
1350                /* .cpu_strict            = */ cs,
1351                /* .poll                  = */ pl,
1352                /* .n_gpu_layers          = */ nl,
1353                /* .n_cpu_moe             = */ ncmoe,
1354                /* .split_mode            = */ sm,
1355                /* .load_mode             = */ lm,
1356                /* .lazy_mode             = */ lzm,
1357                /* .main_gpu              = */ mg,
1358                /* .no_kv_offload         = */ nkvo,
1359                /* .flash_attn            = */ fa,
1360                /* .devices               = */ devs,
1361                /* .tensor_split          = */ ts,
1362                /* .tensor_buft_overrides = */ ot,
1363                /* .embeddings            = */ embd,
1364                /* .no_op_offload         = */ nopo,
1365                /* .no_host               = */ noh,
1366                /* .fit_target            = */ fpt,
1367                /* .fit_min_ctx           = */ fpc,
1368            };
1369            instances.push_back(instance);
1370        }
1371
1372        for (const auto & n_gen : params.n_gen) {
1373            if (n_gen == 0) {
1374                continue;
1375            }
1376            cmd_params_instance instance = {
1377                /* .model                 = */ m,
1378                /* .n_prompt              = */ 0,
1379                /* .n_gen                 = */ n_gen,
1380                /* .n_depth               = */ nd,
1381                /* .n_batch               = */ nb,
1382                /* .n_ubatch              = */ nub,
1383                /* .type_k                = */ tk,
1384                /* .type_v                = */ tv,
1385                /* .n_threads             = */ nt,
1386                /* .cpu_mask              = */ cm,
1387                /* .cpu_strict            = */ cs,
1388                /* .poll                  = */ pl,
1389                /* .n_gpu_layers          = */ nl,
1390                /* .n_cpu_moe             = */ ncmoe,
1391                /* .split_mode            = */ sm,
1392                /* .load_mode             = */ lm,
1393                /* .lazy_mode             = */ lzm,
1394                /* .main_gpu              = */ mg,
1395                /* .no_kv_offload         = */ nkvo,
1396                /* .flash_attn            = */ fa,
1397                /* .devices               = */ devs,
1398                /* .tensor_split          = */ ts,
1399                /* .tensor_buft_overrides = */ ot,
1400                /* .embeddings            = */ embd,
1401                /* .no_op_offload         = */ nopo,
1402                /* .no_host               = */ noh,
1403                /* .fit_target            = */ fpt,
1404                /* .fit_min_ctx           = */ fpc,
1405            };
1406            instances.push_back(instance);
1407        }
1408
1409        for (const auto & n_pg : params.n_pg) {
1410            if (n_pg.first == 0 && n_pg.second == 0) {
1411                continue;
1412            }
1413            cmd_params_instance instance = {
1414                /* .model                 = */ m,
1415                /* .n_prompt              = */ n_pg.first,
1416                /* .n_gen                 = */ n_pg.second,
1417                /* .n_depth               = */ nd,
1418                /* .n_batch               = */ nb,
1419                /* .n_ubatch              = */ nub,
1420                /* .type_k                = */ tk,
1421                /* .type_v                = */ tv,
1422                /* .n_threads             = */ nt,
1423                /* .cpu_mask              = */ cm,
1424                /* .cpu_strict            = */ cs,
1425                /* .poll                  = */ pl,
1426                /* .n_gpu_layers          = */ nl,
1427                /* .n_cpu_moe             = */ ncmoe,
1428                /* .split_mode            = */ sm,
1429                /* .load_mode             = */ lm,
1430                /* .lazy_mode             = */ lzm,
1431                /* .main_gpu              = */ mg,
1432                /* .no_kv_offload         = */ nkvo,
1433                /* .flash_attn            = */ fa,
1434                /* .devices               = */ devs,
1435                /* .tensor_split          = */ ts,
1436                /* .tensor_buft_overrides = */ ot,
1437                /* .embeddings            = */ embd,
1438                /* .no_op_offload         = */ nopo,
1439                /* .no_host               = */ noh,
1440                /* .fit_target            = */ fpt,
1441                /* .fit_min_ctx           = */ fpc,
1442            };
1443            instances.push_back(instance);
1444        }
1445    }
1446    // clang-format on
1447
1448    return instances;
1449}
1450
1451struct test {
1452    static const std::string build_commit;
1453    static const int         build_number;
1454    const std::string        cpu_info;
1455    const std::string        gpu_info;
1456    std::string              model_filename;
1457    std::string              model_type;
1458    uint64_t                 model_size;
1459    uint64_t                 model_n_params;
1460    int                      n_batch;
1461    int                      n_ubatch;
1462    int                      n_threads;
1463    std::string              cpu_mask;
1464    bool                     cpu_strict;
1465    int                      poll;
1466    ggml_type                type_k;
1467    ggml_type                type_v;
1468    int                      n_gpu_layers;
1469    int                      n_cpu_moe;
1470    llama_split_mode         split_mode;
1471    llama_load_mode          load_mode;
1472    llama_lazy_mode          lazy_mode;
1473    int                      main_gpu;
1474    bool                     no_kv_offload;
1475    llama_flash_attn_type    flash_attn;
1476    std::vector<ggml_backend_dev_t> devices;
1477    std::vector<float>       tensor_split;
1478    std::vector<llama_model_tensor_buft_override> tensor_buft_overrides;
1479    bool                     embeddings;
1480    bool                     no_op_offload;
1481    bool                     no_host;
1482    size_t                   fit_target;
1483    uint32_t                 fit_min_ctx;
1484    int                      n_prompt;
1485    int                      n_gen;
1486    int                      n_depth;
1487    std::string              test_time;
1488    std::vector<uint64_t>    samples_ns;
1489
1490    test(const cmd_params_instance & inst, const llama_model * lmodel, const llama_context * ctx) :
1491        cpu_info(get_cpu_info()),
1492        gpu_info(get_gpu_info()) {
1493
1494        model_filename = inst.model;
1495        char buf[128];
1496        llama_model_desc(lmodel, buf, sizeof(buf));
1497        model_type     = buf;
1498        model_size     = llama_model_size(lmodel);
1499        model_n_params = llama_model_n_params(lmodel);
1500        n_batch        = inst.n_batch;
1501        n_ubatch       = inst.n_ubatch;
1502        n_threads      = inst.n_threads;
1503        cpu_mask       = inst.cpu_mask;
1504        cpu_strict     = inst.cpu_strict;
1505        poll           = inst.poll;
1506        type_k         = inst.type_k;
1507        type_v         = inst.type_v;
1508        n_gpu_layers   = inst.n_gpu_layers;
1509        n_cpu_moe      = inst.n_cpu_moe;
1510        split_mode     = inst.split_mode;
1511        load_mode      = inst.load_mode;
1512        lazy_mode      = inst.lazy_mode;
1513        main_gpu       = inst.main_gpu;
1514        no_kv_offload  = inst.no_kv_offload;
1515        flash_attn     = inst.flash_attn;
1516        devices        = inst.devices;
1517        tensor_split   = inst.tensor_split;
1518        tensor_buft_overrides = inst.tensor_buft_overrides;
1519        embeddings     = inst.embeddings;
1520        no_op_offload  = inst.no_op_offload;
1521        no_host        = inst.no_host;
1522        fit_target     = inst.fit_target;
1523        fit_min_ctx    = inst.fit_min_ctx;
1524        n_prompt       = inst.n_prompt;
1525        n_gen          = inst.n_gen;
1526        n_depth        = inst.n_depth;
1527        // RFC 3339 date-time format
1528        time_t t       = time(NULL);
1529        std::strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&t));
1530        test_time = buf;
1531
1532        (void) ctx;
1533    }
1534
1535    uint64_t avg_ns() const { return ::avg(samples_ns); }
1536
1537    uint64_t stdev_ns() const { return ::stdev(samples_ns); }
1538
1539    std::vector<double> get_ts() const {
1540        int                 n_tokens = n_prompt + n_gen;
1541        std::vector<double> ts;
1542        std::transform(samples_ns.begin(), samples_ns.end(), std::back_inserter(ts),
1543                       [n_tokens](uint64_t t) { return 1e9 * n_tokens / t; });
1544        return ts;
1545    }
1546
1547    double avg_ts() const { return ::avg(get_ts()); }
1548
1549    double stdev_ts() const { return ::stdev(get_ts()); }
1550
1551    static std::string get_backend() {
1552        std::vector<std::string> backends;
1553        bool                     rpc_used = false;
1554        for (size_t i = 0; i < ggml_backend_reg_count(); i++) {
1555            auto *      reg  = ggml_backend_reg_get(i);
1556            std::string name = ggml_backend_reg_name(reg);
1557            if (string_starts_with(name, "RPC")) {
1558                if (ggml_backend_reg_dev_count(reg) > 0) {
1559                    rpc_used = true;
1560                }
1561            } else {
1562                if (name != "CPU") {
1563                    backends.push_back(ggml_backend_reg_name(reg));
1564                }
1565            }
1566        }
1567        if (rpc_used) {
1568            backends.push_back("RPC");
1569        }
1570        return backends.empty() ? "CPU" : join(backends, ",");
1571    }
1572
1573    static const std::vector<std::string> & get_fields() {
1574        static const std::vector<std::string> fields = {
1575            "build_commit",   "build_number",   "cpu_info",      "gpu_info",       "backends",
1576            "model_filename", "model_type",     "model_size",    "model_n_params", "n_batch",
1577            "n_ubatch",       "n_threads",      "cpu_mask",      "cpu_strict",     "poll",
1578            "type_k",         "type_v",         "n_gpu_layers",  "n_cpu_moe",      "split_mode",
1579            "main_gpu",       "no_kv_offload",  "flash_attn",    "devices",        "tensor_split",
1580            "tensor_buft_overrides",            "load_mode",     "lazy_mode",
1581            "embeddings",
1582            "no_op_offload",  "no_host",        "fit_target",    "fit_min_ctx",
1583            "n_prompt",       "n_gen",          "n_depth",
1584            "test_time",      "avg_ns",         "stddev_ns",     "avg_ts",         "stddev_ts"
1585        };
1586        return fields;
1587    }
1588
1589    enum field_type { STRING, BOOL, INT, FLOAT };
1590
1591    static field_type get_field_type(const std::string & field) {
1592        if (field == "build_number" || field == "n_batch" || field == "n_ubatch" || field == "n_threads" ||
1593            field == "poll" || field == "model_size" || field == "model_n_params" || field == "n_gpu_layers" ||
1594            field == "main_gpu" || field == "n_prompt" || field == "n_gen" || field == "n_depth" || field == "avg_ns" ||
1595            field == "stddev_ns" || field == "no_op_offload" || field == "n_cpu_moe" ||
1596            field == "fit_target" || field == "fit_min_ctx" || field == "flash_attn") {
1597            return INT;
1598        }
1599        if (field == "f16_kv" || field == "no_kv_offload" || field == "cpu_strict" ||
1600            field == "embeddings" || field == "no_host") {
1601            return BOOL;
1602        }
1603        if (field == "avg_ts" || field == "stddev_ts") {
1604            return FLOAT;
1605        }
1606        if (field == "load_mode" || field == "lazy_mode") {
1607            return STRING;
1608        }
1609        return STRING;
1610    }
1611
1612    std::vector<std::string> get_values() const {
1613        std::string tensor_split_str;
1614        std::string tensor_buft_overrides_str;
1615        int         max_nonzero = 0;
1616        for (size_t i = 0; i < llama_max_devices(); i++) {
1617            if (tensor_split[i] > 0) {
1618                max_nonzero = i;
1619            }
1620        }
1621        for (int i = 0; i <= max_nonzero; i++) {
1622            char buf[32];
1623            snprintf(buf, sizeof(buf), "%.2f", tensor_split[i]);
1624            tensor_split_str += buf;
1625            if (i < max_nonzero) {
1626                tensor_split_str += "/";
1627            }
1628        }
1629        if (tensor_buft_overrides.size() == 1) {
1630            // Last element of tensor_buft_overrides is always a null pattern
1631            // so if it is only one element long, it must be a null pattern.
1632            GGML_ASSERT(tensor_buft_overrides[0].pattern == nullptr);
1633            tensor_buft_overrides_str += "none";
1634        } else {
1635            for (size_t i = 0; i < tensor_buft_overrides.size()-1; i++) {
1636                // Last element of tensor_buft_overrides is always a null pattern
1637                if (tensor_buft_overrides[i].pattern == nullptr) {
1638                    tensor_buft_overrides_str += "none";
1639                } else {
1640                    tensor_buft_overrides_str += tensor_buft_overrides[i].pattern;
1641                    tensor_buft_overrides_str += "=";
1642                    tensor_buft_overrides_str += ggml_backend_buft_name(tensor_buft_overrides[i].buft);
1643                }
1644                if (i + 2 < tensor_buft_overrides.size()) {
1645                    tensor_buft_overrides_str += ";";
1646                }
1647            }
1648        }
1649        std::vector<std::string> values = { build_commit,
1650                                            std::to_string(build_number),
1651                                            cpu_info,
1652                                            gpu_info,
1653                                            get_backend(),
1654                                            model_filename,
1655                                            model_type,
1656                                            std::to_string(model_size),
1657                                            std::to_string(model_n_params),
1658                                            std::to_string(n_batch),
1659                                            std::to_string(n_ubatch),
1660                                            std::to_string(n_threads),
1661                                            cpu_mask,
1662                                            std::to_string(cpu_strict),
1663                                            std::to_string(poll),
1664                                            ggml_type_name(type_k),
1665                                            ggml_type_name(type_v),
1666                                            std::to_string(n_gpu_layers),
1667                                            std::to_string(n_cpu_moe),
1668                                            split_mode_str(split_mode),
1669                                            std::to_string(main_gpu),
1670                                            std::to_string(no_kv_offload),
1671                                            std::to_string((int) flash_attn),
1672                                            devices_to_string(devices),
1673                                            tensor_split_str,
1674                                            tensor_buft_overrides_str,
1675                                            llama_load_mode_name(load_mode),
1676                                            lazy_mode_str(lazy_mode),
1677                                            std::to_string(embeddings),
1678                                            std::to_string(no_op_offload),
1679                                            std::to_string(no_host),
1680                                            std::to_string(fit_target),
1681                                            std::to_string(fit_min_ctx),
1682                                            std::to_string(n_prompt),
1683                                            std::to_string(n_gen),
1684                                            std::to_string(n_depth),
1685                                            test_time,
1686                                            std::to_string(avg_ns()),
1687                                            std::to_string(stdev_ns()),
1688                                            std::to_string(avg_ts()),
1689                                            std::to_string(stdev_ts()) };
1690        return values;
1691    }
1692
1693    std::map<std::string, std::string> get_map() const {
1694        std::map<std::string, std::string> map;
1695        auto                               fields = get_fields();
1696        auto                               values = get_values();
1697        std::transform(fields.begin(), fields.end(), values.begin(), std::inserter(map, map.end()),
1698                       std::make_pair<const std::string &, const std::string &>);
1699        return map;
1700    }
1701};
1702
1703const std::string test::build_commit = llama_commit();
1704const int         test::build_number = llama_build_number();
1705
1706struct printer {
1707    virtual ~printer() {}
1708
1709    FILE * fout;
1710
1711    virtual void print_header(const cmd_params & params) { (void) params; }
1712
1713    virtual void print_test(const test & t) = 0;
1714
1715    virtual void print_footer() {}
1716};
1717
1718struct csv_printer : public printer {
1719    static std::string escape_csv(const std::string & field) {
1720        std::string escaped = "\"";
1721        for (auto c : field) {
1722            if (c == '"') {
1723                escaped += "\"";
1724            }
1725            escaped += c;
1726        }
1727        escaped += "\"";
1728        return escaped;
1729    }
1730
1731    void print_header(const cmd_params & params) override {
1732        std::vector<std::string> fields = test::get_fields();
1733        fprintf(fout, "%s\n", join(fields, ",").c_str());
1734        (void) params;
1735    }
1736
1737    void print_test(const test & t) override {
1738        std::vector<std::string> values = t.get_values();
1739        std::transform(values.begin(), values.end(), values.begin(), escape_csv);
1740        fprintf(fout, "%s\n", join(values, ",").c_str());
1741    }
1742};
1743
1744static std::string escape_json(const std::string & value) {
1745    std::string escaped;
1746    for (auto c : value) {
1747        if (c == '"') {
1748            escaped += "\\\"";
1749        } else if (c == '\\') {
1750            escaped += "\\\\";
1751        } else if (c <= 0x1f) {
1752            char buf[8];
1753            snprintf(buf, sizeof(buf), "\\u%04x", c);
1754            escaped += buf;
1755        } else {
1756            escaped += c;
1757        }
1758    }
1759    return escaped;
1760}
1761
1762static std::string format_json_value(const std::string & field, const std::string & value) {
1763    switch (test::get_field_type(field)) {
1764        case test::STRING:
1765            return "\"" + escape_json(value) + "\"";
1766        case test::BOOL:
1767            return value == "0" ? "false" : "true";
1768        default:
1769            return value;
1770    }
1771}
1772
1773struct json_printer : public printer {
1774    bool first = true;
1775
1776    void print_header(const cmd_params & params) override {
1777        fprintf(fout, "[\n");
1778        (void) params;
1779    }
1780
1781    void print_fields(const std::vector<std::string> & fields, const std::vector<std::string> & values) {
1782        assert(fields.size() == values.size());
1783        for (size_t i = 0; i < fields.size(); i++) {
1784            fprintf(fout, "    \"%s\": %s,\n", fields.at(i).c_str(),
1785                    format_json_value(fields.at(i), values.at(i)).c_str());
1786        }
1787    }
1788
1789    void print_test(const test & t) override {
1790        if (first) {
1791            first = false;
1792        } else {
1793            fprintf(fout, ",\n");
1794        }
1795        fprintf(fout, "  {\n");
1796        print_fields(test::get_fields(), t.get_values());
1797        fprintf(fout, "    \"samples_ns\": [ %s ],\n", join(t.samples_ns, ", ").c_str());
1798        fprintf(fout, "    \"samples_ts\": [ %s ]\n", join(t.get_ts(), ", ").c_str());
1799        fprintf(fout, "  }");
1800        fflush(fout);
1801    }
1802
1803    void print_footer() override { fprintf(fout, "\n]\n"); }
1804};
1805
1806struct jsonl_printer : public printer {
1807    void print_fields(const std::vector<std::string> & fields, const std::vector<std::string> & values) {
1808        assert(fields.size() == values.size());
1809        for (size_t i = 0; i < fields.size(); i++) {
1810            fprintf(fout, "\"%s\": %s, ", fields.at(i).c_str(), format_json_value(fields.at(i), values.at(i)).c_str());
1811        }
1812    }
1813
1814    void print_test(const test & t) override {
1815        fprintf(fout, "{");
1816        print_fields(test::get_fields(), t.get_values());
1817        fprintf(fout, "\"samples_ns\": [ %s ],", join(t.samples_ns, ", ").c_str());
1818        fprintf(fout, "\"samples_ts\": [ %s ]", join(t.get_ts(), ", ").c_str());
1819        fprintf(fout, "}\n");
1820        fflush(fout);
1821    }
1822};
1823
1824struct markdown_printer : public printer {
1825    std::vector<std::string> fields;
1826
1827    static int get_field_width(const std::string & field) {
1828        if (field == "model") {
1829            return -30;
1830        }
1831        if (field == "t/s") {
1832            return 20;
1833        }
1834        if (field == "size" || field == "params") {
1835            return 10;
1836        }
1837        if (field == "n_gpu_layers") {
1838            return 3;
1839        }
1840        if (field == "n_threads") {
1841            return 7;
1842        }
1843        if (field == "n_batch") {
1844            return 7;
1845        }
1846        if (field == "n_ubatch") {
1847            return 8;
1848        }
1849        if (field == "type_k" || field == "type_v") {
1850            return 6;
1851        }
1852        if (field == "split_mode") {
1853            return 6;
1854        }
1855        if (field == "load_mode") {
1856            return 10;
1857        }
1858        if (field == "flash_attn") {
1859            return 3;
1860        }
1861        if (field == "devices") {
1862            return -12;
1863        }
1864        if (field == "test") {
1865            return 15;
1866        }
1867        if (field == "no_op_offload") {
1868            return 4;
1869        }
1870        if (field == "no_host") {
1871            return 4;
1872        }
1873
1874        int width = std::max((int) field.length(), 10);
1875
1876        if (test::get_field_type(field) == test::STRING) {
1877            return -width;
1878        }
1879        return width;
1880    }
1881
1882    static std::string get_field_display_name(const std::string & field) {
1883        if (field == "n_gpu_layers") {
1884            return "ngl";
1885        }
1886        if (field == "split_mode") {
1887            return "sm";
1888        }
1889        if (field == "n_threads") {
1890            return "threads";
1891        }
1892        if (field == "no_kv_offload") {
1893            return "nkvo";
1894        }
1895        if (field == "flash_attn") {
1896            return "fa";
1897        }
1898        if (field == "load_mode") {
1899            return "lm";
1900        }
1901        if (field == "embeddings") {
1902            return "embd";
1903        }
1904        if (field == "no_op_offload") {
1905            return "nopo";
1906        }
1907        if (field == "no_host") {
1908            return "noh";
1909        }
1910        if (field == "devices") {
1911            return "dev";
1912        }
1913        if (field == "tensor_split") {
1914            return "ts";
1915        }
1916        if (field == "tensor_buft_overrides") {
1917            return "ot";
1918        }
1919        if (field == "fit_target") {
1920            return "fitt";
1921        }
1922        if (field == "fit_min_ctx") {
1923            return "fitc";
1924        }
1925        return field;
1926    }
1927
1928    void print_header(const cmd_params & params) override {
1929        // select fields to print
1930        fields.emplace_back("model");
1931        fields.emplace_back("size");
1932        fields.emplace_back("params");
1933        fields.emplace_back("backend");
1934        bool is_cpu_backend = test::get_backend().find("CPU") != std::string::npos ||
1935                              test::get_backend().find("BLAS") != std::string::npos ||
1936                              test::get_backend().find("ZenDNN") != std::string::npos;
1937        if (!is_cpu_backend) {
1938            fields.emplace_back("n_gpu_layers");
1939        }
1940        if (params.n_cpu_moe.size() > 1 || params.n_cpu_moe != cmd_params_defaults.n_cpu_moe) {
1941            fields.emplace_back("n_cpu_moe");
1942        }
1943        if (params.n_threads.size() > 1 || params.n_threads != cmd_params_defaults.n_threads || is_cpu_backend) {
1944            fields.emplace_back("n_threads");
1945        }
1946        if (params.cpu_mask.size() > 1 || params.cpu_mask != cmd_params_defaults.cpu_mask) {
1947            fields.emplace_back("cpu_mask");
1948        }
1949        if (params.cpu_strict.size() > 1 || params.cpu_strict != cmd_params_defaults.cpu_strict) {
1950            fields.emplace_back("cpu_strict");
1951        }
1952        if (params.poll.size() > 1 || params.poll != cmd_params_defaults.poll) {
1953            fields.emplace_back("poll");
1954        }
1955        if (params.n_batch.size() > 1 || params.n_batch != cmd_params_defaults.n_batch) {
1956            fields.emplace_back("n_batch");
1957        }
1958        if (params.n_ubatch.size() > 1 || params.n_ubatch != cmd_params_defaults.n_ubatch) {
1959            fields.emplace_back("n_ubatch");
1960        }
1961        if (params.type_k.size() > 1 || params.type_k != cmd_params_defaults.type_k) {
1962            fields.emplace_back("type_k");
1963        }
1964        if (params.type_v.size() > 1 || params.type_v != cmd_params_defaults.type_v) {
1965            fields.emplace_back("type_v");
1966        }
1967        if (params.main_gpu.size() > 1 || params.main_gpu != cmd_params_defaults.main_gpu) {
1968            fields.emplace_back("main_gpu");
1969        }
1970        if (params.split_mode.size() > 1 || params.split_mode != cmd_params_defaults.split_mode) {
1971            fields.emplace_back("split_mode");
1972        }
1973        if (params.no_kv_offload.size() > 1 || params.no_kv_offload != cmd_params_defaults.no_kv_offload) {
1974            fields.emplace_back("no_kv_offload");
1975        }
1976        if (params.flash_attn.size() > 1 || params.flash_attn != cmd_params_defaults.flash_attn) {
1977            fields.emplace_back("flash_attn");
1978        }
1979        if (params.devices.size() > 1 || params.devices != cmd_params_defaults.devices) {
1980            fields.emplace_back("devices");
1981        }
1982        if (params.tensor_split.size() > 1 || params.tensor_split != cmd_params_defaults.tensor_split) {
1983            fields.emplace_back("tensor_split");
1984        }
1985        if (params.tensor_buft_overrides.size() > 1 || !vec_vec_tensor_buft_override_equal(params.tensor_buft_overrides, cmd_params_defaults.tensor_buft_overrides)) {
1986            fields.emplace_back("tensor_buft_overrides");
1987        }
1988        if (params.load_mode.size() > 1 || params.load_mode != cmd_params_defaults.load_mode) {
1989            fields.emplace_back("load_mode");
1990        }
1991        if (params.lazy_mode.size() > 1 || params.lazy_mode != cmd_params_defaults.lazy_mode) {
1992            fields.emplace_back("lazy_mode");
1993        }
1994        if (params.embeddings.size() > 1 || params.embeddings != cmd_params_defaults.embeddings) {
1995            fields.emplace_back("embeddings");
1996        }
1997        if (params.no_op_offload.size() > 1 || params.no_op_offload != cmd_params_defaults.no_op_offload) {
1998            fields.emplace_back("no_op_offload");
1999        }
2000        if (params.no_host.size() > 1 || params.no_host != cmd_params_defaults.no_host) {
2001            fields.emplace_back("no_host");
2002        }
2003        if (params.fit_params_target.size() > 1 || params.fit_params_target != cmd_params_defaults.fit_params_target) {
2004            fields.emplace_back("fit_target");
2005        }
2006        if (params.fit_params_min_ctx.size() > 1 || params.fit_params_min_ctx != cmd_params_defaults.fit_params_min_ctx) {
2007            fields.emplace_back("fit_min_ctx");
2008        }
2009        fields.emplace_back("test");
2010        fields.emplace_back("t/s");
2011
2012        fprintf(fout, "|");
2013        for (const auto & field : fields) {
2014            fprintf(fout, " %*s |", get_field_width(field), get_field_display_name(field).c_str());
2015        }
2016        fprintf(fout, "\n");
2017        fprintf(fout, "|");
2018        for (const auto & field : fields) {
2019            int width = get_field_width(field);
2020            fprintf(fout, " %s%s |", std::string(std::abs(width) - 1, '-').c_str(), width > 0 ? ":" : "-");
2021        }
2022        fprintf(fout, "\n");
2023    }
2024
2025    void print_test(const test & t) override {
2026        std::map<std::string, std::string> vmap = t.get_map();
2027
2028        fprintf(fout, "|");
2029        for (const auto & field : fields) {
2030            std::string value;
2031            char        buf[128];
2032            if (field == "model") {
2033                value = t.model_type;
2034            } else if (field == "size") {
2035                if (t.model_size < 1024 * 1024 * 1024) {
2036                    snprintf(buf, sizeof(buf), "%.2f MiB", t.model_size / 1024.0 / 1024.0);
2037                } else {
2038                    snprintf(buf, sizeof(buf), "%.2f GiB", t.model_size / 1024.0 / 1024.0 / 1024.0);
2039                }
2040                value = buf;
2041            } else if (field == "params") {
2042                if (t.model_n_params < 1000 * 1000 * 1000) {
2043                    snprintf(buf, sizeof(buf), "%.2f M", t.model_n_params / 1e6);
2044                } else {
2045                    snprintf(buf, sizeof(buf), "%.2f B", t.model_n_params / 1e9);
2046                }
2047                value = buf;
2048            } else if (field == "backend") {
2049                value = test::get_backend();
2050            } else if (field == "test") {
2051                if (t.n_prompt > 0 && t.n_gen == 0) {
2052                    snprintf(buf, sizeof(buf), "pp%d", t.n_prompt);
2053                } else if (t.n_gen > 0 && t.n_prompt == 0) {
2054                    snprintf(buf, sizeof(buf), "tg%d", t.n_gen);
2055                } else {
2056                    snprintf(buf, sizeof(buf), "pp%d+tg%d", t.n_prompt, t.n_gen);
2057                }
2058                if (t.n_depth > 0) {
2059                    int len = strlen(buf);
2060                    snprintf(buf + len, sizeof(buf) - len, " @ d%d", t.n_depth);
2061                }
2062                value = buf;
2063            } else if (field == "t/s") {
2064                snprintf(buf, sizeof(buf), "%.2f ± %.2f", t.avg_ts(), t.stdev_ts());
2065                value = buf;
2066            } else if (vmap.find(field) != vmap.end()) {
2067                value = vmap.at(field);
2068            } else {
2069                assert(false);
2070                exit(1);
2071            }
2072
2073            int width = get_field_width(field);
2074            if (field == "t/s") {
2075                // HACK: the utf-8 character is 2 bytes
2076                width += 1;
2077            }
2078            fprintf(fout, " %*s |", width, value.c_str());
2079        }
2080        fprintf(fout, "\n");
2081    }
2082
2083    void print_footer() override {
2084        fprintf(fout, "\nbuild: %s (%d)\n", test::build_commit.c_str(), test::build_number);
2085    }
2086};
2087
2088struct sql_printer : public printer {
2089    static std::string get_sql_field_type(const std::string & field) {
2090        switch (test::get_field_type(field)) {
2091            case test::STRING:
2092                return "TEXT";
2093            case test::BOOL:
2094            case test::INT:
2095                return "INTEGER";
2096            case test::FLOAT:
2097                return "REAL";
2098            default:
2099                assert(false);
2100                exit(1);
2101        }
2102    }
2103
2104    void print_header(const cmd_params & params) override {
2105        std::vector<std::string> fields = test::get_fields();
2106        fprintf(fout, "CREATE TABLE IF NOT EXISTS llama_bench (\n");
2107        for (size_t i = 0; i < fields.size(); i++) {
2108            fprintf(fout, "  %s %s%s\n", fields.at(i).c_str(), get_sql_field_type(fields.at(i)).c_str(),
2109                    i < fields.size() - 1 ? "," : "");
2110        }
2111        fprintf(fout, ");\n");
2112        fprintf(fout, "\n");
2113        (void) params;
2114    }
2115
2116    void print_test(const test & t) override {
2117        fprintf(fout, "INSERT INTO llama_bench (%s) ", join(test::get_fields(), ", ").c_str());
2118        fprintf(fout, "VALUES (");
2119        std::vector<std::string> values = t.get_values();
2120        for (size_t i = 0; i < values.size(); i++) {
2121            fprintf(fout, "'%s'%s", values.at(i).c_str(), i < values.size() - 1 ? ", " : "");
2122        }
2123        fprintf(fout, ");\n");
2124    }
2125};
2126
2127struct ctx_state {
2128    int depth = 0; // in tokens
2129
2130    std::vector<uint8_t> buf; // the llama_context state buffer
2131};
2132
2133static bool test_prompt(llama_context * ctx, int n_prompt, int n_batch, int n_threads) {
2134    llama_set_n_threads(ctx, n_threads, n_threads);
2135
2136    const llama_model * model   = llama_get_model(ctx);
2137    const llama_vocab * vocab   = llama_model_get_vocab(model);
2138    const int32_t       n_vocab = llama_vocab_n_tokens(vocab);
2139
2140    std::vector<llama_token> tokens(n_batch);
2141
2142    int n_processed = 0;
2143
2144    while (n_processed < n_prompt) {
2145        int n_tokens = std::min(n_prompt - n_processed, n_batch);
2146        tokens[0]    = n_processed == 0 && llama_vocab_get_add_bos(vocab) ? llama_vocab_bos(vocab) : std::rand() % n_vocab;
2147        for (int i = 1; i < n_tokens; i++) {
2148            tokens[i] = std::rand() % n_vocab;
2149        }
2150        int res = llama_decode(ctx, llama_batch_get_one(tokens.data(), n_tokens));
2151        if (res != 0) {
2152            fprintf(stderr, "%s: failed to decode prompt batch, res = %d\n", __func__, res);
2153            return false;
2154        }
2155        n_processed += n_tokens;
2156    }
2157
2158    llama_synchronize(ctx);
2159    return true;
2160}
2161
2162static bool test_gen(llama_context * ctx, int n_gen, int n_threads) {
2163    llama_set_n_threads(ctx, n_threads, n_threads);
2164
2165    const llama_model * model   = llama_get_model(ctx);
2166    const llama_vocab * vocab   = llama_model_get_vocab(model);
2167    const int32_t       n_vocab = llama_vocab_n_tokens(vocab);
2168
2169    llama_token token = llama_vocab_get_add_bos(vocab) ? llama_vocab_bos(vocab) : std::rand() % n_vocab;
2170
2171    for (int i = 0; i < n_gen; i++) {
2172        int res = llama_decode(ctx, llama_batch_get_one(&token, 1));
2173        if (res != 0) {
2174            fprintf(stderr, "%s: failed to decode generation batch, res = %d\n", __func__, res);
2175            return false;
2176        }
2177        llama_synchronize(ctx);
2178        token = std::rand() % n_vocab;
2179    }
2180    return true;
2181}
2182
2183static void llama_null_log_callback(enum ggml_log_level level, const char * text, void * user_data) {
2184    (void) level;
2185    (void) text;
2186    (void) user_data;
2187}
2188
2189static std::unique_ptr<printer> create_printer(output_formats format) {
2190    switch (format) {
2191        case NONE:
2192            return nullptr;
2193        case CSV:
2194            return std::unique_ptr<printer>(new csv_printer());
2195        case JSON:
2196            return std::unique_ptr<printer>(new json_printer());
2197        case JSONL:
2198            return std::unique_ptr<printer>(new jsonl_printer());
2199        case MARKDOWN:
2200            return std::unique_ptr<printer>(new markdown_printer());
2201        case SQL:
2202            return std::unique_ptr<printer>(new sql_printer());
2203    }
2204    GGML_ABORT("fatal error");
2205}
2206
2207// satisfies -Wmissing-declarations
2208int llama_bench(int argc, char ** argv);
2209
2210int llama_bench(int argc, char ** argv) {
2211    std::setlocale(LC_NUMERIC, "C");
2212    // try to set locale for unicode characters in markdown
2213    std::setlocale(LC_CTYPE, ".UTF-8");
2214
2215#if !defined(NDEBUG)
2216    fprintf(stderr, "warning: asserts enabled, performance may be affected\n");
2217#endif
2218
2219#if (defined(_MSC_VER) && defined(_DEBUG)) || (!defined(_MSC_VER) && !defined(__OPTIMIZE__))
2220    fprintf(stderr, "warning: debug build, performance may be affected\n");
2221#endif
2222
2223#if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
2224    fprintf(stderr, "warning: sanitizer enabled, performance may be affected\n");
2225#endif
2226
2227    // initialize backends
2228    ggml_backend_load_all();
2229
2230    cmd_params params = parse_cmd_params(argc, argv);
2231
2232    auto * cpu_dev = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU);
2233    if (!cpu_dev) {
2234        fprintf(stderr, "%s: error: CPU backend is not loaded\n", __func__);
2235        return 1;
2236    }
2237    auto * cpu_reg = ggml_backend_dev_backend_reg(cpu_dev);
2238    auto * ggml_threadpool_new_fn = (decltype(ggml_threadpool_new) *) ggml_backend_reg_get_proc_address(cpu_reg, "ggml_threadpool_new");
2239    auto * ggml_threadpool_free_fn = (decltype(ggml_threadpool_free) *) ggml_backend_reg_get_proc_address(cpu_reg, "ggml_threadpool_free");
2240
2241    // initialize llama.cpp
2242    if (!params.verbose) {
2243        llama_log_set(llama_null_log_callback, NULL);
2244    }
2245    llama_backend_init();
2246    llama_numa_init(params.numa);
2247
2248    if (!set_process_priority(params.prio)) {
2249        fprintf(stderr, "%s: error: failed to set process priority\n", __func__);
2250        return 1;
2251    }
2252
2253    // initialize printer
2254    std::unique_ptr<printer> p     = create_printer(params.output_format);
2255    std::unique_ptr<printer> p_err = create_printer(params.output_format_stderr);
2256
2257    if (p) {
2258        p->fout = stdout;
2259        p->print_header(params);
2260    }
2261
2262    if (p_err) {
2263        p_err->fout = stderr;
2264        p_err->print_header(params);
2265    }
2266
2267    std::vector<cmd_params_instance> params_instances = get_cmd_params_instances(params);
2268
2269    llama_model *               lmodel    = nullptr;
2270    const cmd_params_instance * prev_inst = nullptr;
2271
2272    // store the llama_context state at the previous depth that we performed a test
2273    // ref: https://github.com/ggml-org/llama.cpp/pull/16944#issuecomment-3478151721
2274    ctx_state cstate;
2275
2276    int  params_idx   = 0;
2277    auto params_count = params_instances.size();
2278    for (const auto & inst : params_instances) {
2279        params_idx++;
2280        if (params.progress) {
2281            fprintf(stderr, "llama-bench: benchmark %d/%zu: starting\n", params_idx, params_count);
2282        }
2283        auto mparams = inst.to_llama_mparams();
2284        auto cparams = inst.to_llama_cparams();
2285
2286        bool do_fit = inst.fit_target != cmd_params_defaults.fit_params_target[0] ||
2287                      inst.fit_min_ctx != cmd_params_defaults.fit_params_min_ctx[0];
2288
2289        std::vector<float> fit_tensor_split(llama_max_devices(), 0.0f);
2290        std::vector<llama_model_tensor_buft_override> fit_overrides(llama_max_tensor_buft_overrides(), {nullptr, nullptr});
2291
2292        if (do_fit) {
2293            // free the previous model so fit sees full free VRAM
2294            if (lmodel) {
2295                llama_model_free(lmodel);
2296                lmodel    = nullptr;
2297                prev_inst = nullptr;
2298            }
2299
2300            // use default n_gpu_layers and n_ctx so common_fit_params can adjust them
2301            mparams.n_gpu_layers          = llama_model_default_params().n_gpu_layers;
2302            mparams.tensor_split          = fit_tensor_split.data();
2303            mparams.tensor_buft_overrides = fit_overrides.data();
2304            cparams.n_ctx                 = 0;
2305
2306            std::vector<size_t> margins(llama_max_devices(), inst.fit_target * 1024 * 1024);
2307
2308            uint32_t n_ctx_needed = inst.n_prompt + inst.n_gen + inst.n_depth;
2309            cparams.n_ctx = std::max(cparams.n_ctx, n_ctx_needed);
2310
2311            common_fit_params(inst.model.c_str(), &mparams, &cparams,
2312                fit_tensor_split.data(),
2313                fit_overrides.data(),
2314                margins.data(),
2315                inst.fit_min_ctx,
2316                nullptr,
2317                params.verbose ? GGML_LOG_LEVEL_DEBUG : GGML_LOG_LEVEL_ERROR);
2318       }
2319
2320        // keep the same model between tests when possible
2321        if (!lmodel || !prev_inst || !inst.equal_mparams(*prev_inst)) {
2322            if (lmodel) {
2323                llama_model_free(lmodel);
2324            }
2325
2326            lmodel = llama_model_load_from_file(inst.model.c_str(), mparams);
2327            if (lmodel == NULL) {
2328                fprintf(stderr, "%s: error: failed to load model '%s'\n", __func__, inst.model.c_str());
2329                return 1;
2330            }
2331            prev_inst = &inst;
2332        }
2333
2334        llama_context * ctx = llama_init_from_model(lmodel, cparams);
2335        if (ctx == NULL) {
2336            fprintf(stderr, "%s: error: failed to create context with model '%s'\n", __func__, inst.model.c_str());
2337            llama_model_free(lmodel);
2338            return 1;
2339        }
2340
2341        test t(inst, lmodel, ctx);
2342
2343        llama_memory_clear(llama_get_memory(ctx), false);
2344
2345        // cool off before the test
2346        if (params.delay) {
2347            std::this_thread::sleep_for(std::chrono::seconds(params.delay));
2348        }
2349
2350        struct ggml_threadpool_params tpp = ggml_threadpool_params_default(t.n_threads);
2351        if (!parse_cpu_mask(t.cpu_mask, tpp.cpumask)) {
2352            fprintf(stderr, "%s: failed to parse cpu-mask: %s\n", __func__, t.cpu_mask.c_str());
2353            llama_free(ctx);
2354            llama_model_free(lmodel);
2355            exit(1);
2356        }
2357        tpp.strict_cpu = t.cpu_strict;
2358        tpp.poll       = t.poll;
2359        tpp.prio       = params.prio;
2360
2361        struct ggml_threadpool * threadpool = ggml_threadpool_new_fn(&tpp);
2362        if (!threadpool) {
2363            fprintf(stderr, "%s: threadpool create failed : n_threads %d\n", __func__, tpp.n_threads);
2364            llama_free(ctx);
2365            llama_model_free(lmodel);
2366            exit(1);
2367        }
2368
2369        llama_attach_threadpool(ctx, threadpool, NULL);
2370
2371        // warmup run
2372        if (!params.no_warmup) {
2373            if (t.n_prompt > 0) {
2374                if (params.progress) {
2375                    fprintf(stderr, "llama-bench: benchmark %d/%zu: warmup prompt run\n", params_idx, params_count);
2376                }
2377                //test_prompt(ctx, std::min(t.n_batch, std::min(t.n_prompt, 32)), 0, t.n_batch, t.n_threads);
2378                bool res = test_prompt(ctx, t.n_prompt, t.n_batch, t.n_threads);
2379                if (!res) {
2380                    fprintf(stderr, "%s: error: failed to run prompt warmup\n", __func__);
2381                    llama_free(ctx);
2382                    llama_model_free(lmodel);
2383                    exit(1);
2384                }
2385            }
2386            if (t.n_gen > 0) {
2387                if (params.progress) {
2388                    fprintf(stderr, "llama-bench: benchmark %d/%zu: warmup generation run\n", params_idx, params_count);
2389                }
2390                bool res = test_gen(ctx, 1, t.n_threads);
2391                if (!res) {
2392                    fprintf(stderr, "%s: error: failed to run gen warmup\n", __func__);
2393                    llama_free(ctx);
2394                    llama_model_free(lmodel);
2395                    exit(1);
2396                }
2397            }
2398        }
2399
2400        for (int i = 0; i < params.reps; i++) {
2401            llama_memory_clear(llama_get_memory(ctx), false);
2402
2403            if (t.n_depth > 0) {
2404                bool is_cached = t.n_depth == cstate.depth;
2405
2406                if (is_cached) {
2407                    // if previously we have computed at this depth, just restore the state
2408                    const size_t ret = llama_state_seq_set_data(ctx, cstate.buf.data(), cstate.buf.size(), 0);
2409                    if (ret == 0) {
2410                        // if the old state is incompatible with the current context - reprocess from scratch
2411                        is_cached = false;
2412                    }
2413                }
2414
2415                if (!is_cached) {
2416                    if (params.progress) {
2417                        fprintf(stderr, "llama-bench: benchmark %d/%zu: depth run %d/%d\n", params_idx, params_count,
2418                                i + 1, params.reps);
2419                    }
2420                    bool res = test_prompt(ctx, t.n_depth, t.n_batch, t.n_threads);
2421                    if (!res) {
2422                        fprintf(stderr, "%s: error: failed to run depth\n", __func__);
2423                        llama_free(ctx);
2424                        llama_model_free(lmodel);
2425                        exit(1);
2426                    }
2427
2428                    // store the context state for reuse in later runs
2429                    cstate.depth = t.n_depth;
2430                    cstate.buf.resize(llama_state_seq_get_size(ctx, 0));
2431                    llama_state_seq_get_data(ctx, cstate.buf.data(), cstate.buf.size(), 0);
2432                } else {
2433                    if (params.progress) {
2434                        fprintf(stderr, "llama-bench: benchmark %d/%zu: depth run %d/%d (cached)\n", params_idx, params_count,
2435                                i + 1, params.reps);
2436                    }
2437                }
2438            }
2439
2440            uint64_t t_start = get_time_ns();
2441
2442            if (t.n_prompt > 0) {
2443                if (params.progress) {
2444                    fprintf(stderr, "llama-bench: benchmark %d/%zu: prompt run %d/%d\n", params_idx, params_count,
2445                            i + 1, params.reps);
2446                }
2447                bool res = test_prompt(ctx, t.n_prompt, t.n_batch, t.n_threads);
2448                if (!res) {
2449                    fprintf(stderr, "%s: error: failed to run prompt\n", __func__);
2450                    llama_free(ctx);
2451                    llama_model_free(lmodel);
2452                    exit(1);
2453                }
2454            }
2455            if (t.n_gen > 0) {
2456                if (params.progress) {
2457                    fprintf(stderr, "llama-bench: benchmark %d/%zu: generation run %d/%d\n", params_idx, params_count,
2458                            i + 1, params.reps);
2459                }
2460                bool res = test_gen(ctx, t.n_gen, t.n_threads);
2461                if (!res) {
2462                    fprintf(stderr, "%s: error: failed to run gen\n", __func__);
2463                    llama_free(ctx);
2464                    llama_model_free(lmodel);
2465                    exit(1);
2466                }
2467            }
2468
2469            uint64_t t_ns = get_time_ns() - t_start;
2470            t.samples_ns.push_back(t_ns);
2471        }
2472
2473        if (p) {
2474            p->print_test(t);
2475            fflush(p->fout);
2476        }
2477
2478        if (p_err) {
2479            p_err->print_test(t);
2480            fflush(p_err->fout);
2481        }
2482
2483        llama_perf_context_print(ctx);
2484
2485        llama_free(ctx);
2486
2487        ggml_threadpool_free_fn(threadpool);
2488    }
2489
2490    llama_model_free(lmodel);
2491
2492    if (p) {
2493        p->print_footer();
2494    }
2495
2496    if (p_err) {
2497        p_err->print_footer();
2498    }
2499
2500    llama_backend_free();
2501
2502    return 0;
2503}