char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
511f9c137
char/aliceai-t5-moe
1#include "utils.h" 2 3#include "ggml-impl.h" 4#include "ggml-openvino-extra.h" 5#include "ggml-openvino/ggml-decoder.h" 6#include "ggml.h" 7#include "model-cache.h" 8#include "openvino/frontend.h" 9#include "openvino/input_model.h" 10 11#include <algorithm> 12#include <cassert> 13#include <cmath> 14#include <cstddef> 15#include <cstdint> 16#include <cstdlib> 17#include <cstring> 18#include <fstream> 19#include <functional> 20#include <future> 21#include <iomanip> 22#include <iostream> 23#include <memory> 24#include <openvino/core/any.hpp> 25#include <openvino/core/graph_util.hpp> 26#include <openvino/core/shape.hpp> 27#include <openvino/core/type/float16.hpp> 28#include <openvino/frontend/manager.hpp> 29#include <openvino/openvino.hpp> 30#include <openvino/runtime/compiled_model.hpp> 31#include <openvino/runtime/infer_request.hpp> 32#include <openvino/runtime/intel_gpu/ocl/ocl.hpp> 33#include <openvino/runtime/intel_npu/properties.hpp> 34#include <openvino/runtime/properties.hpp> 35#include <openvino/runtime/tensor.hpp> 36#include <optional> 37#include <string> 38#include <unordered_map> 39#include <vector> 40 41// Suppress deprecation warning for ov::Tensor::data() 42#pragma GCC diagnostic push 43#pragma GCC diagnostic ignored "-Wdeprecated-declarations" 44 45enum ggml_status ov_graph_compute (ggml_cgraph * cgraph ,ggml_backend_t backend ) { 46ggml_backend_openvino_context * ctx = (ggml_backend_openvino_context * )backend -> context ; 47try { 48if (ggml_openvino_getenv_int ("GGML_OPENVINO_DUMP_CGRAPH" )) { 49 std::string filename = "cgraph_ov.txt" ; 50GgmlOvDecoder ::dump_cgraph (cgraph ,filename ); 51 } 52 53const auto is_static = ggml_openvino_is_npu ()|| ggml_openvino_getenv_int ("GGML_OPENVINO_FORCE_STATIC" ); 54 55GGML_ASSERT (ctx -> runtime_context != nullptr ); 56 std::shared_ptr < ov_runtime_context > r_ctx = std::static_pointer_cast < ov_runtime_context > (ctx -> runtime_context ); 57 58return is_static ?ov_graph_compute_static (cgraph ,r_ctx ) :ov_graph_compute_dynamic (cgraph ,r_ctx ); 59 }catch (const ov::Exception & e ) { 60GGML_LOG_ERROR ("GGML OpenVINO backend ov::Exception: %s\n" ,e .what ()); 61return GGML_STATUS_FAILED ; 62 }catch (const std::exception & e ) { 63GGML_LOG_ERROR ("GGML OpenVINO backend std::exception: %s\n" ,e .what ()); 64return GGML_STATUS_FAILED ; 65 }catch (...) { 66GGML_LOG_ERROR ("GGML OpenVINO backend unknown exception\n" ); 67return GGML_STATUS_FAILED ; 68 } 69} 70 71// For a KV cache input, return an ov::Tensor sized to n_kv (== attention_size 72// for that layer) instead of the fully-allocated ctx_per_seq. Pre-conditions: 73// * non-static (CPU/GPU) backend, single sequence, seq_active_start == 0 74// * ggml KV layout is a contiguous [1, 1, ctx_per_seq, n_heads_kv*head_size] 75// so the first n_kv rows are the live prefix and shrinking the ctx axis 76// gives a valid tensor over the same host storage 77// * not an SWA layer (ring cache): once the window has wrapped the first 78// n_kv rows no longer contain the live prefix 79// On any unmet pre-condition returns std::nullopt; the caller falls back to 80// the full-size tensor. 81static std::optional < ov::Tensor > try_make_kv_sliced_tensor (std::shared_ptr < GgmlOvDecoder > ggml_decoder , 82const std::string & name , 83const ggml_tensor * ggml_tensor ) { 84static const bool kv_slice_disabled = ggml_openvino_getenv_int ("GGML_OPENVINO_DISABLE_KV_SLICE" ); 85if (kv_slice_disabled ) { 86return std::nullopt ; 87 } 88if (ggml_decoder -> is_static ()|| ggml_decoder -> is_stateful ()) { 89return std::nullopt ; 90 } 91if (ggml_tensor -> op != GGML_OP_NONE || ggml_tensor -> view_src != nullptr ) { 92return std::nullopt ; 93 } 94const auto * op = ggml_decoder -> get_tensor_used_op (ggml_tensor ); 95if (!GgmlOvDecoder ::is_kvcache (ggml_tensor ,op )) { 96return std::nullopt ; 97 } 98 99const auto & compute_params = ggml_decoder -> get_compute_params (); 100if (compute_params .n_seq_active != 1 || compute_params .seq_active_start != 0 ) { 101return std::nullopt ; 102 } 103 104int layer ; 105if (auto layer_opt = extract_layer_from_name (name );layer_opt .has_value ()) { 106layer = layer_opt .value (); 107 }else { 108return std::nullopt ; 109 } 110 111const bool is_swa = ggml_decoder -> is_swa_layer (layer ); 112if (is_swa ) { 113return std::nullopt ; 114 } 115const int ctx_per_seq = ggml_decoder -> get_ctx_per_seq (); 116const int n_kv = compute_params .attention_size ; 117if (ctx_per_seq <=0 || n_kv <=0 || n_kv >=ctx_per_seq ) { 118return std::nullopt ; 119 } 120 121 ov::Shape full_shape = ggml_decoder -> get_shape (ggml_tensor ); 122if (full_shape .size ()!= 4 || full_shape [0 ]!= 1 || full_shape [1 ]!= 1 || 123static_cast < int > (full_shape [2 ])!= ctx_per_seq ) { 124return std::nullopt ; 125 } 126 127 ov::Shape sliced_shape = full_shape ; 128sliced_shape [2 ]= static_cast < size_t > (n_kv ); 129 130// Disabling for now as gpu has bug with in-place ScatterUpdate with remote tensors, can re-enable once CVS-186519 is fixed 131// if (ggml_openvino_buffer_is_remote(ggml_tensor)) { 132// auto remote_context = ggml_openvino_get_remote_context(); 133// auto gpu_context = remote_context->as<ov::intel_gpu::ocl::ClContext>(); 134// return gpu_context.create_tensor(ggml_decoder->get_ov_type(ggml_tensor), sliced_shape, ggml_tensor->data); 135// } 136 137return ov::Tensor (ggml_decoder -> get_ov_type (ggml_tensor ),sliced_shape ,ggml_tensor -> data ); 138} 139 140static uint64_t ggml_openvino_model_cache_extra_cfg (const std::string & device ,bool stateful ) { 141const char * manual_gqa_env = ggml_openvino_getenv_str ("GGML_OPENVINO_MANUAL_GQA_ATTN" ); 142const bool manual_gqa_enabled = manual_gqa_env != nullptr ? 143ggml_openvino_getenv_int ("GGML_OPENVINO_MANUAL_GQA_ATTN" )> 0 : 144device == "GPU" ; 145 146uint64_t extra_cfg = 0 ; 147extra_cfg = extra_cfg * 131 + (stateful ?1u :0u ); 148extra_cfg = extra_cfg * 131 + (ggml_openvino_reduce_compile_mem_enabled () ?1u :0u ); 149extra_cfg = extra_cfg * 131 + (ggml_openvino_getenv_int ("GGML_OPENVINO_DISABLE_KV_SLICE" ) ?1u :0u ); 150extra_cfg = extra_cfg * 131 + (manual_gqa_enabled ?1u :0u ); 151return extra_cfg ; 152} 153 154ov::Tensor create_ov_output_tensor (std::shared_ptr < GgmlOvDecoder > ggml_decoder , 155 std::shared_ptr < ov::InferRequest > infer_request , 156int output_index , 157const ggml_tensor * ggml_tensor ) { 158if (auto sliced = try_make_kv_sliced_tensor (ggml_decoder , std::string (ggml_tensor -> name ),ggml_tensor )) { 159return * sliced ; 160 } 161 162// Disabling for now as gpu has bug with in-place ScatterUpdate with remote tensors, can re-enable once CVS-186519 is fixed 163// if (ggml_tensor->extra != nullptr && !ggml_decoder->is_splited_model()) { 164// auto * extra_base = static_cast<ggml_openvino_extra_base *>(ggml_tensor->extra); 165// if (extra_base->type == ggml_openvino_extra_base::Type::TENSOR) { 166// auto * tensor_extra = static_cast<ggml_openvino_tensor_extra *>(extra_base); 167// return *tensor_extra->tensor; 168// } 169// } 170 171auto output_type = ggml_decoder -> get_ov_type (ggml_tensor ); 172 ov::Shape output_shape ; 173void * output_data = ggml_tensor -> data ; 174if (ggml_decoder -> is_static ()) { 175output_shape = infer_request -> get_output_tensor (output_index ).get_shape (); 176 }else { 177// For a CPY into a padded view_src (e.g. a padded KV cache buffer), the 178// OV ScatterUpdate node outputs the full view_src shape, not the CPY node's 179// own (smaller) shape. Using the CPY shape here causes set_output_tensor to 180// fail with a shape-incompatibility error. Use view_src's shape and data 181// pointer instead so the OV tensor matches the model output exactly. 182if (ggml_tensor -> op == GGML_OP_CPY && ggml_tensor -> view_src != nullptr && 183ggml_nbytes (ggml_tensor )!= ggml_nbytes (ggml_tensor -> view_src )) { 184output_shape = ggml_decoder -> get_shape (ggml_tensor -> view_src ); 185output_data = ggml_tensor -> view_src -> data ; 186 }else { 187output_shape = ggml_decoder -> get_shape (ggml_tensor ); 188 } 189 } 190 ov::Tensor output_tensor (output_type ,output_shape ,output_data ); 191return output_tensor ; 192} 193 194enum ggml_status ov_graph_compute_dynamic (ggml_cgraph * cgraph , std::shared_ptr < ov_runtime_context > r_ctx ) { 195auto & core = ov_singleton_core (); 196const auto & config = ggml_openvino_get_compile_config (); 197const auto & device = r_ctx -> device ; 198const auto & stateful = r_ctx -> stateful ; 199static auto is_static = false; 200 201static const bool cache_disabled = ggml_openvino_getenv_int ("GGML_OPENVINO_DISABLE_CACHE" ); 202 203// is_model_splitted is O(n_nodes^2) plus a create_weight_nodes scan and takes ~20 ms 204// on a Llama-1B decode graph. It is called once per graph_compute invocation but the 205// graph shape is identical across all decode steps, so memoize by graph_key: compute 206// graph_key first (a few hundred us), and if the same key is already in decoder_cache 207// we know the graph is not splitted (only not-splitted graphs get inserted there). 208graph_key key (cgraph ); 209bool key_seen = false; 210if (!cache_disabled ) { 211 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 212key_seen = r_ctx -> decoder_cache .find (key )!= r_ctx -> decoder_cache .end (); 213 } 214 215bool model_is_splitted = key_seen ? false :is_model_splitted (cgraph ); 216 217if (is_naive (cgraph )) { 218if (!model_is_splitted ) { 219return naive_compute (cgraph ,core ,device ,config ); 220 } 221 } 222 223auto start_time = ggml_time_us (); 224 225 std::shared_ptr < GgmlOvDecoder > ggml_decoder ; 226 std::shared_ptr < ov::InferRequest > infer_request ; 227ModelParams m_params ; 228ComputeParams c_params ; 229 std::tie (m_params ,c_params )= GgmlOvDecoder ::compute_llm_params (cgraph ,is_static ); 230 231const bool cache_enabled = !model_is_splitted && !cache_disabled ; 232bool cache_hit = false; 233 234int64_t decoder_end_time ; 235int64_t conversion_end_time ; 236int64_t compile_end_time ; 237int64_t infer_end_time ; 238int64_t ov_raw_infer_start ; 239 240 { 241 std::shared_ptr < decoder_runtime_ctx > entry ; 242ModelParams old_m_params ; 243 244if (cache_enabled ) { 245 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 246auto it = r_ctx -> decoder_cache .find (key ); 247cache_hit = it != r_ctx -> decoder_cache .end (); 248if (cache_hit ) { 249entry = it -> second ; 250 }else { 251r_ctx -> clear_caches_locked (); 252auto mutex = std::make_shared < std::mutex > (); 253entry = std::make_shared < decoder_runtime_ctx > (mutex ); 254r_ctx -> decoder_cache [key ]= entry ; 255 } 256 }else { 257auto mutex = std::make_shared < std::mutex > (); 258entry = std::make_shared < decoder_runtime_ctx > (mutex ); 259cache_hit = false; 260 } 261 262 std::lock_guard < std::mutex > lock (* (entry -> mutex )); 263 264if (cache_hit ) { 265ggml_decoder = entry -> ptr ; 266old_m_params = ggml_decoder -> get_model_params (); 267if (!ggml_decoder -> is_splited_model ()) { 268cache_hit = old_m_params .can_reuse_dynamically (m_params ); 269 } 270 } 271 272 std::vector < std::string > ov_input_names ; 273 std::vector < std::string > ov_output_names ; 274 275if (cache_hit ) { 276 std::map < std::string , std::shared_ptr < ov::Node >> model_weights ; 277ggml_decoder -> set_compute_params (c_params ); 278ggml_decoder -> set_model_params (m_params ); 279if (old_m_params .kv_buffer_changed (m_params )) { 280ggml_decoder -> update_io (cgraph ); 281 } 282ggml_decoder -> add_extra_inputs (); 283 { 284 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 285infer_request = r_ctx -> infer_request_cache .at (key ); 286ov_input_names = r_ctx -> ov_input_names_cache .at (key ); 287ov_output_names = r_ctx -> ov_output_names_cache .at (key ); 288 } 289 290if (stateful ) { 291const auto * inp_pos = get_inp_pos_tensor (cgraph ); 292int32_t * pos_data = (int32_t * )inp_pos -> data ; 293auto pos_shape = ggml_decoder -> get_shape (inp_pos ); 294if (pos_data [0 ]== 0 ) { 295infer_request -> reset_state (); 296r_ctx -> stateful_kv_size = pos_shape [3 ]; 297 }else if (r_ctx -> stateful_kv_size == static_cast < size_t > (pos_data [0 ])) { 298r_ctx -> stateful_kv_size += pos_shape [3 ]; 299 }else { 300auto states = infer_request -> query_state (); 301for (auto state :states ) { 302auto state_tensor = state .get_state (); 303auto state_tensor_shape = state_tensor .get_shape (); 304if (static_cast < uint32_t > (pos_data [0 ])> r_ctx -> stateful_kv_size ) { 305 std::string state_name ; 306try { 307state_name = r_ctx -> kv_state_input_name_map .at (state .get_name ()); 308 }catch (...) { 309GGML_LOG_ERROR ( 310"GGML OpenVINO backend stateful inference failed: no input found for the state\n" ); 311return GGML_STATUS_FAILED ; 312 } 313auto kv_tensor = get_ov_input_tensor (ggml_decoder ,state_name ); 314kv_tensor .set_shape ({state_tensor_shape [0 ],kv_tensor .get_shape ()[2 ],state_tensor_shape [2 ], 315state_tensor_shape [3 ]}); 316state_tensor = kv_tensor ; 317state_tensor_shape = state_tensor .get_shape (); 318 } 319 ov::Coordinate begin = {0 ,0 ,0 ,0 }; 320 ov::Coordinate end = {state_tensor_shape [0 ],static_cast < uint32_t > (pos_data [0 ]), 321state_tensor_shape [2 ],state_tensor_shape [3 ]}; 322 ov::Tensor new_state_tensor (state_tensor ,begin ,end ); 323state .set_state (new_state_tensor ); 324 } 325r_ctx -> stateful_kv_size = pos_data [0 ]+ pos_shape [3 ]; 326 } 327 } 328 329decoder_end_time = ggml_time_us (); 330conversion_end_time = decoder_end_time ; 331compile_end_time = decoder_end_time ; 332 }else { 333// Fail fast: a cache-miss recompile feeds weight data to compile_model, but 334// GGML_OPENVINO_RELEASE_WEIGHTS (or GGML_OPENVINO_MEMORY_OPTIMIZE on GPU) 335// may have already dropped the host weight pages 336// (they would read as zeros). That mode requires stable graph shapes. 337if (ggml_openvino_weight_buffers_released ()) { 338GGML_ABORT ( 339"ggml-openvino: a new graph needs to be compiled but host weight buffers were already " 340"released via GGML_OPENVINO_RELEASE_WEIGHTS/GGML_OPENVINO_MEMORY_OPTIMIZE. This mode requires " 341"stable graph shapes; disable host weight release for dynamic workloads." ); 342 } 343if (cache_enabled ) { 344 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 345r_ctx -> infer_request_cache .erase (key ); 346 } 347 348// Frontend-level compiled-model cache (GGML_OPENVINO_COMPILED_MODEL_CACHE_DIR): if this model 349// was compiled before, import the saved blob and skip requant + convert + 350// compile. Only the dynamic single-model path is cached (split models compile 351// two graphs and are left to the plugin-level ov::cache_dir). The decoder is 352// still needed for I/O mapping, but can be built without weight nodes since 353// the weights are baked into the imported CompiledModel. 354const std::string model_cache_dir = ggml_openvino_model_cache_dir (); 355uint64_t model_fp = 0 ; 356 std::string blob_path ,manifest_path ; 357bool imported = false; 358// When the frontend model cache is active it supersedes the plugin-level 359// ov::cache_dir: a blob exported from a model compiled WITH cache_dir cannot 360// be re-imported (import returns an uninitialized model). Strip cache_dir / 361// cache_mode from the config used for the cached compile and the import. 362 ov::AnyMap mc_config = config ; 363if (!model_cache_dir .empty ()) { 364mc_config .erase ("CACHE_DIR" ); 365mc_config .erase ("CACHE_MODE" ); 366 } 367if (!model_cache_dir .empty ()&& !model_is_splitted ) { 368const uint64_t extra_cfg = ggml_openvino_model_cache_extra_cfg (device ,stateful ); 369model_fp = ggml_openvino_model_fingerprint (cgraph ,device ,/*fa=*/ true,m_params .rope_params , 37015 ,extra_cfg ); 371blob_path = ggml_openvino_model_cache_blob_path (model_cache_dir ,model_fp ); 372manifest_path = ggml_openvino_model_cache_manifest_path (model_cache_dir ,model_fp ); 373 374 std::ifstream blob_in (blob_path , std::ios::binary ); 375bool blob_ok = blob_in .is_open (); 376bool manifest_ok = blob_ok && ggml_openvino_model_cache_verify_manifest (manifest_path ,cgraph ,model_fp ); 377if (blob_ok && manifest_ok ) { 378int64_t import_start = ggml_time_us (); 379try { 380 ov::CompiledModel cm ; 381auto remote_context = ggml_openvino_get_remote_context (); 382if (remote_context .has_value ()) { 383cm = core .import_model (blob_in ,remote_context .value (),mc_config ); 384 }else { 385cm = core .import_model (blob_in ,device ,mc_config ); 386 } 387// Lightweight decoder: names-only weight map (membership is all the 388// decoder needs; weights live in the imported model). 389 std::map < std::string , std::shared_ptr < ov::Node >> weight_names ; 390for (const auto & n :GgmlOvDecoder ::collect_weight_names (cgraph )) { 391weight_names [n ]= nullptr ; 392 } 393ggml_decoder = std::make_shared < GgmlOvDecoder > (cgraph ,m_params ,c_params ,weight_names , 394is_static ,stateful ,model_is_splitted ); 395infer_request = std::make_shared < ov::InferRequest > (cm .create_infer_request ()); 396entry -> ptr = ggml_decoder ; 397// Names must match the decoder's ggml-tensor keys. The non-cached 398// path keys off Parameter/Result *friendly names* (set by the 399// frontend); export_model preserves these, and each compiled-model 400// port's node is exactly that Parameter/Result. Use the port nodes 401// directly (NOT get_runtime_model(), whose graph differs and is 402// unsafe to deref this way). 403for (const auto & p :cm .inputs ()) { 404ov_input_names .push_back (p .get_node ()-> get_friendly_name ()); 405 } 406for (const auto & o :cm .outputs ()) { 407ov_output_names .push_back (o .get_node ()-> get_friendly_name ()); 408 } 409imported = true; 410if (ggml_openvino_getenv_int ("GGML_OPENVINO_PROFILING" )) { 411GGML_LOG_INFO (" - Model cache import time: %.3f ms \n" , 412 (ggml_time_us ()- import_start ) /1000.0 ); 413 } 414GGML_LOG_INFO ("ggml-openvino: model cache HIT %s\n" ,blob_path .c_str ()); 415 }catch (const std::exception & e ) { 416GGML_LOG_WARN ("ggml-openvino: model cache import failed (%s), recompiling\n" ,e .what ()); 417imported = false; 418 } 419 } 420 } 421 422 std::shared_ptr < ov::Model > model ; 423if (imported ) { 424decoder_end_time = conversion_end_time = compile_end_time = ggml_time_us (); 425 }else { 426auto model_weights = GgmlOvDecoder ::create_weight_nodes (cgraph ); 427 428ggml_decoder = std::make_shared < GgmlOvDecoder > (cgraph ,m_params ,c_params ,model_weights ,is_static , 429stateful ,model_is_splitted ); 430decoder_end_time = ggml_time_us (); 431 432auto input_model = std::make_shared < ov::frontend::ggml::InputModel > (ggml_decoder ); 433model = ov::frontend::ggml::FrontEnd ::convert (input_model ); 434ggml_decoder -> clear_model_weights (); 435conversion_end_time = ggml_time_us (); 436 437if (ggml_openvino_getenv_int ("GGML_OPENVINO_DUMP_IR" )) { 438char timestamped_filename [64 ]; 439auto timestamp = (long long )ggml_time_us (); 440snprintf (timestamped_filename ,sizeof (timestamped_filename ),"model_%lld.xml" ,timestamp ); 441 ov::serialize (model ,timestamped_filename ); 442 } 443 444// Use the cache-stripped config when the frontend model cache is active, so 445// the resulting CompiledModel can be exported and later re-imported. 446const ov::AnyMap & compile_config = model_cache_dir .empty () ?config :mc_config ; 447 ov::CompiledModel compiled_model ; 448auto remote_context = ggml_openvino_get_remote_context (); 449if (remote_context .has_value ()) { 450compiled_model = core .compile_model (model ,remote_context .value (),compile_config ); 451 }else { 452compiled_model = core .compile_model (model ,device ,compile_config ); 453 } 454compile_end_time = ggml_time_us (); 455 456// Export to the frontend model cache for next time. Publish the blob first, 457// then the manifest, so a cache hit only sees fully written artifacts. 458if (!model_cache_dir .empty ()&& !model_is_splitted && model_fp != 0 ) { 459try { 460const std::string blob_tmp = blob_path + ".tmp" ; 461const std::string manifest_tmp = manifest_path + ".tmp" ; 462if (ggml_openvino_model_cache_write_manifest (manifest_tmp ,cgraph ,model_fp )) { 463 std::ofstream blob_out (blob_tmp , std::ios::binary | std::ios::trunc ); 464if (blob_out .is_open ()) { 465compiled_model .export_model (blob_out ); 466blob_out .close (); 467if (blob_out .good ()) { 468if (std::rename (blob_tmp .c_str (),blob_path .c_str ())== 0 && 469 std::rename (manifest_tmp .c_str (),manifest_path .c_str ())== 0 ) { 470GGML_LOG_INFO ("ggml-openvino: model cache WROTE %s\n" ,blob_path .c_str ()); 471 }else { 472 std::remove (blob_tmp .c_str ()); 473 std::remove (manifest_tmp .c_str ()); 474 } 475 }else { 476 std::remove (blob_tmp .c_str ()); 477 std::remove (manifest_tmp .c_str ()); 478 } 479 }else { 480 std::remove (manifest_tmp .c_str ()); 481 } 482 } 483 }catch (const std::exception & e ) { 484GGML_LOG_WARN ("ggml-openvino: model cache export failed: %s\n" ,e .what ()); 485 } 486 } 487 488infer_request = std::make_shared < ov::InferRequest > (compiled_model .create_infer_request ()); 489entry -> ptr = ggml_decoder ; 490 491for (const auto & ov_param :model -> get_parameters ()) { 492ov_input_names .push_back (ov_param -> get_friendly_name ()); 493 } 494for (const auto & ov_output :model -> get_results ()) { 495ov_output_names .push_back (ov_output -> get_friendly_name ()); 496 } 497 }// end non-imported (compile) path 498 499if (cache_enabled ) { 500 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 501r_ctx -> infer_request_cache [key ]= infer_request ; 502r_ctx -> ov_input_names_cache [key ]= ov_input_names ; 503r_ctx -> ov_output_names_cache [key ]= ov_output_names ; 504 } 505 506if (stateful && cache_enabled ) { 507const auto * inp_pos = get_inp_pos_tensor (cgraph ); 508auto pos_shape = ggml_decoder -> get_shape (inp_pos ); 509r_ctx -> stateful_kv_size = pos_shape [3 ]; 510const auto kv_param_res_names = ggml_decoder -> get_kv_param_res_names (); 511for (const auto & pair :kv_param_res_names ) { 512r_ctx -> kv_state_input_name_map [pair .first + pair .second ]= pair .first ; 513 } 514 } 515 } 516 517for (size_t i = 0 ;i < ov_input_names .size ();i ++ ) { 518auto param_name = ov_input_names [i ]; 519auto input_tensor = get_ov_input_tensor (ggml_decoder ,param_name ); 520infer_request -> set_input_tensor (i ,input_tensor ); 521 522if (ggml_openvino_getenv_int ("GGML_OPENVINO_DEBUG_INPUT" )) { 523print_input_tensor_info (param_name ,input_tensor ); 524 } 525 } 526 527for (size_t i = 0 ;i < ov_output_names .size ();i ++ ) { 528// Debug-only outputs added via GGML_OPENVINO_DEBUG_NODE (see 529// translate_session.cpp) have no corresponding ggml tensor; leave 530// them unbound so OpenVINO allocates its own tensor for them, 531// rather than aliasing a ggml buffer that may be overwritten by a 532// later in-place op before we get to read it. 533const auto & model_outputs = ggml_decoder -> get_model_outputs (); 534auto model_output_it = model_outputs .find (ov_output_names [i ]); 535if (model_output_it == model_outputs .end ()) { 536continue ; 537 } 538auto * ggml_tensor = model_output_it -> second ; 539if (ggml_nbytes (ggml_tensor )== 0 ) { 540continue ; 541 } 542auto output_tensor = create_ov_output_tensor (ggml_decoder ,infer_request ,i ,ggml_tensor ); 543infer_request -> set_output_tensor (i ,output_tensor ); 544 } 545 546ov_raw_infer_start = ggml_time_us (); 547infer_request -> infer (); 548infer_end_time = ggml_time_us (); 549 550if (ggml_openvino_getenv_int ("GGML_OPENVINO_DEBUG_OUTPUT" )|| 551ggml_openvino_getenv_str ("GGML_OPENVINO_DEBUG_NODE" )) { 552for (size_t i = 0 ;i < ov_output_names .size ();i ++ ) { 553const auto output_tensor = infer_request -> get_output_tensor (i ); 554print_output_tensor_info (ov_output_names [i ],output_tensor ,output_tensor .data ()); 555 } 556 } 557 558if (ggml_openvino_getenv_int ("GGML_OPENVINO_PROFILING" )) { 559GGML_LOG_INFO ("\nGGML OpenVINO Backend: \n" ); 560GGML_LOG_INFO (" - Graph decoder time: %.3f ms \n" , (decoder_end_time - start_time ) /1000.0 ); 561if (!cache_hit ) { 562GGML_LOG_INFO (" - Graph conversion time: %.3f ms \n" , 563 (conversion_end_time - decoder_end_time ) /1000.0 ); 564GGML_LOG_INFO (" - Graph compile time: %.3f ms \n" , (compile_end_time - conversion_end_time ) /1000.0 ); 565 } 566GGML_LOG_INFO (" - Graph inference time: %.3f ms \n" , (infer_end_time - compile_end_time ) /1000.0 ); 567GGML_LOG_INFO (" - OV raw infer time: %.3f ms \n" , (infer_end_time - ov_raw_infer_start ) /1000.0 ); 568 } 569 } 570 571// GGML_OPENVINO_RELEASE_WEIGHTS (or GGML_OPENVINO_MEMORY_OPTIMIZE on GPU): the plugin holds its own device copy of 572// every weight after compile, so the host weight buffers can be dropped to reclaim 573// RSS. The GPU backend uses a single dynamic-shape model for both prefill and decode, 574// so once a graph is compiled it is reused for the whole session — the only thing 575// that forces a recompile is clear_caches() on backend teardown. We therefore release 576// on the first cache-hit (model compiled, plugin has its copy) and, crucially, pin the 577// compiled-model cache so it survives backend teardown (see ggml_backend_openvino_free). 578// Without the pin, a later test/context would recompile against the now-dropped pages. 579// A genuinely new graph still fails fast at the cache-miss compile branch. 580if (cache_hit && ggml_openvino_release_weights_enabled (device )&& 581 !ggml_openvino_weight_buffers_released ()) { 582ggml_openvino_release_weight_buffers (); 583 } 584 585return GGML_STATUS_SUCCESS ; 586} 587 588enum ggml_status ov_graph_compute_static (ggml_cgraph * cgraph , std::shared_ptr < ov_runtime_context > r_ctx ) { 589auto & core = ov_singleton_core (); 590 591auto get_prefill_chunk_size = [] { 592static const int chunk_size = []() { 593int env_prefill_chunk_size = ggml_openvino_getenv_int ("GGML_OPENVINO_PREFILL_CHUNK_SIZE" ); 594return env_prefill_chunk_size > 0 ?env_prefill_chunk_size :256 ; 595 }(); 596return chunk_size ; 597 }; 598 599// Normally NPU, but honors GGML_OPENVINO_DEVICE so GGML_OPENVINO_FORCE_STATIC can run the 600// static-shape path on CPU/GPU to isolate translation bugs from NPUW/NPU-driver issues. 601static std::string device = ggml_openvino_get_device_name (); 602static auto is_static = true; 603static auto stateful = false; 604 605auto prefill_chunk_size = get_prefill_chunk_size (); 606const auto & config = ggml_openvino_get_compile_config (); 607 608if (is_naive (cgraph )) { 609return naive_compute (cgraph ,core ,device ,config ); 610 } 611 612auto start_time = ggml_time_us (); 613 614 std::shared_ptr < GgmlOvDecoder > ggml_decoder ; 615 std::shared_ptr < ov::InferRequest > infer_request ; 616ModelParams m_params ; 617ComputeParams c_params ; 618 std::tie (m_params ,c_params )= GgmlOvDecoder ::compute_llm_params (cgraph ,is_static ); 619 620const auto * inp_pos = get_inp_pos_tensor (cgraph ); 621const auto is_prefill = get_is_prefill (cgraph ,inp_pos ); 622graph_key key (cgraph ); 623static const bool cache_enabled = !ggml_openvino_getenv_int ("GGML_OPENVINO_DISABLE_CACHE" ); 624bool cache_hit = false; 625 626int64_t decoder_end_time ; 627int64_t conversion_end_time ; 628int64_t compile_end_time ; 629int64_t infer_end_time ; 630int64_t ov_raw_infer_start ; 631int64_t ov_raw_infer_total = 0 ; 632 633 std::shared_ptr < decoder_runtime_ctx > entry ; 634ModelParams old_m_params ; 635 636if (cache_enabled ) { 637 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 638auto it = r_ctx -> decoder_cache .find (key ); 639cache_hit = it != r_ctx -> decoder_cache .end (); 640if (cache_hit ) { 641entry = it -> second ; 642 }else { 643r_ctx -> clear_caches_locked (); 644auto mutex = std::make_shared < std::mutex > (); 645entry = std::make_shared < decoder_runtime_ctx > (mutex ); 646r_ctx -> decoder_cache [key ]= entry ; 647 } 648 }else { 649auto mutex = std::make_shared < std::mutex > (); 650entry = std::make_shared < decoder_runtime_ctx > (mutex ); 651cache_hit = false; 652 } 653 654 std::lock_guard < std::mutex > lock (* (entry -> mutex )); 655 656if (cache_hit ) { 657ggml_decoder = entry -> ptr ; 658old_m_params = ggml_decoder -> get_model_params (); 659cache_hit = old_m_params .can_reuse_statically (m_params ); 660 } 661 662 std::vector < std::string > ov_input_names_local ; 663 std::vector < std::string > ov_output_names_local ; 664 665if (cache_hit ) { 666 std::map < std::string , std::shared_ptr < ov::Node >> model_weights ; 667ggml_decoder -> m_is_prefill = is_prefill ; 668ggml_decoder -> set_model_params (m_params ); 669ggml_decoder -> set_compute_params (c_params ); 670if (old_m_params .kv_buffer_changed (m_params )) { 671ggml_decoder -> update_io (cgraph ); 672 } 673ggml_decoder -> add_extra_inputs (); 674 { 675 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 676infer_request = 677is_prefill ?r_ctx -> infer_request_cache_prefill .at (key ) :r_ctx -> infer_request_cache .at (key ); 678ov_input_names_local = r_ctx -> ov_input_names_cache .at (key ); 679ov_output_names_local = r_ctx -> ov_output_names_cache .at (key ); 680 } 681 682decoder_end_time = ggml_time_us (); 683conversion_end_time = decoder_end_time ; 684compile_end_time = decoder_end_time ; 685 }else { 686if (cache_enabled ) { 687 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 688r_ctx -> infer_request_cache .erase (key ); 689r_ctx -> infer_request_cache_prefill .erase (key ); 690 } 691 692 std::shared_ptr < ov::Model > model ; 693auto model_weights = GgmlOvDecoder ::create_weight_nodes (cgraph ); 694 695if (m_params .n_heads_kv == -1 ) { 696// graph is not a LLM, e.g. context-shift graph 697prefill_chunk_size = inp_pos -> ne [0 ]; 698 } 699auto ggml_decoder_prefill = std::make_shared < GgmlOvDecoder > ( 700cgraph ,m_params ,c_params ,model_weights ,is_static ,stateful , false, true,prefill_chunk_size ); 701auto ggml_decoder_decode = std::make_shared < GgmlOvDecoder > (cgraph ,m_params ,c_params ,model_weights ,is_static , 702stateful , false, false,prefill_chunk_size ); 703decoder_end_time = ggml_time_us (); 704 705const bool dump_ir = ggml_openvino_getenv_int ("GGML_OPENVINO_DUMP_IR" ); 706const auto dump_ir_timestamp = static_cast < long long > (ggml_time_us ()); 707 708auto build_static_model = [& core ,& config ,dump_ir ,dump_ir_timestamp ]( 709 std::shared_ptr < GgmlOvDecoder > decoder , 710const char * tag , 711 std::shared_ptr < ov::Model > & model , 712 ov::CompiledModel & compiled_model , 713 std::shared_ptr < ov::InferRequest > & infer_request , 714int64_t & local_conversion_end_time , 715int64_t & local_compile_end_time ) { 716auto input_model = std::make_shared < ov::frontend::ggml::InputModel > (decoder ); 717model = ov::frontend::ggml::FrontEnd ::convert (input_model ); 718decoder -> clear_model_weights (); 719local_conversion_end_time = ggml_time_us (); 720 721if (dump_ir ) { 722char timestamped_filename [64 ]; 723snprintf (timestamped_filename ,sizeof (timestamped_filename ),"model_%s_%lld.xml" ,tag , 724dump_ir_timestamp ); 725 ov::serialize (model ,timestamped_filename ); 726 } 727 728compiled_model = core .compile_model (model ,device ,config ); 729infer_request = std::make_shared < ov::InferRequest > (compiled_model .create_infer_request ()); 730local_compile_end_time = ggml_time_us (); 731 }; 732 std::shared_ptr < ov::Model > model_prefill ; 733 std::shared_ptr < ov::Model > model_decode ; 734 ov::CompiledModel compiled_model_prefill ; 735 ov::CompiledModel compiled_model_decode ; 736 std::shared_ptr < ov::InferRequest > infer_request_prefill ; 737 std::shared_ptr < ov::InferRequest > infer_request_decode ; 738int64_t prefill_conversion_end_time ; 739int64_t decode_conversion_end_time ; 740int64_t prefill_compile_end_time ; 741int64_t decode_compile_end_time ; 742auto prefill_future = std::async (std::launch::async ,build_static_model ,ggml_decoder_prefill ,"prefill" , 743 std::ref (model_prefill ), std::ref (compiled_model_prefill ), 744 std::ref (infer_request_prefill ), std::ref (prefill_conversion_end_time ), 745 std::ref (prefill_compile_end_time )); 746auto decode_future = std::async (std::launch::async ,build_static_model ,ggml_decoder_decode ,"decode" , 747 std::ref (model_decode ), std::ref (compiled_model_decode ), 748 std::ref (infer_request_decode ), std::ref (decode_conversion_end_time ), 749 std::ref (decode_compile_end_time )); 750prefill_future .get (); 751decode_future .get (); 752conversion_end_time = std::max (prefill_conversion_end_time ,decode_conversion_end_time ); 753compile_end_time = std::max (prefill_compile_end_time ,decode_compile_end_time ); 754 755model = is_prefill ?model_prefill :model_decode ; 756ggml_decoder = is_prefill ?ggml_decoder_prefill :ggml_decoder_decode ; 757infer_request = is_prefill ?infer_request_prefill :infer_request_decode ; 758entry -> ptr = ggml_decoder ; 759 760for (const auto & ov_param :model -> get_parameters ()) { 761ov_input_names_local .push_back (ov_param -> get_friendly_name ()); 762 } 763for (const auto & ov_output :model -> get_results ()) { 764ov_output_names_local .push_back (ov_output -> get_friendly_name ()); 765 } 766 767if (cache_enabled ) { 768 std::lock_guard < std::mutex > map_lock (r_ctx -> ctx_mutex ); 769r_ctx -> infer_request_cache_prefill [key ]= infer_request_prefill ; 770r_ctx -> infer_request_cache [key ]= infer_request_decode ; 771r_ctx -> ov_input_names_cache [key ]= ov_input_names_local ; 772r_ctx -> ov_output_names_cache [key ]= ov_output_names_local ; 773 } 774 } 775 776if (is_prefill ) { 777auto inp_len = get_inp_pos_n_tokens (cgraph ,inp_pos ); 778for (int chunk_index = 0 ;chunk_index * prefill_chunk_size < inp_len ;chunk_index ++ ) { 779for (size_t i = 0 ;i < ov_input_names_local .size ();i ++ ) { 780auto param_name = ov_input_names_local [i ]; 781auto input_tensor = get_ov_input_tensor_static_prefill (ggml_decoder ,param_name ,chunk_index ); 782infer_request -> set_input_tensor (i ,input_tensor ); 783 784if (ggml_openvino_getenv_int ("GGML_OPENVINO_DEBUG_INPUT" )) { 785const auto input_tensor = infer_request -> get_input_tensor (i ); 786print_input_tensor_info (param_name ,input_tensor ); 787 } 788 } 789 790for (size_t i = 0 ;i < ov_output_names_local .size ();i ++ ) { 791const auto & model_outputs = ggml_decoder -> get_model_outputs (); 792auto model_output_it = model_outputs .find (ov_output_names_local [i ]); 793if (model_output_it == model_outputs .end ()) { 794continue ; 795 } 796auto * ggml_tensor = model_output_it -> second ; 797if (ggml_nbytes (ggml_tensor )== 0 ) { 798// Zero-row in-place writeback (e.g. the empty s_copy defrag remainder). The OV 799// Result is the full cache, so binding it over this 0-byte buffer overflows it. 800continue ; 801 } 802auto output_tensor = create_ov_output_tensor (ggml_decoder ,infer_request ,i ,ggml_tensor ); 803infer_request -> set_output_tensor (i ,output_tensor ); 804 } 805 806ov_raw_infer_start = ggml_time_us (); 807infer_request -> infer (); 808ov_raw_infer_total += ggml_time_us ()- ov_raw_infer_start ; 809 810if (ggml_openvino_getenv_int ("GGML_OPENVINO_DEBUG_OUTPUT" )|| 811ggml_openvino_getenv_str ("GGML_OPENVINO_DEBUG_NODE" )) { 812for (size_t i = 0 ;i < ov_output_names_local .size ();i ++ ) { 813const auto output_tensor = infer_request -> get_output_tensor (i ); 814print_output_tensor_info (ov_output_names_local [i ],output_tensor ,output_tensor .data ()); 815 } 816 } 817 } 818infer_end_time = ggml_time_us (); 819 }else { 820for (size_t i = 0 ;i < ov_input_names_local .size ();i ++ ) { 821auto param_name = ov_input_names_local [i ]; 822auto input_tensor = get_ov_input_tensor_static_decode (ggml_decoder ,param_name ); 823infer_request -> set_input_tensor (i ,input_tensor ); 824 825if (ggml_openvino_getenv_int ("GGML_OPENVINO_DEBUG_INPUT" )) { 826const auto input_tensor = infer_request -> get_input_tensor (i ); 827print_input_tensor_info (param_name ,input_tensor ); 828 } 829 } 830 831for (size_t i = 0 ;i < ov_output_names_local .size ();i ++ ) { 832const auto & model_outputs = ggml_decoder -> get_model_outputs (); 833auto model_output_it = model_outputs .find (ov_output_names_local [i ]); 834if (model_output_it == model_outputs .end ()) { 835continue ; 836 } 837auto * ggml_tensor = model_output_it -> second ; 838if (ggml_nbytes (ggml_tensor )== 0 ) { 839continue ; 840 } 841auto output_tensor = create_ov_output_tensor (ggml_decoder ,infer_request ,i ,ggml_tensor ); 842infer_request -> set_output_tensor (i ,output_tensor ); 843 } 844 845ov_raw_infer_start = ggml_time_us (); 846infer_request -> infer (); 847infer_end_time = ggml_time_us (); 848ov_raw_infer_total = infer_end_time - ov_raw_infer_start ; 849 850if (ggml_openvino_getenv_int ("GGML_OPENVINO_DEBUG_OUTPUT" )|| 851ggml_openvino_getenv_str ("GGML_OPENVINO_DEBUG_NODE" )) { 852for (size_t i = 0 ;i < ov_output_names_local .size ();i ++ ) { 853const auto output_tensor = infer_request -> get_output_tensor (i ); 854print_output_tensor_info (ov_output_names_local [i ],output_tensor ,output_tensor .data ()); 855 } 856 } 857 } 858 859if (ggml_openvino_getenv_int ("GGML_OPENVINO_PROFILING" )) { 860GGML_LOG_INFO ("\nGGML OpenVINO Backend: \n" ); 861GGML_LOG_INFO (" - Graph decoder time: %.3f ms \n" , (decoder_end_time - start_time ) /1000.0 ); 862if (!cache_hit ) { 863GGML_LOG_INFO (" - Graph conversion time: %.3f ms \n" , (conversion_end_time - decoder_end_time ) /1000.0 ); 864GGML_LOG_INFO (" - Graph compile time: %.3f ms \n" , (compile_end_time - conversion_end_time ) /1000.0 ); 865 } 866GGML_LOG_INFO (" - Graph inference time: %.3f ms \n" , (infer_end_time - compile_end_time ) /1000.0 ); 867GGML_LOG_INFO (" - OV raw infer time: %.3f ms \n" ,ov_raw_infer_total /1000.0 ); 868 } 869 870return GGML_STATUS_SUCCESS ; 871} 872 873// Detect whether a cgraph is a split subgraph or not. 874// Step 1 compares each node's recorded use_count with actual fan-out references in node->src. 875// Step 2 verifies that node inputs come from model nodes/weights/leafs; external sources imply split. 876bool is_model_splitted (ggml_cgraph * cgraph ) { 877static const bool fallback_enabled = ggml_openvino_getenv_int ("GGML_OPENVINO_ENABLE_FALLBACK" )!= 0 ; 878if (!fallback_enabled ) { 879return false; 880 } 881 882// Backend op tests execute each node through ggml_graph_view(), which preserves the original 883// graph use_counts while exposing only one node. Treat those single-node views as regular 884// naive graphs so intermediate ops do not look like split-model fragments. 885if (cgraph -> n_nodes <=1 && cgraph -> n_leafs == 0 ) { 886return false; 887 } 888 889// check the nodes of the model are used by the following nodes, through compare the node's use count and the count of nodes that use it as input. If does not match, return true, else return false. 890for (int i = 0 ;i < cgraph -> n_nodes ;i ++ ) { 891ggml_tensor * node = cgraph -> nodes [i ]; 892int use_count = cgraph -> use_counts [ggml_hash_find (& cgraph -> visited_hash_set ,node )]; 893// TODO: this is a workround for the tests case from llama.cpp, fix should from the root cause in the future. 894if ((cgraph -> n_nodes <=1 && use_count == 0 )|| 895 (cgraph -> n_nodes <=1 && node -> op == GGML_OP_VIEW && use_count == 1 && node -> src [0 ]!= nullptr && 896node -> src [0 ]-> op == GGML_OP_NONE )) { 897return false; 898 } 899if (cgraph -> n_nodes == 1 && 900 (cgraph -> nodes [0 ]-> op == GGML_OP_TRANSPOSE || cgraph -> nodes [0 ]-> op == GGML_OP_PERMUTE )) { 901return false; 902 } 903int input_use_count = 0 ; 904for (int j = 0 ;j < cgraph -> n_nodes ;j ++ ) { 905ggml_tensor * other_node = cgraph -> nodes [j ]; 906for (int k = 0 ;k < GGML_MAX_SRC ;k ++ ) { 907if (other_node -> src [k ]== node ) { 908input_use_count ++ ; 909 } 910 } 911 } 912if (use_count != input_use_count && node -> op != GGML_OP_NONE ) { 913return true; 914 } 915 } 916// if all nodes's src node's src is not come from the nodes in the model, we think the model is splitted. This is a complementary check for the above check, because for some special case like the output node is not used by any node, the use count and input use count are both 0, we can not determine whether the model is splitted or not just based on the first check. 917// Only weight-name membership is needed below. With GGML_OPENVINO_REDUCE_COMPILE_MEM 918// use the name-only collector (no weight extraction); otherwise keep the original 919// behavior of building (naive) weight nodes and take their names. 920 std::set < std::string > model_weights ; 921if (ggml_openvino_reduce_compile_mem_enabled ()) { 922model_weights = GgmlOvDecoder ::collect_weight_names (cgraph ); 923 }else { 924for (const auto & kv :GgmlOvDecoder ::create_weight_nodes (cgraph , true)) { 925model_weights .insert (kv .first ); 926 } 927 } 928 std::set < ggml_tensor *> model_nodes (cgraph -> nodes ,cgraph -> nodes + cgraph -> n_nodes ); 929// leaf nodes 930 std::set < ggml_tensor *> model_leafs (cgraph -> leafs ,cgraph -> leafs + cgraph -> n_leafs ); 931for (int i = 0 ;i < cgraph -> n_nodes ;i ++ ) { 932ggml_tensor * node = cgraph -> nodes [i ]; 933for (int j = 0 ;j < GGML_MAX_SRC ;j ++ ) { 934ggml_tensor * src = node -> src [j ]; 935// the src is also not the model weights, we think the model is splitted. 936// the src is also not in model leafs, we think the model is splitted. 937if (src != nullptr && model_nodes .find (src )== model_nodes .end ()&& 938model_weights .find (std::string (src -> name ))== model_weights .end ()&& !model_leafs .empty ()== false&& 939model_leafs .find (src )== model_leafs .end ()) { 940if (GgmlOvDecoder ::is_inp_tok (src ,node )) { 941return false; 942 } 943return true; 944 } 945 } 946 } 947return false; 948} 949 950bool is_naive (ggml_cgraph * cgraph ) { 951constexpr int naive_graph_size_threshold = 20 ; 952int count = 0 ; 953for (int i = 0 ;i < cgraph -> n_nodes ;i ++ ) { 954if (cgraph -> nodes [i ]-> op != GGML_OP_NONE ) { 955count ++ ; 956 } 957 } 958return count < naive_graph_size_threshold ; 959} 960 961enum ggml_status naive_compute (ggml_cgraph * cgraph , 962 ov::Core & core , 963const std::string & device , 964const ov::AnyMap & config ) { 965if (cgraph -> n_nodes == 1 && (cgraph -> nodes [0 ]-> op == GGML_OP_NONE || cgraph -> nodes [0 ]-> op == GGML_OP_VIEW )) { 966return GGML_STATUS_SUCCESS ; 967 } 968 969bool naive = true; 970auto model_weights = GgmlOvDecoder ::create_weight_nodes (cgraph ,naive ); 971auto decoder = std::make_shared < GgmlOvDecoder > (cgraph ,model_weights ); 972auto input_model = std::make_shared < ov::frontend::ggml::InputModel > (decoder ); 973auto model = ov::frontend::ggml::FrontEnd ::convert (input_model ,naive ); 974if (ggml_openvino_getenv_int ("GGML_OPENVINO_DUMP_IR" )) { 975 ov::serialize (model ,"IR_naive.xml" ); 976 } 977 978 std::shared_ptr < ov::InferRequest > infer_request ; 979auto remote_context = ggml_openvino_get_remote_context (); 980if (cgraph -> nodes [0 ]-> op == GGML_OP_MUL_MAT ) { 981// TODO ACCURACY hint triggers a bug in GPU plugin/driver on Lunar Lake. Remove once CVS-182166 is resolved 982core .set_property (device , ov::hint::execution_mode (ov::hint::ExecutionMode ::PERFORMANCE )); 983 }else { 984core .set_property (device , ov::hint::execution_mode (ov::hint::ExecutionMode ::ACCURACY )); 985 } 986if (remote_context .has_value ()) { 987infer_request = std::make_shared < ov::InferRequest > ( 988core .compile_model (model ,remote_context .value (),config ).create_infer_request ()); 989 }else { 990infer_request = 991 std::make_shared < ov::InferRequest > (core .compile_model (model ,device ,config ).create_infer_request ()); 992 } 993 994auto ov_params = model -> get_parameters (); 995for (size_t i = 0 ;i < ov_params .size ();i ++ ) { 996auto param_name = ov_params [i ]-> get_friendly_name (); 997auto input_tensor = get_ov_input_tensor (decoder ,param_name ); 998infer_request -> set_input_tensor (i ,input_tensor ); 999 } 1000 1001// Use get_output_tensor + memcpy instead of set_output_tensor to avoid memory overwritten 1002// when i/o buffer overlaps, e.g. the cgraph is a single PERMUTE 1003 1004infer_request -> infer (); 1005 1006auto ov_results = model -> get_results (); 1007for (size_t i = 0 ;i < ov_results .size ();i ++ ) { 1008auto output_tensor = infer_request -> get_output_tensor (i ); 1009const auto & model_outputs = decoder -> get_model_outputs (); 1010auto model_output_it = model_outputs .find (ov_results [i ]-> get_friendly_name ()); 1011if (model_output_it == model_outputs .end ()) { 1012// Debug-only output added via GGML_OPENVINO_DEBUG_NODE; nothing to copy into. 1013if (ggml_openvino_getenv_int ("GGML_OPENVINO_DEBUG_OUTPUT" )|| 1014ggml_openvino_getenv_str ("GGML_OPENVINO_DEBUG_NODE" )) { 1015print_output_tensor_info (ov_results [i ]-> get_friendly_name (),output_tensor ,output_tensor .data ()); 1016 } 1017continue ; 1018 } 1019auto * ggml_tensor = model_output_it -> second ; 1020 std::memcpy (ggml_tensor -> data ,output_tensor .data (),output_tensor .get_byte_size ()); 1021 } 1022return GGML_STATUS_SUCCESS ; 1023} 1024 1025namespace { 1026template < typename T > void set_zero_diagonal (std::vector < T > & matrix ,size_t rows ,size_t cols ,T zero_value = T {}) { 1027for (size_t i = 0 ;i < rows ;++ i ) { 1028size_t diag_col = std::min (i ,cols - 1 ); 1029matrix [i * cols + diag_col ]= zero_value ; 1030 } 1031} 1032 1033ov::Tensor make_contiguous_split_input_tensor (std::shared_ptr < GgmlOvDecoder > ggml_decoder , 1034const struct ggml_tensor * ggml_tensor , 1035const ov::Shape & input_shape ) { 1036const size_t element_size = ggml_type_size (ggml_tensor -> type ); 1037const size_t block_size = ggml_blck_size (ggml_tensor -> type ); 1038 1039GGML_ASSERT (block_size == 1 && "non-contiguous split inputs must be plain element types" ); 1040 1041const struct ggml_tensor * source_tensor = ggml_tensor -> view_src != nullptr ?ggml_tensor -> view_src :ggml_tensor ; 1042const size_t source_offset = ggml_tensor -> view_src != nullptr ?ggml_tensor -> view_offs :0 ; 1043 1044 std::vector < uint8_t > source_data (ggml_nbytes (source_tensor )); 1045ggml_backend_tensor_get (source_tensor ,source_data .data (),0 ,source_data .size ()); 1046 1047 ov::Tensor input_tensor (ggml_decoder -> get_ov_type (ggml_tensor ),input_shape ); 1048auto * dst = static_cast < uint8_t *> (input_tensor .data ()); 1049size_t dst_offset = 0 ; 1050 1051for (size_t i3 = 0 ;i3 < static_cast < size_t > (ggml_tensor -> ne [3 ]);++ i3 ) { 1052for (size_t i2 = 0 ;i2 < static_cast < size_t > (ggml_tensor -> ne [2 ]);++ i2 ) { 1053for (size_t i1 = 0 ;i1 < static_cast < size_t > (ggml_tensor -> ne [1 ]);++ i1 ) { 1054for (size_t i0 = 0 ;i0 < static_cast < size_t > (ggml_tensor -> ne [0 ]);++ i0 ) { 1055const size_t src_offset = source_offset + i3 * ggml_tensor -> nb [3 ]+ i2 * ggml_tensor -> nb [2 ]+ 1056i1 * ggml_tensor -> nb [1 ]+ i0 * ggml_tensor -> nb [0 ]; 1057 std::memcpy (dst + dst_offset ,source_data .data ()+ src_offset ,element_size ); 1058dst_offset += element_size ; 1059 } 1060 } 1061 } 1062 } 1063 1064return input_tensor ; 1065} 1066 1067ov::Tensor convert_ggml_input_to_ov (std::shared_ptr < GgmlOvDecoder > ggml_decoder ,const std::string & name ) { 1068const auto * ggml_tensor = ggml_decoder -> get_input_ggml_tensor (name ); 1069 1070if (auto sliced = try_make_kv_sliced_tensor (ggml_decoder ,name ,ggml_tensor )) { 1071return * sliced ; 1072 } 1073 1074if (ggml_tensor -> extra != nullptr && !ggml_decoder -> is_splited_model ()) { 1075auto * extra_base = static_cast < ggml_openvino_extra_base *> (ggml_tensor -> extra ); 1076if (extra_base -> type == ggml_openvino_extra_base::Type ::TENSOR ) { 1077// GGML_LOG_DEBUG("Using ggml_tensor->extra as ov::Tensor for input: %s\n", name.c_str()); 1078auto * tensor_extra = static_cast < ggml_openvino_tensor_extra *> (extra_base ); 1079return * tensor_extra -> tensor ; 1080 } 1081 } 1082 1083// GGML_LOG_DEBUG("Converting ggml tensor to ov::Tensor for input: %s\n", name.c_str()); 1084auto * input_data = ggml_tensor -> data ; 1085 ov::Shape input_shape ; 1086if (ggml_tensor -> op == GGML_OP_VIEW && !ggml_decoder -> is_splited_model ()) { 1087// This case is added to make test-backend-ops work 1088input_shape = ggml_decoder -> get_shape (ggml_tensor -> view_src ); 1089 }else { 1090input_shape = ggml_decoder -> get_shape (ggml_tensor ); 1091 } 1092 1093if (ggml_decoder -> is_splited_model ()&& !ggml_is_contiguous (ggml_tensor )) { 1094return make_contiguous_split_input_tensor (ggml_decoder ,ggml_tensor ,input_shape ); 1095 } 1096 1097auto input_tensor = ov::Tensor (ggml_decoder -> get_ov_type (ggml_tensor ),input_shape ,input_data ); 1098return input_tensor ; 1099} 1100}// namespace 1101 1102ov::Tensor get_ov_input_tensor (std::shared_ptr < GgmlOvDecoder > ggml_decoder ,const std::string & param_name ) { 1103 ov::Tensor input_tensor ; 1104auto extra_input = ggml_decoder -> get_model_extra_inputs ().find (param_name ); 1105if (extra_input != ggml_decoder -> get_model_extra_inputs ().end ()) { 1106input_tensor = ov::Tensor (extra_input -> second .type ,extra_input -> second .shape ); 1107* input_tensor .data < int64_t > ()= extra_input -> second .value ; 1108 }else { 1109input_tensor = convert_ggml_input_to_ov (ggml_decoder ,param_name ); 1110 } 1111return input_tensor ; 1112} 1113 1114ov::Tensor get_ov_input_tensor_static_decode (std::shared_ptr < GgmlOvDecoder > ggml_decoder , 1115const std::string & param_name ) { 1116// NPU decoding stage 1117if (ggml_decoder -> get_model_extra_inputs ().count (param_name )) { 1118return get_ov_input_tensor (ggml_decoder ,param_name ); 1119 } 1120const auto * ggml_tensor = ggml_decoder -> get_input_ggml_tensor (param_name ); 1121const auto * op = ggml_decoder -> get_tensor_used_op (ggml_tensor ); 1122 1123if (GgmlOvDecoder ::is_inp_tok (ggml_tensor ,op )|| GgmlOvDecoder ::is_inp_pos (ggml_tensor ,op )|| 1124GgmlOvDecoder ::is_kv_idx (ggml_tensor ,op )) { 1125// IMROPE's inp_pos holds one value per t/h/w/e plane instead of a single position; 1126// with a single decode token the planes are still contiguous, so a flat copy works. 1127const int n_planes = GgmlOvDecoder ::is_inp_pos (ggml_tensor ,op ) ?GgmlOvDecoder ::get_inp_pos_n_planes (op ) :1 ; 1128assert (ggml_tensor -> ne [0 ]== n_planes ); 1129 ov::Shape input_shape = {1 ,1 ,1 , (size_t )n_planes }; 1130 ov::Tensor input_tensor (ggml_decoder -> get_ov_type (ggml_tensor ),input_shape ); 1131 std::memcpy (input_tensor .data (),ggml_tensor -> data ,n_planes * ggml_type_size (ggml_tensor -> type )); 1132return input_tensor ; 1133 } 1134 1135if (GgmlOvDecoder ::is_output_idx (ggml_tensor ,op )) { 1136 ov::Shape input_shape = {1 ,1 ,1 ,1 }; 1137 ov::Tensor input_tensor (ggml_decoder -> get_ov_type (ggml_tensor ),input_shape ); 1138int32_t inp_out_id = * ((int32_t * )ggml_tensor -> data ); 1139assert (ggml_tensor -> ne [0 ]== 1 ); 1140assert (inp_out_id == 0 ); 1141* input_tensor .data < int32_t > ()= inp_out_id ; 1142return input_tensor ; 1143 } 1144 1145if (GgmlOvDecoder ::is_inp_mask (ggml_tensor ,op )) { 1146size_t context_size = ggml_decoder -> get_ctx_size (); 1147if (ggml_tensor -> type == GGML_TYPE_F16 ) { 1148 std::vector < ggml_fp16_t > padded_data = 1149pad_input < ggml_fp16_t > (ggml_tensor ,1 ,context_size ,GGML_FP32_TO_FP16 (- INFINITY )); 1150 ov::Tensor input_tensor (ov::element::f16 , ov::Shape {1 ,1 ,1 ,context_size }); 1151 std::memcpy (input_tensor .data (),padded_data .data (),padded_data .size ()* sizeof (ggml_fp16_t )); 1152return input_tensor ; 1153 } 1154 1155 std::vector < float > padded_data = pad_input < float > (ggml_tensor ,1 ,context_size ,- INFINITY ); 1156 ov::Tensor input_tensor (ov::element::f32 , ov::Shape {1 ,1 ,1 ,context_size }); 1157auto * data_ptr = input_tensor .data < float > (); 1158 std::copy (padded_data .begin (),padded_data .begin ()+ context_size ,data_ptr ); 1159return input_tensor ; 1160 } 1161 1162return get_ov_input_tensor (ggml_decoder ,param_name ); 1163} 1164 1165ov::Tensor get_ov_input_tensor_static_prefill (std::shared_ptr < GgmlOvDecoder > ggml_decoder , 1166const std::string & param_name , 1167int chunk_index ) { 1168// NPU prompt processing stage 1169const size_t input_len = ggml_decoder -> get_input_len (); 1170const size_t chunk_size = ggml_decoder -> m_prefill_chunk_size ; 1171const size_t chunk_valid_size = std::min (chunk_size ,input_len - chunk_index * chunk_size ); 1172const size_t chunk_pad_size = chunk_size - chunk_valid_size ; 1173 1174if (param_name == "chunk_valid_len" ) { 1175 ov::Tensor input_tensor (ov::element::i64 , ov::Shape {1 }); 1176* input_tensor .data < int64_t > ()= (int64_t )chunk_valid_size ; 1177return input_tensor ; 1178 } 1179if (chunk_index > 0 && param_name == "cache_rs_reset_len" ) { 1180// The recurrent-state clear belongs to the start of the sequence. Re-applying it on every 1181// chunk would wipe the state accumulated by the preceding chunks, so disable it (a zero 1182// length makes scale.cpp's keep-mask select every slot) after the first chunk. 1183 ov::Tensor input_tensor (ov::element::i64 , ov::Shape {1 }); 1184* input_tensor .data < int64_t > ()= 0 ; 1185return input_tensor ; 1186 } 1187if (ggml_decoder -> get_model_extra_inputs ().count (param_name )) { 1188return get_ov_input_tensor (ggml_decoder ,param_name ); 1189 } 1190const auto * ggml_tensor = ggml_decoder -> get_input_ggml_tensor (param_name ); 1191const auto * op = ggml_decoder -> get_tensor_used_op (ggml_tensor ); 1192 1193if (GgmlOvDecoder ::is_inp_pos (ggml_tensor ,op )&& GgmlOvDecoder ::get_inp_pos_n_planes (op )> 1 ) { 1194// IMROPE: inp_pos stacks n_planes (t/h/w/e) position planes, each of length 1195// input_len; pad every plane independently so they stay aligned to chunk_size. 1196const int n_planes = GgmlOvDecoder ::get_inp_pos_n_planes (op ); 1197const size_t element_size = ggml_type_size (ggml_tensor -> type ); 1198 ov::Shape input_shape = {1 ,1 ,1 , (size_t )n_planes * chunk_size }; 1199 ov::Tensor input_tensor (ggml_decoder -> get_ov_type (ggml_tensor ),input_shape ); 1200for (int p = 0 ;p < n_planes ;p ++ ) { 1201const char * src = 1202 (const char * )ggml_tensor -> data + (p * input_len + chunk_index * chunk_size )* element_size ; 1203char * dst = (char * )input_tensor .data ()+ p * chunk_size * element_size ; 1204 std::memcpy (dst ,src ,chunk_valid_size * element_size ); 1205if (chunk_pad_size > 0 ) { 1206if (ggml_tensor -> type == GGML_TYPE_I32 ) { 1207int32_t last_value = * ((const int32_t * )src + chunk_valid_size - 1 ); 1208int32_t * out = (int32_t * )dst ; 1209 std::fill (out + chunk_valid_size ,out + chunk_size ,last_value + 1 ); 1210 }else if (ggml_tensor -> type == GGML_TYPE_I64 ) { 1211int64_t last_value = * ((const int64_t * )src + chunk_valid_size - 1 ); 1212int64_t * out = (int64_t * )dst ; 1213 std::fill (out + chunk_valid_size ,out + chunk_size ,last_value + 1 ); 1214 }else { 1215throw std::runtime_error ("Unexpected tensor type for " + param_name ); 1216 } 1217 } 1218 } 1219return input_tensor ; 1220 } 1221 1222if (GgmlOvDecoder ::is_inp_tok (ggml_tensor ,op )|| GgmlOvDecoder ::is_inp_pos (ggml_tensor ,op )|| 1223GgmlOvDecoder ::is_kv_idx (ggml_tensor ,op )) { 1224 ov::Shape input_shape = {1 ,1 ,1 ,chunk_size }; 1225 ov::Tensor input_tensor (ggml_decoder -> get_ov_type (ggml_tensor ),input_shape ); 1226// copy the chunk_index-th chunk from ggml_tensor 1227size_t element_size = ggml_type_size (ggml_tensor -> type ); 1228void * input_data = (char * )ggml_tensor -> data + chunk_index * chunk_size * element_size ; 1229 std::memcpy (input_tensor .data (),input_data ,chunk_valid_size * element_size ); 1230// pad the rest with last_value + 1, so that kv's of padded positions are inserted 1231// to the next row after the valids row in the kvcache 1232if (chunk_pad_size > 0 ) { 1233if (ggml_tensor -> type == GGML_TYPE_I32 ) { 1234int32_t last_value = 1235* ((int32_t * )ggml_tensor -> data + (chunk_index * chunk_size + chunk_valid_size - 1 )); 1236int32_t * output_data = input_tensor .data < int32_t > (); 1237 std::fill (output_data + chunk_valid_size ,output_data + chunk_size ,last_value + 1 ); 1238 }else if (ggml_tensor -> type == GGML_TYPE_I64 ) { 1239int64_t last_value = 1240* ((int64_t * )ggml_tensor -> data + (chunk_index * chunk_size + chunk_valid_size - 1 )); 1241int64_t * output_data = input_tensor .data < int64_t > (); 1242 std::fill (output_data + chunk_valid_size ,output_data + chunk_size ,last_value + 1 ); 1243 }else { 1244throw std::runtime_error ("Unexpected tensor type for " + param_name ); 1245 } 1246 } 1247return input_tensor ; 1248 } 1249 1250if (GgmlOvDecoder ::is_output_idx (ggml_tensor ,op )) { 1251size_t output_len = ggml_decoder -> get_compute_params ().output_len ; 1252 ov::Shape input_shape = {1 ,1 ,1 ,output_len }; 1253 ov::Tensor input_tensor (ggml_decoder -> get_ov_type (ggml_tensor ),input_shape ); 1254if (ggml_tensor -> ne [0 ]== 0 ) { 1255* input_tensor .data < int32_t > ()= 0 ; 1256 }else { 1257auto * data_addr = input_tensor .data < int32_t > (); 1258for (size_t i = 0 ;i < output_len ;i ++ ) { 1259data_addr [i ]= ((int32_t * )ggml_tensor -> data )[i ] %chunk_size ; 1260 } 1261 } 1262return input_tensor ; 1263 } 1264 1265if (GgmlOvDecoder ::is_inp_mask (ggml_tensor ,op )) { 1266size_t cols = ggml_tensor -> ne [0 ]; 1267size_t rows = ggml_tensor -> ne [1 ]; 1268size_t chunk_valid_rows = std::min (chunk_size ,rows - chunk_index * chunk_size ); 1269size_t context_size = ggml_decoder -> get_ctx_size (); 1270if (ggml_tensor -> type == GGML_TYPE_F16 ) { 1271const auto * ggml_data = 1272static_cast < const ggml_fp16_t *> (ggml_tensor -> data )+ chunk_index * chunk_size * cols ; 1273 std::vector < ggml_fp16_t > padded_data = pad_input < ggml_fp16_t > (ggml_data ,chunk_valid_rows ,cols ,chunk_size , 1274context_size ,GGML_FP32_TO_FP16 (- INFINITY )); 1275set_zero_diagonal (padded_data ,chunk_size ,context_size ,GGML_FP32_TO_FP16 (0.0f )); 1276 ov::Tensor input_tensor (ov::element::f16 , ov::Shape {1 ,1 ,chunk_size ,context_size }); 1277 std::memcpy (input_tensor .data (),padded_data .data (),padded_data .size ()* sizeof (ggml_fp16_t )); 1278return input_tensor ; 1279 } 1280 1281const auto * ggml_data = static_cast < const float *> (ggml_tensor -> data )+ chunk_index * chunk_size * cols ; 1282 std::vector < float > padded_data = 1283pad_input < float > (ggml_data ,chunk_valid_rows ,cols ,chunk_size ,context_size ,- INFINITY ); 1284set_zero_diagonal (padded_data ,chunk_size ,context_size ); 1285 ov::Tensor input_tensor (ov::element::f32 , ov::Shape {1 ,1 ,chunk_size ,context_size }); 1286auto * data_ptr = input_tensor .data < float > (); 1287 std::copy (padded_data .begin (),padded_data .begin ()+ chunk_size * context_size ,data_ptr ); 1288return input_tensor ; 1289 } 1290 1291return get_ov_input_tensor (ggml_decoder ,param_name ); 1292} 1293 1294size_t checksum (const void * data ,size_t size ) { 1295const uint8_t * bytes = static_cast < const uint8_t *> (data ); 1296size_t sum = 0 ; 1297for (size_t i = 0 ;i < size ;++ i ) { 1298sum += (uint8_t )i ; 1299sum += bytes [i ]; 1300 } 1301return sum ; 1302} 1303 1304bool save_ggml_tensor_data_to_txt (const ggml_tensor * tensor ,const std::string & file_path ) { 1305if (tensor == nullptr || tensor -> data == nullptr ) { 1306return false; 1307 } 1308 1309 std::ofstream out (file_path ); 1310if (!out .is_open ()) { 1311return false; 1312 } 1313 1314const size_t n = ggml_nelements (tensor ); 1315out <<"name: " <<tensor -> name <<", type: " <<ggml_type_name (tensor -> type ) <<", shape: [" <<tensor -> ne [0 ] 1316 <<", " <<tensor -> ne [1 ] <<", " <<tensor -> ne [2 ] <<", " <<tensor -> ne [3 ] <<"]" <<", elements: " <<n 1317 <<", data:" <<'\n' ; 1318 1319switch (tensor -> type ) { 1320case GGML_TYPE_F32 : { 1321const auto * data = static_cast < const float *> (tensor -> data ); 1322for (size_t i = 0 ;i < n ;++ i ) { 1323out <<data [i ] <<'\n' ; 1324 } 1325break ; 1326 } 1327case GGML_TYPE_F16 : { 1328const auto * data = static_cast < const ggml_fp16_t *> (tensor -> data ); 1329for (size_t i = 0 ;i < n ;++ i ) { 1330out <<ggml_fp16_to_fp32 (data [i ]) <<'\n' ; 1331 } 1332break ; 1333 } 1334case GGML_TYPE_BF16 : { 1335const auto * data = static_cast < const ggml_bf16_t *> (tensor -> data ); 1336for (size_t i = 0 ;i < n ;++ i ) { 1337out <<ggml_bf16_to_fp32 (data [i ]) <<'\n' ; 1338 } 1339break ; 1340 } 1341case GGML_TYPE_I32 : { 1342const auto * data = static_cast < const int32_t *> (tensor -> data ); 1343for (size_t i = 0 ;i < n ;++ i ) { 1344out <<data [i ] <<'\n' ; 1345 } 1346break ; 1347 } 1348case GGML_TYPE_I64 : { 1349const auto * data = static_cast < const int64_t *> (tensor -> data ); 1350for (size_t i = 0 ;i < n ;++ i ) { 1351out <<data [i ] <<'\n' ; 1352 } 1353break ; 1354 } 1355default : 1356out <<"unsupported tensor type for text dump" <<'\n' ; 1357return false; 1358 } 1359 1360return true; 1361} 1362 1363void print_input_tensor_info (const std::string & name ,const ov::Tensor & tensor ) { 1364 std::cout <<"Input name: " <<name <<", Input shape: " <<tensor .get_shape () <<", Address: " <<tensor .data () 1365 << std::endl ; 1366switch (tensor .get_element_type ()) { 1367case ov::element::f32 : { 1368if (name .find ("self_kq_mask" )== std::string::npos && name .find ("KQ_mask" )== std::string::npos ) { 1369 std::cout <<* (tensor .data < float > ()) << std::endl ; 1370 }else { 1371size_t rows = tensor .get_shape ()[2 ]; 1372size_t cols = tensor .get_shape ()[3 ]; 1373auto * data = tensor .data < float > (); 1374for (size_t i = 0 ;i < rows ;++ i ) { 1375for (size_t j = 0 ;j < cols ;++ j ) { 1376float val = data [i * cols + j ]; 1377if (std::isinf (val )&& val < 0 ) { 1378 std::cout << std::setw (5 ) <<"-inf" ; 1379 }else { 1380 std::cout << std::setw (5 ) <<val ; 1381 } 1382 } 1383 std::cout << std::endl ; 1384 } 1385 } 1386 1387break ; 1388 } 1389case ov::element::f16 : 1390 std::cout <<* (tensor .data < ov::float16 > ()) << std::endl ; 1391break ; 1392case ov::element::i32 : 1393for (size_t i = 0 ;i < tensor .get_size ();++ i ) { 1394 std::cout <<tensor .data < int32_t > ()[i ] <<" " ; 1395 } 1396 std::cout << std::endl ; 1397break ; 1398case ov::element::i64 : 1399for (size_t i = 0 ;i < tensor .get_size ();++ i ) { 1400 std::cout <<tensor .data < int64_t > ()[i ] <<" " ; 1401 } 1402 std::cout << std::endl ; 1403break ; 1404default : 1405break ; 1406 } 1407} 1408 1409void print_output_tensor_info (const std::string & name ,const ov::Tensor & tensor ,const void * output_dst ) { 1410 std::cout <<"Output name: " <<name <<", Output shape: " <<tensor .get_shape () <<", Address: " <<output_dst 1411 << std::endl ; 1412 1413auto print_float_stats = [](const std::string & type_name ,size_t size ,auto get_value ) { 1414if (size == 0 ) { 1415return ; 1416 } 1417 1418float first = get_value (0 ); 1419float min = first ; 1420float max = first ; 1421double sum = first ; 1422 1423for (size_t i = 1 ;i < size ;++ i ) { 1424float v = get_value (i ); 1425if (v < min ) { 1426min = v ; 1427 } 1428if (v > max ) { 1429max = v ; 1430 } 1431sum += v ; 1432 } 1433double mean = sum /size ; 1434 1435 std::cout << std::right << std::setw (6 ) <<type_name << std::right << std::setw (12 ) <<"First" << std::setw (12 ) 1436 <<"Min" << std::setw (12 ) <<"Max" << std::setw (12 ) <<"Mean" << std::endl ; 1437 std::cout << std::right << std::setw (6 ) <<"" << std::right << std::setw (12 ) <<first << std::setw (12 ) <<min 1438 << std::setw (12 ) <<max << std::setw (12 ) <<mean << std::endl ; 1439 }; 1440 1441switch (tensor .get_element_type ()) { 1442case ov::element::f32 : { 1443const float * data = tensor .data < float > (); 1444size_t size = tensor .get_size (); 1445print_float_stats ("[f32]" ,size , [data ](size_t i ) {return data [i ]; }); 1446break ; 1447 } 1448case ov::element::f16 : { 1449const ov::float16 * data = tensor .data < ov::float16 > (); 1450size_t size = tensor .get_size (); 1451print_float_stats ("[f16]" ,size , [data ](size_t i ) {return static_cast < float > (data [i ]); }); 1452break ; 1453 } 1454default : 1455break ; 1456 } 1457} 1458 1459const ggml_tensor * get_inp_pos_tensor (ggml_cgraph * cgraph ) { 1460for (int i = 0 ;i < cgraph -> n_nodes ;++ i ) { 1461auto * op = cgraph -> nodes [i ]; 1462for (int j = 0 ;j < GGML_MAX_SRC ;++ j ) { 1463auto * src = op -> src [j ]; 1464if (src == nullptr ) { 1465break ; 1466 } 1467if (GgmlOvDecoder ::is_inp_pos (src ,op )) { 1468return src ; 1469 } 1470 } 1471 } 1472GGML_LOG_ERROR ("get_inp_pos_tensor: inp_pos not found in cgraph" ); 1473throw std::runtime_error ("get_inp_pos_tensor: inp_pos not found in cgraph" ); 1474} 1475 1476int64_t get_inp_pos_n_tokens (ggml_cgraph * cgraph ,const ggml_tensor * inp_pos ) { 1477// IMROPE stacks n_planes (t/h/w/e) position planes into inp_pos, so ne[0] is 1478// n_planes * n_tokens. Callers that need a token count must divide the planes out. 1479int n_planes = 1 ; 1480for (int i = 0 ;i < cgraph -> n_nodes ;++ i ) { 1481auto * op = cgraph -> nodes [i ]; 1482for (int j = 0 ;j < GGML_MAX_SRC ;++ j ) { 1483if (op -> src [j ]== inp_pos ) { 1484n_planes = GgmlOvDecoder ::get_inp_pos_n_planes (op ); 1485break ; 1486 } 1487 } 1488 } 1489return inp_pos -> ne [0 ] /n_planes ; 1490} 1491 1492bool get_is_prefill (ggml_cgraph * cgraph ,const ggml_tensor * inp_pos ) { 1493return get_inp_pos_n_tokens (cgraph ,inp_pos )> 1 ; 1494} 1495 1496#pragma GCC diagnostic pop