diff --git a/.github/workflows/build-openvino.yml b/.github/workflows/build-openvino.yml index df4a8f077543..cdc217188135 100644 --- a/.github/workflows/build-openvino.yml +++ b/.github/workflows/build-openvino.yml @@ -19,6 +19,8 @@ on: types: [opened, synchronize, reopened] paths: [ '.github/workflows/build-openvino.yml', + 'common/fit.cpp', + 'tests/test-fit-params.cpp', 'ggml/src/ggml-openvino/**' ] diff --git a/common/fit.cpp b/common/fit.cpp index ab0213fcd0e9..7cb45d2f0c58 100644 --- a/common/fit.cpp +++ b/common/fit.cpp @@ -7,9 +7,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -47,6 +49,33 @@ static std::vector common_get_device_memory_data_impl( void * user_data; } original_logger; ggml_log_level min_level; // prints below this log level go to debug log + std::exception_ptr callback_exception; + std::mutex callback_exception_mutex; + std::atomic callback_failed = false; + + // Keep callback exceptions inside the wrapper, then rethrow after the llama call so the logger guard restores. + void log(ggml_log_level level, const char * text) { + if (callback_failed.load(std::memory_order_acquire)) { + return; + } + try { + const ggml_log_level level_eff = level >= min_level ? level : GGML_LOG_LEVEL_DEBUG; + original_logger.callback(level_eff, text, original_logger.user_data); + } catch (...) { + const std::lock_guard lock(callback_exception_mutex); + if (!callback_exception) { + callback_exception = std::current_exception(); + } + callback_failed.store(true, std::memory_order_release); + } + } + + void rethrow_callback_exception() { + const std::lock_guard lock(callback_exception_mutex); + if (callback_exception) { + std::rethrow_exception(callback_exception); + } + } ~user_data_t() { llama_log_set(original_logger.callback, original_logger.user_data); @@ -56,23 +85,23 @@ static std::vector common_get_device_memory_data_impl( llama_log_get(&ud.original_logger.callback, &ud.original_logger.user_data); ud.min_level = log_level; - llama_log_set([](ggml_log_level level, const char * text, void * user_data) { - const user_data_t * ud = (const user_data_t *) user_data; - const ggml_log_level level_eff = level >= ud->min_level ? level : GGML_LOG_LEVEL_DEBUG; - ud->original_logger.callback(level_eff, text, ud->original_logger.user_data); - }, &ud); + llama_log_set([](ggml_log_level level, const char * text, + void * user_data) { ((user_data_t *) user_data)->log(level, text); }, + &ud); llama_model_params mparams_copy = *mparams; mparams_copy.no_alloc = true; mparams_copy.load_mode = LLAMA_LOAD_MODE_NONE; llama_model_ptr model_owner(llama_model_load_from_file(path_model, mparams_copy)); + ud.rethrow_callback_exception(); llama_model * model = model_owner.get(); if (model == nullptr) { throw std::runtime_error("failed to load model"); } llama_context_ptr ctx_owner(llama_init_from_model(model, *cparams)); + ud.rethrow_callback_exception(); llama_context * ctx = ctx_owner.get(); if (ctx == nullptr) { throw common_params_fit_exception("failed to create llama_context from model"); @@ -108,6 +137,7 @@ static std::vector common_get_device_memory_data_impl( { ggml_backend_dev_t cpu_dev = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU); if (cpu_dev == nullptr) { + ud.rethrow_callback_exception(); throw std::runtime_error("no CPU backend found"); } size_t free; @@ -153,6 +183,9 @@ static std::vector common_get_device_memory_data_impl( hp_n_expert = llama_model_n_expert(model); common_memory_breakdown_print(ctx); + ctx_owner.reset(); + model_owner.reset(); + ud.rethrow_callback_exception(); return ret; } diff --git a/ggml/src/ggml-openvino/ggml-decoder.cpp b/ggml/src/ggml-openvino/ggml-decoder.cpp index 599f41aebbdc..ffc300424a5d 100644 --- a/ggml/src/ggml-openvino/ggml-decoder.cpp +++ b/ggml/src/ggml-openvino/ggml-decoder.cpp @@ -43,8 +43,10 @@ GgmlOvDecoder::GgmlOvDecoder(ggml_cgraph * cgraph, bool is_stateful, bool model_is_splitted, bool is_prefill, - int prefill_chunk_size) : + int prefill_chunk_size, + bool fixed_token_shape) : m_is_static(is_static), + m_fixed_token_shape(fixed_token_shape), m_is_stateful(is_stateful), m_is_prefill(is_prefill), m_naive(false), @@ -66,6 +68,19 @@ GgmlOvDecoder::GgmlOvDecoder(ggml_cgraph * cgraph, set_input_output(); compute_node_dynamic_dims(); + if (m_fixed_token_shape) { + // cgraph nodes are topologically ordered, so one pass reaches every descendant. + for (int i = 0; i < m_cgraph->n_nodes; ++i) { + const ggml_tensor * node = m_cgraph->nodes[i]; + for (int j = 0; j < GGML_MAX_SRC; ++j) { + const ggml_tensor * src = node->src[j]; + if (src != nullptr && (is_output_idx(src, node) || m_out_ids_derived.count(src))) { + m_out_ids_derived.insert(node); + break; + } + } + } + } compute_model_inputs(); compute_model_outputs(); @@ -694,7 +709,7 @@ ov::PartialShape GgmlOvDecoder::get_graph_input_shape(const ggml_tensor * op, if (is_inp_tok(input, op) || is_inp_pos(input, op)) { // tokens or positions - int len = m_is_static ? (m_is_prefill ? m_prefill_chunk_size : 1) : -1; + int len = m_is_static ? (m_is_prefill ? m_prefill_chunk_size : 1) : m_fixed_token_shape ? input->ne[0] : -1; if (m_is_static && is_inp_pos(input, op)) { // IMROPE stacks n_planes (t/h/w/e) position planes back to back len *= get_inp_pos_n_planes(op); @@ -713,6 +728,9 @@ ov::PartialShape GgmlOvDecoder::get_graph_input_shape(const ggml_tensor * op, input_shape = ov::PartialShape{1, 1, -1, -1}; } else { input_shape = ov::PartialShape{-1, 1, -1, -1}; + if (m_fixed_token_shape) { + input_shape[2] = input->ne[1]; + } } } else if (is_kvcache(input, op)) { @@ -734,7 +752,7 @@ ov::PartialShape GgmlOvDecoder::get_graph_input_shape(const ggml_tensor * op, } else if (is_kv_idx(input, op)) { // kv update index - int len = m_is_static ? (m_is_prefill ? m_prefill_chunk_size : 1) : -1; + int len = m_is_static ? (m_is_prefill ? m_prefill_chunk_size : 1) : m_fixed_token_shape ? input->ne[0] : -1; input_shape = ov::PartialShape{1, 1, 1, len}; } else if (is_inp_s_copy(input, op) || is_s_copy_leaf(input)) { @@ -1427,7 +1445,10 @@ ov::PartialShape GgmlOvDecoder::get_view_input_ov_shape(int node_idx, if (dynamic_it != m_node_dynamic_dims.end() && dynamic_it->second != -1) { int dynamic_dim_index = dynamic_it->second; // GGML uses reverse indexing, so convert to OpenVINO indexing - shape[3 - dynamic_dim_index] = m_is_static ? get_static_n_tokens() : -1; + shape[3 - dynamic_dim_index] = m_is_static ? get_static_n_tokens() : + m_fixed_token_shape && !m_out_ids_derived.count(tensor) ? + tensor->ne[dynamic_dim_index] : + -1; } return shape; @@ -1452,7 +1473,10 @@ ov::PartialShape GgmlOvDecoder::get_view_input_src_ov_shape(int node_idx, if (dynamic_it != m_node_dynamic_dims.end() && dynamic_it->second != -1) { int dynamic_dim_index = dynamic_it->second; // GGML uses reverse indexing, so convert to OpenVINO indexing - shape[3 - dynamic_dim_index] = m_is_static ? get_static_n_tokens() : -1; + shape[3 - dynamic_dim_index] = m_is_static ? get_static_n_tokens() : + m_fixed_token_shape && !m_out_ids_derived.count(src_tensor) ? + src_tensor->ne[dynamic_dim_index] : + -1; } return shape; diff --git a/ggml/src/ggml-openvino/ggml-decoder.h b/ggml/src/ggml-openvino/ggml-decoder.h index 8e39a26c8b79..9e5b981f07e7 100644 --- a/ggml/src/ggml-openvino/ggml-decoder.h +++ b/ggml/src/ggml-openvino/ggml-decoder.h @@ -116,7 +116,8 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder { bool is_stateful = false, bool model_is_splitted = false, bool is_prefill = false, - int prefill_chunk_size = 256); + int prefill_chunk_size = 256, + bool fixed_token_shape = false); // Naive graph decoder GgmlOvDecoder(ggml_cgraph * cgraph, std::map> & model_weights); @@ -301,6 +302,7 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder { void set_compute_params(const ComputeParams & compute_params) { m_compute_params = compute_params; } bool m_is_static = false; + bool m_fixed_token_shape = false; bool m_is_stateful = false; bool m_is_prefill = false; bool m_naive = false; @@ -405,6 +407,8 @@ class GgmlOvDecoder : public ov::frontend::ggml::GgmlDecoder { std::set m_model_output_names; std::vector m_node_info_list; std::map m_node_dynamic_dims; + // Nodes downstream of an inp_out_ids gather. Their dims follow n_outputs, which stays dynamic. + std::set m_out_ids_derived; ModelParams m_model_params; ComputeParams m_compute_params; diff --git a/ggml/src/ggml-openvino/model-cache.cpp b/ggml/src/ggml-openvino/model-cache.cpp index 3fc7028d88bc..8cdc0fc21297 100644 --- a/ggml/src/ggml-openvino/model-cache.cpp +++ b/ggml/src/ggml-openvino/model-cache.cpp @@ -178,7 +178,8 @@ uint64_t ggml_openvino_model_fingerprint(const ggml_cgraph * cgraph, bool fa, const int32_t * rope_params, int rope_len, - uint64_t extra_cfg) { + uint64_t extra_cfg, + int64_t fixed_token_count) { uint64_t h = FNV_OFFSET; // Topology: node count + each node's op and name (cheap, and distinguishes @@ -200,6 +201,11 @@ uint64_t ggml_openvino_model_fingerprint(const ggml_cgraph * cgraph, h = fnv1a(h, rope_params, sizeof(int32_t) * static_cast(rope_len)); } h = fnv1a_u64(h, extra_cfg); + if (fixed_token_count > 0) { + constexpr char token_shape_tag[] = "fixed-token-shape"; + h = fnv1a(h, token_shape_tag, sizeof(token_shape_tag) - 1); + h = fnv1a_u64(h, static_cast(fixed_token_count)); + } const std::string ver = ov_version_string(); h = fnv1a(h, ver.data(), ver.size()); diff --git a/ggml/src/ggml-openvino/model-cache.h b/ggml/src/ggml-openvino/model-cache.h index 15967b962203..18cadeb409df 100644 --- a/ggml/src/ggml-openvino/model-cache.h +++ b/ggml/src/ggml-openvino/model-cache.h @@ -32,7 +32,8 @@ uint64_t ggml_openvino_model_fingerprint(const ggml_cgraph * cgraph, bool fa, const int32_t * rope_params, int rope_len, - uint64_t extra_cfg); + uint64_t extra_cfg, + int64_t fixed_token_count = 0); // Path to the compiled-blob file for a fingerprint (/.blob). std::string ggml_openvino_model_cache_blob_path(const std::string & dir, uint64_t fingerprint); diff --git a/ggml/src/ggml-openvino/openvino/op/mul_mat_id.cpp b/ggml/src/ggml-openvino/openvino/op/mul_mat_id.cpp index f1b28c85d401..66c32134acd4 100644 --- a/ggml/src/ggml-openvino/openvino/op/mul_mat_id.cpp +++ b/ggml/src/ggml-openvino/openvino/op/mul_mat_id.cpp @@ -4,6 +4,7 @@ #include "gather_matmul.hpp" #include "ggml-openvino/ggml-openvino-extra.h" +#include #include #include #include @@ -117,6 +118,32 @@ ov::Output translate_mul_mat_id_mxfp4_packed(const NodeContext & conte const int64_t k_blocks = static_cast(packed_shape[3]); const int64_t qk = 32; const int64_t cols = k_blocks * qk; + const bool is_cpu = ggml_openvino_get_device_name() == "CPU"; + + if (is_cpu) { + const ov::PartialShape id_shape = ids.get_partial_shape(); + const ov::PartialShape act_shape = activations.get_partial_shape(); + if (id_shape.rank().is_static() && id_shape.rank().get_length() == 4 && act_shape.rank().is_static() && + act_shape.rank().get_length() == 4 && id_shape[3].is_static() && id_shape[3].get_length() != 0 && + rows != 0 && cols != 0) { + // The CPU JIT uses signed 32-bit gather offsets for the decoded weights. + static constexpr size_t max_selected_bytes = 512ULL << 20; + const size_t max_chunk_tokens = + std::max(1, max_selected_bytes / sizeof(float) / id_shape[3].get_length() / rows / cols); + if (id_shape[2].is_static() && act_shape[1].is_static() && id_shape[2].get_length() > max_chunk_tokens) { + const size_t n_tokens = id_shape[2].get_length(); + ov::OutputVector chunks; + chunks.reserve((n_tokens + max_chunk_tokens - 1) / max_chunk_tokens); + for (size_t begin = 0; begin < n_tokens; begin += max_chunk_tokens) { + const size_t end = std::min(n_tokens, begin + max_chunk_tokens); + chunks.push_back(translate_mul_mat_id_mxfp4_packed(context, expert_weights, + slice_axis(activations, 1, begin, end), + slice_axis(ids, 2, begin, end))); + } + return std::make_shared(chunks, 1); + } + } + } auto packed_shape_4d = const_i64({n_expert, rows, k_blocks, 17}); expert_weights = std::make_shared(expert_weights, packed_shape_4d, false); @@ -144,19 +171,35 @@ ov::Output translate_mul_mat_id_mxfp4_packed(const NodeContext & conte e8m0_lut[0] = std::numeric_limits::min() / 2.0f; e8m0_lut[255] = std::numeric_limits::quiet_NaN(); - auto f4_lut = ov::op::v0::Constant::create(ov::element::f32, ov::Shape{f4e2m1_lut.size()}, f4e2m1_lut); auto scale_lut = ov::op::v0::Constant::create(ov::element::f32, ov::Shape{e8m0_lut.size()}, e8m0_lut); auto selected_packed_weights = std::make_shared(expert_weights, ids, gather_axis); auto scale_byte = slice_axis(selected_packed_weights, 4, 0, 1); auto qs = slice_axis(selected_packed_weights, 4, 1, 17); - auto low = std::make_shared( - qs, ov::op::v0::Constant::create(ov::element::u8, ov::Shape{}, {0x0F}), ov::op::AutoBroadcastType::NUMPY); - auto high_shift = std::make_shared( - qs, ov::op::v0::Constant::create(ov::element::u8, ov::Shape{}, {4}), ov::op::AutoBroadcastType::NUMPY); - auto nibbles = std::make_shared(ov::OutputVector{low, high_shift}, 4); - auto nibble_indices = std::make_shared(nibbles, ov::element::i32); - auto weights_f32 = std::make_shared(f4_lut, nibble_indices, gather_axis); + ov::Output weights_f32; + if (is_cpu) { + std::vector low_lut(256); + std::vector high_lut(256); + for (size_t i = 0; i < low_lut.size(); ++i) { + low_lut[i] = f4e2m1_lut[i & 0x0F]; + high_lut[i] = f4e2m1_lut[i >> 4]; + } + auto low_lut_node = ov::op::v0::Constant::create(ov::element::f32, ov::Shape{low_lut.size()}, low_lut); + auto high_lut_node = ov::op::v0::Constant::create(ov::element::f32, ov::Shape{high_lut.size()}, high_lut); + auto byte_indices = std::make_shared(qs, ov::element::i32); + auto low = std::make_shared(low_lut_node, byte_indices, gather_axis); + auto high = std::make_shared(high_lut_node, byte_indices, gather_axis); + weights_f32 = std::make_shared(ov::OutputVector{low, high}, 4); + } else { + auto f4_lut = ov::op::v0::Constant::create(ov::element::f32, ov::Shape{f4e2m1_lut.size()}, f4e2m1_lut); + auto low = std::make_shared( + qs, ov::op::v0::Constant::create(ov::element::u8, ov::Shape{}, {0x0F}), ov::op::AutoBroadcastType::NUMPY); + auto high = std::make_shared( + qs, ov::op::v0::Constant::create(ov::element::u8, ov::Shape{}, {4}), ov::op::AutoBroadcastType::NUMPY); + auto nibbles = std::make_shared(ov::OutputVector{low, high}, 4); + auto nibble_indices = std::make_shared(nibbles, ov::element::i32); + weights_f32 = std::make_shared(f4_lut, nibble_indices, gather_axis); + } auto scale_indices = std::make_shared(scale_byte, ov::element::i32); auto scales_f32 = std::make_shared(scale_lut, scale_indices, gather_axis); diff --git a/ggml/src/ggml-openvino/utils.cpp b/ggml/src/ggml-openvino/utils.cpp index 4df8381dcbd9..5984558e6631 100644 --- a/ggml/src/ggml-openvino/utils.cpp +++ b/ggml/src/ggml-openvino/utils.cpp @@ -192,7 +192,18 @@ enum ggml_status ov_graph_compute_dynamic(ggml_cgraph * cgraph, std::shared_ptr< // graph shape is identical across all decode steps, so memoize by graph_key: compute // graph_key first (a few hundred us), and if the same key is already in decoder_cache // we know the graph is not splitted (only not-splitted graphs get inserted there). - graph_key key(cgraph); + int64_t fixed_token_count = 0; + // Stateful KV lives in InferRequest and cannot survive a shape-key recompile. + if (device == "CPU" && !stateful && !is_naive(cgraph)) { + for (int i = 0; i < cgraph->n_nodes; ++i) { + const ggml_tensor * node = cgraph->nodes[i]; + if (node->op == GGML_OP_MUL_MAT_ID && node->src[0] != nullptr && node->src[0]->type == GGML_TYPE_MXFP4) { + fixed_token_count = node->src[2]->ne[1]; + break; + } + } + } + graph_key key(cgraph, fixed_token_count); bool key_seen = false; if (!cache_disabled) { std::lock_guard map_lock(r_ctx->ctx_mutex); @@ -200,6 +211,8 @@ enum ggml_status ov_graph_compute_dynamic(ggml_cgraph * cgraph, std::shared_ptr< } bool model_is_splitted = key_seen ? false : is_model_splitted(cgraph); + // Split fragments keep dynamic graph inputs, so their views must stay dynamic too. + const bool fixed_token_shape = fixed_token_count > 0 && !model_is_splitted; if (is_naive(cgraph)) { if (!model_is_splitted) { @@ -338,7 +351,8 @@ enum ggml_status ov_graph_compute_dynamic(ggml_cgraph * cgraph, std::shared_ptr< // two graphs and are left to the plugin-level ov::cache_dir). The decoder is // still needed for I/O mapping, but can be built without weight nodes since // the weights are baked into the imported CompiledModel. - const std::string model_cache_dir = ggml_openvino_model_cache_dir(); + // Each fixed prefill length would export another model-sized blob. + const std::string model_cache_dir = fixed_token_count > 1 ? "" : ggml_openvino_model_cache_dir(); uint64_t model_fp = 0; std::string blob_path, manifest_path; bool imported = false; @@ -353,8 +367,8 @@ enum ggml_status ov_graph_compute_dynamic(ggml_cgraph * cgraph, std::shared_ptr< } if (!model_cache_dir.empty() && !model_is_splitted) { const uint64_t extra_cfg = ggml_openvino_model_cache_extra_cfg(device, stateful); - model_fp = ggml_openvino_model_fingerprint(cgraph, device, /*fa=*/true, m_params.rope_params, - 15, extra_cfg); + model_fp = ggml_openvino_model_fingerprint(cgraph, device, /*fa=*/true, m_params.rope_params, 15, + extra_cfg, fixed_token_count); blob_path = ggml_openvino_model_cache_blob_path(model_cache_dir, model_fp); manifest_path = ggml_openvino_model_cache_manifest_path(model_cache_dir, model_fp); @@ -377,8 +391,10 @@ enum ggml_status ov_graph_compute_dynamic(ggml_cgraph * cgraph, std::shared_ptr< for (const auto & n : GgmlOvDecoder::collect_weight_names(cgraph)) { weight_names[n] = nullptr; } - ggml_decoder = std::make_shared(cgraph, m_params, c_params, weight_names, - is_static, stateful, model_is_splitted); + ggml_decoder = + std::make_shared(cgraph, m_params, c_params, weight_names, is_static, + stateful, model_is_splitted, /*is_prefill=*/false, + /*prefill_chunk_size=*/256, fixed_token_shape); infer_request = std::make_shared(cm.create_infer_request()); entry->ptr = ggml_decoder; // Names must match the decoder's ggml-tensor keys. The non-cached @@ -413,7 +429,8 @@ enum ggml_status ov_graph_compute_dynamic(ggml_cgraph * cgraph, std::shared_ptr< auto model_weights = GgmlOvDecoder::create_weight_nodes(cgraph); ggml_decoder = std::make_shared(cgraph, m_params, c_params, model_weights, is_static, - stateful, model_is_splitted); + stateful, model_is_splitted, /*is_prefill=*/false, + /*prefill_chunk_size=*/256, fixed_token_shape); decoder_end_time = ggml_time_us(); auto input_model = std::make_shared(ggml_decoder); diff --git a/ggml/src/ggml-openvino/utils.h b/ggml/src/ggml-openvino/utils.h index 513fa83c9d6e..b1389770e491 100644 --- a/ggml/src/ggml-openvino/utils.h +++ b/ggml/src/ggml-openvino/utils.h @@ -16,11 +16,14 @@ struct graph_key { int n_nodes; + int64_t fixed_token_count; std::string first_node_name; std::string last_node_name; std::vector input_src_names; - graph_key(const ggml_cgraph * cgraph) : n_nodes(cgraph->n_nodes) { + graph_key(const ggml_cgraph * cgraph, int64_t fixed_token_count = 0) : + n_nodes(cgraph->n_nodes), + fixed_token_count(fixed_token_count) { if (n_nodes > 0) { first_node_name = cgraph->nodes[0]->name; last_node_name = cgraph->nodes[n_nodes - 1]->name; @@ -64,14 +67,16 @@ struct graph_key { } bool operator==(const graph_key & other) const { - return n_nodes == other.n_nodes && first_node_name == other.first_node_name && - last_node_name == other.last_node_name && input_src_names == other.input_src_names; + return n_nodes == other.n_nodes && fixed_token_count == other.fixed_token_count && + first_node_name == other.first_node_name && last_node_name == other.last_node_name && + input_src_names == other.input_src_names; } }; struct graph_key_hash { size_t operator()(const graph_key & key) const { size_t hash = std::hash{}(key.n_nodes); + hash ^= std::hash{}(key.fixed_token_count) + 0x9e3779b9 + (hash << 6) + (hash >> 2); if (key.n_nodes > 0) { hash ^= std::hash{}(key.first_node_name) + 0x9e3779b9 + (hash << 6) + (hash >> 2); hash ^= std::hash{}(key.last_node_name) + 0x9e3779b9 + (hash << 6) + (hash >> 2); diff --git a/tests/test-fit-params.cpp b/tests/test-fit-params.cpp index 250d07c6c4cd..83ae7a42a2d5 100644 --- a/tests/test-fit-params.cpp +++ b/tests/test-fit-params.cpp @@ -107,13 +107,20 @@ static void test_probe_logger_restoration() { void * original_data; llama_log_get(&original_callback, &original_data); struct injected_exception {}; - bool inject = true; + + struct callback_state { + bool inject = true; + int calls = 0; + } state; + const auto callback = +[](ggml_log_level, const char *, void * data) { - if (*static_cast(data)) { + auto * state = static_cast(data); + state->calls++; + if (state->inject) { throw injected_exception{}; } }; - llama_log_set(callback, &inject); + llama_log_set(callback, &state); auto mparams = llama_model_default_params(); auto cparams = llama_context_default_params(); std::vector devices; @@ -125,13 +132,14 @@ static void test_probe_logger_restoration() { } catch (const injected_exception &) { caught = true; } - inject = false; + state.inject = false; ggml_log_callback restored_callback; void * restored_data; llama_log_get(&restored_callback, &restored_data); expect_i64("probe exception injected", caught, true); + expect_i64("probe stops calling a failing callback", state.calls, 1); expect_i64("probe restores callback", restored_callback == callback, true); - expect_i64("probe restores callback data", restored_data == &inject, true); + expect_i64("probe restores callback data", restored_data == &state, true); // A second probe verifies that unwinding released the serialization mutex. try { common_get_device_memory_data("/nonexistent-fit-test/model.gguf", &mparams, &cparams, devices, @@ -140,7 +148,7 @@ static void test_probe_logger_restoration() { } llama_log_get(&restored_callback, &restored_data); expect_i64("failed load restores callback", restored_callback == callback, true); - expect_i64("failed load restores callback data", restored_data == &inject, true); + expect_i64("failed load restores callback data", restored_data == &state, true); llama_log_set(original_callback, original_data); } diff --git a/tests/test-vector-index.cpp b/tests/test-vector-index.cpp index b79d96693b83..4222e4994ee3 100644 --- a/tests/test-vector-index.cpp +++ b/tests/test-vector-index.cpp @@ -2947,14 +2947,14 @@ void check_delta_log_alternating_writers() { for (uint64_t id : ids) { CHECK(ggml_vec_index_contains(writer_a, id) == 1); } - CHECK(ggml_vec_index_len(writer_a) == static_cast(ids.size())); + CHECK(ggml_vec_index_len(writer_a) == static_cast(ids.size())); auto * loaded = ggml_vec_index_load_with_delta(snapshot.c_str(), delta.c_str()); CHECK(loaded != nullptr); for (uint64_t id : ids) { CHECK(ggml_vec_index_contains(loaded, id) == 1); } - CHECK(ggml_vec_index_len(loaded) == static_cast(ids.size())); + CHECK(ggml_vec_index_len(loaded) == static_cast(ids.size())); ggml_vec_index_free(loaded); ggml_vec_index_free(writer_d);