char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
aee56b3ab
char/aliceai-t5-moe
1#include "model-cache.h" 2 3#include "ggml-backend-impl.h" 4#include "ggml-backend.h" 5#include "ggml-impl.h" 6#include "ggml-openvino-extra.h" 7 8#include <cerrno> 9#include <cstdio> 10#include <cstring> 11#include <fstream> 12#include <openvino/core/version.hpp> 13#include <string> 14#include <sys/stat.h> 15#include <sys/types.h> 16#include <vector> 17 18#if defined(_WIN32 ) 19# include <direct.h> 20#endif 21 22namespace { 23 24// 64-bit FNV-1a, the mixing primitive for all fingerprints here. 25inline uint64_t fnv1a (uint64_t h ,const void * data ,size_t n ) { 26const uint8_t * p = static_cast < const uint8_t *> (data ); 27for (size_t i = 0 ;i < n ;++ i ) { 28h ^=p [i ]; 29h *=0x100000001b3ull ; 30 } 31return h ; 32} 33 34inline uint64_t fnv1a_u64 (uint64_t h ,uint64_t v ) { 35return fnv1a (h ,& v ,sizeof (v )); 36} 37 38constexpr uint64_t FNV_OFFSET = 0xcbf29ce484222325ull ; 39 40// Bytes sampled from each end of a weight tensor for the sampled hash. The whole 41// model is never hashed (that would cost seconds every run); instead we sample a 42// bounded window from the head and tail of each weight's bytes. The manifest 43// re-verify (same sample) guards the residual collision risk. 44constexpr size_t WEIGHT_SAMPLE_BYTES = 4096 ; 45 46// Is this src a model weight, mirroring create_weight_nodes()'s selection: 47// non-view tensor whose buffer is USAGE_WEIGHTS or whose type is quantized. 48bool is_weight_src (const ggml_tensor * src ) { 49if (src == nullptr || src -> view_src != nullptr || src -> buffer == nullptr ) { 50return false; 51 } 52return src -> buffer -> usage == GGML_BACKEND_BUFFER_USAGE_WEIGHTS || ggml_is_quantized (src -> type ); 53} 54 55// Per-weight sampled fingerprint: identity (name/shape/type) + a bounded byte 56// sample. Returns FNV offset basis if data is unavailable (kept deterministic). 57uint64_t weight_fingerprint (const ggml_tensor * t ) { 58uint64_t h = FNV_OFFSET ; 59h = fnv1a (h ,t -> name ,strlen (t -> name )); 60for (int i = 0 ;i < GGML_MAX_DIMS ;++ i ) { 61h = fnv1a_u64 (h ,static_cast < uint64_t > (t -> ne [i ])); 62 } 63h = fnv1a_u64 (h ,static_cast < uint64_t > (t -> type )); 64const size_t nbytes = ggml_nbytes (t ); 65h = fnv1a_u64 (h ,nbytes ); 66if (t -> data != nullptr && nbytes > 0 ) { 67const size_t head = nbytes < WEIGHT_SAMPLE_BYTES ?nbytes :WEIGHT_SAMPLE_BYTES ; 68h = fnv1a (h ,t -> data ,head ); 69if (nbytes > WEIGHT_SAMPLE_BYTES ) { 70const size_t tail = nbytes < 2 * WEIGHT_SAMPLE_BYTES ?nbytes - WEIGHT_SAMPLE_BYTES :WEIGHT_SAMPLE_BYTES ; 71h = fnv1a (h ,static_cast < const uint8_t *> (t -> data )+ (nbytes - tail ),tail ); 72 } 73 } 74return h ; 75} 76 77// Walk the cgraph and invoke fn(weight_tensor) for each distinct weight, in node 78// order. De-duplicates by tensor pointer so a weight used by several nodes is 79// fingerprinted once, deterministically. 80template < typename F > 81void for_each_weight (const ggml_cgraph * cgraph ,F && fn ) { 82 std::vector < const ggml_tensor *> seen ; 83for (int i = 0 ;i < cgraph -> n_nodes ;++ i ) { 84const ggml_tensor * node = cgraph -> nodes [i ]; 85for (int s = 0 ;s < GGML_MAX_SRC ;++ s ) { 86const ggml_tensor * src = node -> src [s ]; 87if (!is_weight_src (src )) { 88continue ; 89 } 90bool dup = false; 91for (const auto * p :seen ) { 92if (p == src ) { 93dup = true; 94break ; 95 } 96 } 97if (dup ) { 98continue ; 99 } 100seen .push_back (src ); 101fn (src ); 102 } 103 } 104} 105 106std::string ov_version_string () { 107const ov::Version v = ov::get_openvino_version (); 108return std::string (v .buildNumber ?v .buildNumber :"unknown" ); 109} 110 111std::string hex64 (uint64_t v ) { 112char buf [17 ]; 113snprintf (buf ,sizeof (buf ),"%016llx" ,static_cast < unsigned long long > (v )); 114return std::string (buf ); 115} 116 117// Portable mkdir for a single path component. Returns true if the directory 118// exists after the call (created now or already present). 119bool make_dir (const std::string & path ) { 120#if defined(_WIN32 ) 121int rc = _mkdir (path .c_str ()); 122#else 123int rc = ::mkdir (path .c_str (),0755 ); 124#endif 125if (rc == 0 || errno == EEXIST ) { 126return true; 127 } 128return false; 129} 130 131// Create `path` and any missing parents (like `mkdir -p`). Best-effort: 132// returns true only if the full directory exists afterwards. 133bool make_dirs (const std::string & path ) { 134if (path .empty ()) { 135return false; 136 } 137 std::string acc ; 138for (size_t i = 0 ;i < path .size ();++ i ) { 139const char c = path [i ]; 140acc .push_back (c ); 141const bool sep = (c == '/' 142#if defined(_WIN32 ) 143|| c == '\\' 144#endif 145 ); 146// Create each intermediate component (skip a leading "/" root). 147if (sep && acc .size ()> 1 ) { 148 std::string component = acc .substr (0 ,acc .size ()- 1 ); 149if (!make_dir (component )) { 150return false; 151 } 152 } 153 } 154return make_dir (path ); 155} 156 157}// namespace 158 159std::string ggml_openvino_model_cache_dir () { 160const char * dir = ggml_openvino_getenv_str ("GGML_OPENVINO_COMPILED_MODEL_CACHE_DIR" ); 161if (!dir || strlen (dir )== 0 ) { 162return std::string (); 163 } 164 std::string path (dir ); 165// Create the cache directory (and parents) on first use so callers don't 166// have to pre-create it; a missing dir would otherwise silently disable the 167// cache (manifest/blob writes fail with no directory to write into). 168if (!make_dirs (path )) { 169GGML_LOG_WARN ("ggml-openvino: could not create model cache dir '%s' (errno=%d); caching disabled\n" , 170path .c_str (),errno ); 171return std::string (); 172 } 173return path ; 174} 175 176uint64_t ggml_openvino_model_fingerprint (const ggml_cgraph * cgraph , 177const std::string & device , 178bool fa , 179const int32_t * rope_params , 180int rope_len , 181uint64_t extra_cfg ) { 182uint64_t h = FNV_OFFSET ; 183 184// Topology: node count + each node's op and name (cheap, and distinguishes 185// graphs that share weights but differ structurally). 186h = fnv1a_u64 (h ,static_cast < uint64_t > (cgraph -> n_nodes )); 187for (int i = 0 ;i < cgraph -> n_nodes ;++ i ) { 188const ggml_tensor * node = cgraph -> nodes [i ]; 189h = fnv1a_u64 (h ,static_cast < uint64_t > (node -> op )); 190h = fnv1a (h ,node -> name ,strlen (node -> name )); 191 } 192 193// Weights: the model identity. 194for_each_weight (cgraph , [& ](const ggml_tensor * t ) {h = fnv1a_u64 (h ,weight_fingerprint (t )); }); 195 196// Config that changes the produced blob. 197h = fnv1a (h ,device .data (),device .size ()); 198h = fnv1a_u64 (h ,fa ?1u :0u ); 199if (rope_params && rope_len > 0 ) { 200h = fnv1a (h ,rope_params ,sizeof (int32_t )* static_cast < size_t > (rope_len )); 201 } 202h = fnv1a_u64 (h ,extra_cfg ); 203const std::string ver = ov_version_string (); 204h = fnv1a (h ,ver .data (),ver .size ()); 205 206return h ; 207} 208 209std::string ggml_openvino_model_cache_blob_path (const std::string & dir ,uint64_t fingerprint ) { 210return dir + "/" + hex64 (fingerprint )+ ".blob" ; 211} 212 213std::string ggml_openvino_model_cache_manifest_path (const std::string & dir ,uint64_t fingerprint ) { 214return dir + "/" + hex64 (fingerprint )+ ".manifest" ; 215} 216 217bool ggml_openvino_model_cache_write_manifest (const std::string & path , 218const ggml_cgraph * cgraph , 219uint64_t fingerprint ) { 220 std::ofstream f (path , std::ios::trunc ); 221if (!f .is_open ()) { 222return false; 223 } 224f <<"fingerprint " <<hex64 (fingerprint ) <<"\n" ; 225f <<"ov_version " <<ov_version_string () <<"\n" ; 226for_each_weight (cgraph , [& ](const ggml_tensor * t ) { 227f <<t -> name <<" " <<t -> ne [0 ] <<" " <<t -> ne [1 ] <<" " <<t -> ne [2 ] <<" " <<t -> ne [3 ] <<" " 228 <<static_cast < int > (t -> type ) <<" " <<hex64 (weight_fingerprint (t )) <<"\n" ; 229 }); 230return f .good (); 231} 232 233bool ggml_openvino_model_cache_verify_manifest (const std::string & path , 234const ggml_cgraph * cgraph , 235uint64_t fingerprint ) { 236 std::ifstream f (path ); 237if (!f .is_open ()) { 238return false; 239 } 240 std::string tag ,val ; 241// header: fingerprint 242if (!(f >>tag >>val )|| tag != "fingerprint" || val != hex64 (fingerprint )) { 243return false; 244 } 245// header: ov_version 246if (!(f >>tag >>val )|| tag != "ov_version" || val != ov_version_string ()) { 247return false; 248 } 249 250// Build the expected per-weight lines from the live cgraph, then require an 251// exact match (same set, same order) against the manifest. 252 std::vector < std::string > expected ; 253for_each_weight (cgraph , [& ](const ggml_tensor * t ) { 254expected .push_back (std::string (t -> name )+ " " + std::to_string (t -> ne [0 ])+ " " + std::to_string (t -> ne [1 ])+ 255" " + std::to_string (t -> ne [2 ])+ " " + std::to_string (t -> ne [3 ])+ " " + 256 std::to_string (static_cast < int > (t -> type ))+ " " + hex64 (weight_fingerprint (t ))); 257 }); 258 259size_t idx = 0 ; 260 std::string line ; 261 std::getline (f ,line );// consume rest of ov_version line 262while (std::getline (f ,line )) { 263if (line .empty ()) { 264continue ; 265 } 266if (idx >=expected .size ()|| line != expected [idx ]) { 267return false; 268 } 269++ idx ; 270 } 271return idx == expected .size (); 272}