Commit 4c5957c27 for llama.cpp

commit 4c5957c2779c7806919e37b0c2a07b1fe6ee7945
Author: Georgi Gerganov <ggerganov@gmail.com>
Date:   Thu Sep 24 09:17:04 2026 +0300

    test-save-load-state : print a per-model results table in --models mode (#29316)

    * test-save-load-state : print a per-model results table in --models mode

    in --models mode the output was very heavy: every model printed its
    token dumps, per-test headers and PASS lines. instead, silence all
    logging except the table itself (common_log_set_verbosity_thold(0)
    leaves only LOG / LOG_LEVEL_OUTPUT) and print one row per model with
    one column per test, colored PASS/FAIL/SKIP cells, row by row.

    - run_save_load_tests_for_model returns a test_suite with a dynamic
      std::vector<test_status> and continues past failures: tests 3-5 are
      SKIPped when the baseline (test 1) fails, model init failure skips all
    - per-test token dumps, test headers and PASS lines are demoted to
      LOGV(LOG_LEVEL_INFO, ...) so they still show in single-model mode
    - the table header/rows derive their columns from test_names; the
      model name is printed and flushed before the suite runs so the model
      currently in flight is always visible
    - single-model output and exit codes are unchanged

    Assisted-by: pi:llama.cpp/Qwen3.8-27B

    * test-save-load-state : print example usage on -h

    add a print_usage callback passed to common_params_parse, so -h/--help
    also shows example commands for the tool-specific --models option and
    the -lv verbosity level

    Assisted-by: pi:llama.cpp/Qwen3.8-27B

    * test-save-load-state : remove comments

    ref: https://github.com/ggml-org/llama.cpp/pull/29316

    Assisted-by: pi:llama.cpp/Qwen3.8-27B

diff --git a/tests/test-save-load-state.cpp b/tests/test-save-load-state.cpp
index 08c7c6772..33ee7143a 100644
--- a/tests/test-save-load-state.cpp
+++ b/tests/test-save-load-state.cpp
@@ -13,6 +13,21 @@

 constexpr double NMSE_THRESHOLD = 1e-5;

+enum class test_status {
+    PASS,
+    FAIL,
+    SKIP,
+};
+
+static const char * test_status_str(test_status status) {
+    switch (status) {
+        case test_status::PASS: return "\033[1;32mPASS\033[0m";
+        case test_status::FAIL: return "\033[1;31mFAIL\033[0m";
+        case test_status::SKIP: return "\033[1;33mSKIP\033[0m";
+    }
+    return "";
+}
+
 // normalized mean squared error = mse(a, b) / mse(a, 0)
 static double nmse(const std::vector<float> & a, const std::vector<float> & b) {
     GGML_ASSERT(a.size() == b.size());
@@ -78,7 +93,8 @@ static generation_result generate_tokens(llama_context * ctx, llama_sampler * sm

         auto next_token = llama_sampler_sample(smpl, ctx, -1);

-        LOG("%d ", next_token);
+        // LOG_LEVEL_INFO gate: visible in single-model mode, silenced in --models table mode
+        LOGV(LOG_LEVEL_INFO, "%d ", next_token);
         result.tokens.push_back(next_token);
         result.logits.push_back(std::move(logits));

@@ -126,7 +142,7 @@ static bool generate_tokens_compare(
         const auto next_token = llama_sampler_sample(smpl, ctx, -1);
         const auto expected_token = expected.tokens[i];

-        LOG("%d ", next_token);
+        LOGV(LOG_LEVEL_INFO, "%d ", next_token);
         if (next_token != expected_token) {
             LOG_TRC("%s: sampled token %d differs from expected %d, using expected token\n", __func__, next_token, expected_token);
         }
@@ -164,14 +180,14 @@ static generation_result test_baseline(struct llama_model * model, const struct
         return {};
     }

-    LOG("\n=== Test 1: baseline ===\n");
+    LOGV(LOG_LEVEL_INFO, "\n=== Test 1: baseline ===\n");

     auto result = generate_tokens(ctx.get(), smpl.get(), n_past, params.n_predict, 0);
     if (result.empty()) {
         return {};
     }

-    LOG("\n");
+    LOGV(LOG_LEVEL_INFO, "\n");

     return result;
 }
@@ -196,7 +212,7 @@ static bool test_seq_rm_isolated(
         return false;
     }

-    LOG("\n=== Test 2: sequence removal isolation ===\n");
+    LOGV(LOG_LEVEL_INFO, "\n=== Test 2: sequence removal isolation ===\n");

     const size_t n_tokens = tokens.size() < 128 ? tokens.size() : 128;
     for (llama_seq_id seq_id = 0; seq_id < 2; ++seq_id) {
@@ -249,7 +265,7 @@ static bool test_seq_rm_isolated(
         return false;
     }

-    LOG("PASS\n");
+    LOGV(LOG_LEVEL_INFO, "PASS\n");
     return true;
 }

@@ -268,7 +284,7 @@ static bool test_state_load(struct llama_model * model, const struct common_para
     auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)};
     llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed));

-    LOG("\n=== Test 3: state load ===\n");
+    LOGV(LOG_LEVEL_INFO, "\n=== Test 3: state load ===\n");

     // Load state from file
     llama_tokens unused_sts(tokens.size());
@@ -293,7 +309,7 @@ static bool test_state_load(struct llama_model * model, const struct common_para
         return false;
     }

-    LOG("\nPASS\n");
+    LOGV(LOG_LEVEL_INFO, "\nPASS\n");
     return true;
 }

@@ -313,7 +329,7 @@ static bool test_seq_cp_host(struct llama_model * model, const struct common_par
     auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)};
     llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed));

-    LOG("\n=== Test 4: seq copy (host) ===\n");
+    LOGV(LOG_LEVEL_INFO, "\n=== Test 4: seq copy (host) ===\n");

     // Load state from file
     llama_tokens unused_sts(tokens.size());
@@ -359,7 +375,7 @@ static bool test_seq_cp_host(struct llama_model * model, const struct common_par
         return false;
     }

-    LOG("\nPASS\n");
+    LOGV(LOG_LEVEL_INFO, "\nPASS\n");
     return true;
 }

@@ -379,7 +395,7 @@ static bool test_seq_cp_device(struct llama_model * model, const struct common_p
     auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)};
     llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed));

-    LOG("\n=== Test 5: seq copy (device) ===\n");
+    LOGV(LOG_LEVEL_INFO, "\n=== Test 5: seq copy (device) ===\n");

     // Load state from file
     llama_tokens unused_sts(tokens.size());
@@ -425,7 +441,7 @@ static bool test_seq_cp_device(struct llama_model * model, const struct common_p
         return false;
     }

-    LOG("\nPASS\n");
+    LOGV(LOG_LEVEL_INFO, "\nPASS\n");
     return true;
 }

@@ -442,7 +458,7 @@ static bool test_seq_cp_scatter(struct llama_model * model, const struct common_
     params_ctx.kv_unified = true;
     auto ctx = llama_context_ptr{llama_init_from_model(model, params_ctx)};

-    LOG("\n=== Test %d: seq copy (%s, scatter) ===\n", test_num, on_device ? "device" : "host");
+    LOGV(LOG_LEVEL_INFO, "\n=== Test %d: seq copy (%s, scatter) ===\n", test_num, on_device ? "device" : "host");

     const uint32_t flags = on_device ? LLAMA_STATE_SEQ_FLAGS_ON_DEVICE : LLAMA_STATE_SEQ_FLAGS_NONE;

@@ -519,7 +535,7 @@ static bool test_seq_cp_scatter(struct llama_model * model, const struct common_
         return false;
     }

-    LOG("\nPASS\n");
+    LOGV(LOG_LEVEL_INFO, "\nPASS\n");
     return true;
 }

@@ -530,7 +546,7 @@ static bool test_state_roundtrip(struct llama_model * model, const struct common
     auto params_ctx = common_context_params_to_llama(params);
     auto ctx = llama_context_ptr{llama_init_from_model(model, params_ctx)};

-    LOG("\n=== Test 8: state blob round-trip ===\n");
+    LOGV(LOG_LEVEL_INFO, "\n=== Test 8: state blob round-trip ===\n");

     if (llama_decode(ctx.get(), llama_batch_get_one(const_cast<llama_token *>(tokens.data()), (int32_t) tokens.size()))) {
         LOG_ERR("\n%s: failed to decode prompt\n", __func__);
@@ -578,14 +594,29 @@ static bool test_state_roundtrip(struct llama_model * model, const struct common
         return false;
     }

-    LOG("\nPASS\n");
+    LOGV(LOG_LEVEL_INFO, "\nPASS\n");
     return true;
 }


+struct test_suite {
+    std::vector<test_status> results;
+
+    bool all_passed() const {
+        return std::all_of(results.begin(), results.end(), [](test_status s) { return s == test_status::PASS; });
+    }
+};
+
+// column headers for the --models table, one per test, in the order they are run
+static const std::vector<const char *> test_names = {
+    "baseline", "seq_rm", "state_load", "cp_h", "cp_d", "cp_h_s", "cp_d_s", "rt",
+};
+
 // Run the full save/load test suite (tests 1-8) for a single model.
-// Returns true if all tests pass, false otherwise.
-static bool run_save_load_tests_for_model(const std::string & model_path, const struct common_params & base_params) {
+// Returns the per-test results.
+static test_suite run_save_load_tests_for_model(const std::string & model_path, const struct common_params & base_params) {
+    test_suite suite;
+
     struct common_params params = base_params;
     params.model.path = model_path;

@@ -594,7 +625,8 @@ static bool run_save_load_tests_for_model(const std::string & model_path, const

     if (model == nullptr) {
         LOG_ERR("%s: failed to init model '%s'\n", __func__, model_path.c_str());
-        return false;
+        suite.results.assign(test_names.size(), test_status::SKIP);
+        return suite;
     }

     GGML_ASSERT(llama_init->context() == nullptr);
@@ -626,51 +658,48 @@ static bool run_save_load_tests_for_model(const std::string & model_path, const

     // Test 1: baseline (saves state to disk)
     auto result_baseline = test_baseline(model, params, tokens);
-    if (result_baseline.empty()) {
-        return false;
-    }
+    suite.results.push_back(result_baseline.empty() ? test_status::FAIL : test_status::PASS);

     // Test 2: sequence removal isolation
-    if (!test_seq_rm_isolated(model, params, tokens)) {
-        return false;
-    }
+    suite.results.push_back(test_seq_rm_isolated(model, params, tokens) ? test_status::PASS : test_status::FAIL);

-    // Test 3: state load
-    if (!test_state_load(model, params, tokens, result_baseline)) {
-        return false;
-    }
+    if (!result_baseline.empty()) {
+        // Test 3: state load
+        suite.results.push_back(test_state_load(model, params, tokens, result_baseline) ? test_status::PASS : test_status::FAIL);

-    // Test 4: seq copy (host)
-    if (!test_seq_cp_host(model, params, tokens, result_baseline)) {
-        return false;
-    }
+        // Test 4: seq copy (host)
+        suite.results.push_back(test_seq_cp_host(model, params, tokens, result_baseline) ? test_status::PASS : test_status::FAIL);

-    // Test 5: seq copy (device)
-    if (!test_seq_cp_device(model, params, tokens, result_baseline)) {
-        return false;
+        // Test 5: seq copy (device)
+        suite.results.push_back(test_seq_cp_device(model, params, tokens, result_baseline) ? test_status::PASS : test_status::FAIL);
+    } else {
+        // tests 3-5 depend on the baseline result and the state file it saves
+        suite.results.push_back(test_status::SKIP);
+        suite.results.push_back(test_status::SKIP);
+        suite.results.push_back(test_status::SKIP);
     }

     // Test 6: seq copy (host, scatter)
-    if (!test_seq_cp_scatter(model, params, tokens, 6, false)) {
-        return false;
-    }
+    suite.results.push_back(test_seq_cp_scatter(model, params, tokens, 6, false) ? test_status::PASS : test_status::FAIL);

     // Test 7: seq copy (device, scatter)
-    if (!test_seq_cp_scatter(model, params, tokens, 7, true)) {
-        return false;
-    }
+    suite.results.push_back(test_seq_cp_scatter(model, params, tokens, 7, true) ? test_status::PASS : test_status::FAIL);

     // Test 8: state blob round-trip
-    if (!test_state_roundtrip(model, params, tokens)) {
-        return false;
-    }
+    suite.results.push_back(test_state_roundtrip(model, params, tokens) ? test_status::PASS : test_status::FAIL);

-    LOG("\nAll tests passed.\n");
-
-    return true;
+    return suite;
 }


+static void print_usage(int /* argc */, char ** argv) {
+    LOG("\nexample usage:\n");
+    LOG("\n  %s -m your_model.gguf\n", argv[0]);
+    LOG("\n  %s --models tests/test-models\n", argv[0]);
+    LOG("\n  %s -m your_model.gguf -lv 5\n", argv[0]);
+    LOG("\n");
+}
+
 int main(int argc, char ** argv) {
     std::setlocale(LC_NUMERIC, "C");

@@ -707,7 +736,7 @@ int main(int argc, char ** argv) {
         params.model.path = models_dir;
     }

-    if (!common_params_parse(fargc, filtered_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
+    if (!common_params_parse(fargc, filtered_argv.data(), params, LLAMA_EXAMPLE_COMMON, print_usage)) {
         return 1;
     }

@@ -742,27 +771,59 @@ int main(int argc, char ** argv) {
             return 1;
         }

-        LOG_INF("%s: running save/load tests over %zu models in '%s'\n", __func__, models.size(), models_dir.c_str());
+        auto col_width = [](const char * name) { return (int) std::max(strlen(name), (size_t) 4); };
+
+        size_t name_width = 5; // "Model"
+        for (const auto & model_path : models) {
+            name_width = std::max(name_width, std::filesystem::path(model_path).filename().string().size());
+        }
+
+        // silence everything but the table itself (LOG has verbosity LOG_LEVEL_OUTPUT = 0)
+        common_log_set_verbosity_thold(0);
+
+        LOG("%-*s", (int) name_width, "Model");
+        for (const auto & name : test_names) {
+            LOG("  %-*s", col_width(name), name);
+        }
+        LOG("\n");
+        common_log_flush(common_log_main());

         size_t n_pass = 0;
         size_t n_fail = 0;
         for (const auto & model_path : models) {
-            LOG("\n================================================================\n");
-            LOG_INF("%s: model %s\n", __func__, model_path.c_str());
+            const auto name = std::filesystem::path(model_path).filename().string();
+
+            LOG("%-*s", (int) name_width, name.c_str());
+            common_log_flush(common_log_main());

-            if (run_save_load_tests_for_model(model_path, params)) {
+            const test_suite suite = run_save_load_tests_for_model(model_path, params);
+
+            for (size_t i = 0; i < suite.results.size(); i++) {
+                LOG("  %s%*s", test_status_str(suite.results[i]), col_width(test_names[i]) - 4, "");
+            }
+            LOG("\n");
+            common_log_flush(common_log_main());
+
+            if (suite.all_passed()) {
                 n_pass++;
             } else {
                 n_fail++;
             }
         }

-        LOG("\n================================================================\n");
+        common_log_set_verbosity_thold(LOG_DEFAULT_LLAMA);
+        common_log_flush(common_log_main());
+
         LOG_INF("%s: summary: %zu passed, %zu failed (of %zu)\n", __func__, n_pass, n_fail, models.size());

         return n_fail == 0 ? 0 : 1;
     }

     // single-model mode
-    return run_save_load_tests_for_model(params.model.path, params) ? 0 : 1;
+    const test_suite suite = run_save_load_tests_for_model(params.model.path, params);
+    const bool all_passed = suite.all_passed();
+    if (all_passed) {
+        LOG("\nAll tests passed.\n");
+    }
+    return all_passed ? 0 : 1;
 }