char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
9dcf84e5a
char/aliceai-t5-moe
1#include "arg.h" 2#include "common.h" 3#include "ggml-backend.h" 4#include "llama.h" 5 6#include "../src/llama-io.h" 7#include "../src/llama-memory.h" 8 9#include <algorithm> 10#include <clocale> 11#include <cmath> 12#include <cstdio> 13#include <limits> 14#include <set> 15#include <vector> 16 17static bool decode_tokens (llama_context * ctx ,const std::vector < llama_token > & tokens ,uint32_t count ) { 18llama_batch batch = llama_batch_init (count ,0 ,1 ); 19for (uint32_t pos = 0 ;pos < count ;++ pos ) { 20common_batch_add (batch ,tokens [pos ],pos , {0 },pos + 1 == count ); 21 } 22const bool ok = llama_decode (ctx ,batch )== 0 ; 23llama_batch_free (batch ); 24return ok ; 25} 26 27static bool decode_one (llama_context * ctx ,llama_token tok ,llama_pos pos ) { 28llama_batch batch = llama_batch_init (1 ,0 ,1 ); 29common_batch_add (batch ,tok ,pos , {0 }, true); 30const bool ok = llama_decode (ctx ,batch )== 0 ; 31llama_batch_free (batch ); 32return ok ; 33} 34 35struct cache_buffer_collector :llama_io_write_i { 36 std::set < ggml_backend_buffer_t > buffers ; 37size_t size = 0 ; 38 39void write (const void * ,size_t n )override { 40size += n ; 41 } 42 43void write_tensor (ggml_tensor * tensor ,size_t ,size_t n )override { 44buffers .insert (tensor -> buffer ); 45size += n ; 46 } 47 48size_t n_bytes ()override { 49return size ; 50 } 51}; 52 53static llama_context * init_ctx (llama_model * model ,llama_context_params cparams ,uint8_t fill ) { 54llama_context * ctx = llama_init_from_model (model ,cparams ); 55if (ctx == nullptr || fill == 0 ) { 56return ctx ; 57 } 58 59// Use a full ubatch so buffer discovery preserves prefill allocation sizes. 60const uint32_t n_tokens = llama_n_ubatch (ctx ); 61if (!decode_tokens (ctx , std::vector < llama_token > (n_tokens ,0 ),n_tokens )) { 62llama_free (ctx ); 63return nullptr ; 64 } 65llama_synchronize (ctx ); 66cache_buffer_collector collector ; 67llama_get_memory (ctx )-> state_write (collector ); 68llama_memory_clear (llama_get_memory (ctx ), true); 69if (collector .buffers .empty ()) { 70fprintf (stderr ,"%s : no cache buffers found\n" ,__func__ ); 71llama_free (ctx ); 72return nullptr ; 73 } 74for (auto * buffer :collector .buffers ) { 75ggml_backend_buffer_clear (buffer ,fill ); 76 } 77return ctx ; 78} 79 80static llama_context * make_ctx (const common_params & params ,llama_model * model ,uint8_t fill ) { 81auto cparams = common_context_params_to_llama (params ); 82cparams .n_seq_max = 1 ; 83cparams .n_rs_seq = 8 ; 84cparams .n_batch = std::max (cparams .n_batch , (uint32_t ) (cparams .n_rs_seq + 1 )); 85cparams .n_ubatch = std::max (cparams .n_ubatch , (uint32_t ) (cparams .n_rs_seq + 1 )); 86return init_ctx (model ,cparams ,fill ); 87} 88 89static float logit_diff (float a ,float b ) { 90return std::isfinite (a )&& std::isfinite (b ) ? std::fabs (a - b ) : std::numeric_limits < float > ::infinity (); 91} 92 93// Roll back multiple sequences, then replay them in a single batch whose 94// per-seq token count exceeds n_ubatch: each seq's replay spans several 95// ubatches while its rollback restore is still pending. Compared against a 96// reference context that never advanced past the rollback point and decodes 97// the identical replay batch. 98static bool test_multi_seq_split_replay (const common_params & params ,llama_model * model ,const int n_vocab ,uint8_t fill ) { 99constexpr uint32_t n_seqs = 2 ; 100constexpr uint32_t n_ubatch = 16 ; 101constexpr uint32_t n_prompt = 19 ; 102constexpr uint32_t n_rollback = 3 ; 103constexpr uint32_t n_replay = 40 ;// > n_ubatch so each seq spans multiple ubatches 104constexpr llama_pos p0 = n_prompt - n_rollback ; 105 106const auto make_ctx_multi = [& ]() { 107auto cparams = common_context_params_to_llama (params ); 108cparams .n_seq_max = n_seqs ; 109cparams .n_rs_seq = 8 ; 110cparams .n_ctx = 256 ; 111cparams .n_batch = 256 ; 112cparams .n_ubatch = n_ubatch ; 113cparams .kv_unified = false; 114return init_ctx (model ,cparams ,fill ); 115 }; 116 117llama_context * ctx_roll = make_ctx_multi (); 118llama_context * ctx_ref = make_ctx_multi (); 119if (ctx_roll == nullptr || ctx_ref == nullptr ) { 120fprintf (stderr ,"%s : failed to init multi-seq contexts\n" ,__func__ ); 121return false; 122 } 123 124const auto cleanup = [& ]() { 125llama_free (ctx_roll ); 126llama_free (ctx_ref ); 127 }; 128 129if (llama_n_rs_seq (ctx_roll )< n_rollback ) { 130fprintf (stderr ,"%s : skipping because n_rs_seq is too small\n" ,__func__ ); 131cleanup (); 132return true; 133 } 134 135const auto tok = [& ](uint32_t seq ,llama_pos pos ) { 136return (llama_token ) ((7 * (uint32_t )pos + 31 * seq + 1 ) % (uint32_t )n_vocab ); 137 }; 138 139bool ok = true; 140 141// both contexts decode the identical [0, p0) prefill; only ctx_roll decodes 142// the tail, which is then rolled back so its restore is pending at replay 143for (uint32_t s = 0 ;s < n_seqs && ok ;++ s ) { 144llama_batch batch = llama_batch_init (n_prompt ,0 ,1 ); 145for (llama_pos pos = 0 ;pos < (llama_pos )p0 ;++ pos ) { 146common_batch_add (batch ,tok (s ,pos ),pos , { (llama_seq_id )s }, false); 147 } 148ok = ok && llama_decode (ctx_roll ,batch )== 0 ; 149ok = ok && llama_decode (ctx_ref ,batch )== 0 ; 150 151common_batch_clear (batch ); 152for (llama_pos pos = p0 ;pos < (llama_pos )n_prompt ;++ pos ) { 153common_batch_add (batch ,tok (s ,pos ),pos , { (llama_seq_id )s }, false); 154 } 155ok = ok && llama_decode (ctx_roll ,batch )== 0 ; 156llama_batch_free (batch ); 157 158ok = ok && llama_memory_seq_rm (llama_get_memory (ctx_roll ), (llama_seq_id )s ,p0 ,-1 ); 159 160// a second partial removal while one is pending must be refused 161ok = ok && !llama_memory_seq_rm (llama_get_memory (ctx_roll ), (llama_seq_id )s ,p0 - 1 ,-1 ); 162 } 163if (!ok ) { 164fprintf (stderr ,"%s : multi-seq prefill/rollback failed\n" ,__func__ ); 165cleanup (); 166return false; 167 } 168 169llama_batch batch = llama_batch_init (n_seqs * n_replay ,0 ,1 ); 170for (uint32_t s = 0 ;s < n_seqs ;++ s ) { 171for (uint32_t i = 0 ;i < n_replay ;++ i ) { 172const llama_pos pos = p0 + (llama_pos )i ; 173common_batch_add (batch ,tok (s ,pos ),pos , { (llama_seq_id )s }, true); 174 } 175 } 176ok = llama_decode (ctx_roll ,batch )== 0 ; 177ok = ok && llama_decode (ctx_ref ,batch )== 0 ; 178llama_batch_free (batch ); 179if (!ok ) { 180fprintf (stderr ,"%s : multi-seq replay decode failed\n" ,__func__ ); 181cleanup (); 182return false; 183 } 184 185// identical ubatch shapes from bit-exact states: a correct implementation 186// matches bitwise, so eps only allows backend scheduling noise 187constexpr float eps = 1e-7f ; 188 189float diff_max = 0.0f ; 190uint32_t seq_first = 0 ; 191int32_t pos_first = -1 ; 192for (uint32_t i = 0 ;i < n_seqs * n_replay ;++ i ) { 193const float * l_roll = llama_get_logits_ith (ctx_roll ,i ); 194const float * l_ref = llama_get_logits_ith (ctx_ref ,i ); 195if (l_roll == nullptr || l_ref == nullptr ) { 196fprintf (stderr ,"%s : missing multi-seq logits at index %u\n" ,__func__ ,i ); 197cleanup (); 198return false; 199 } 200for (int t = 0 ;t < n_vocab ;++ t ) { 201const float diff = logit_diff (l_roll [t ],l_ref [t ]); 202if (diff > eps && pos_first < 0 ) { 203seq_first = i /n_replay ; 204pos_first = p0 + (int32_t ) (i %n_replay ); 205 } 206diff_max = std::max (diff_max ,diff ); 207 } 208 } 209 210if (diff_max > eps ) { 211fprintf (stderr ,"%s : multi-seq split replay logits mismatch (max diff %g, first at seq %u pos %d)\n" , 212__func__ , (double )diff_max ,seq_first ,pos_first ); 213cleanup (); 214return false; 215 } 216 217fprintf (stderr ,"%s : multi-seq split replay matched (max diff %g)\n" ,__func__ , (double )diff_max ); 218 219// seq-1-only decodes must be independent of seq 0's content: diverge seq 0 220// in ctx_ref only, then compare identical seq-1-only continuations bitwise 221constexpr uint32_t n_tail = 4 ; 222 223 { 224llama_batch batch_tail = llama_batch_init (n_tail ,0 ,1 ); 225for (uint32_t i = 0 ;i < n_tail ;++ i ) { 226const llama_pos pos = p0 + (llama_pos ) (n_replay + i ); 227common_batch_add (batch_tail ,tok (0 ,pos + 7 ),pos , {0 }, false); 228 } 229ok = llama_decode (ctx_ref ,batch_tail )== 0 ; 230llama_batch_free (batch_tail ); 231 } 232 233float diff_tail = 0.0f ; 234for (uint32_t i = 0 ;i < n_tail && ok ;++ i ) { 235const llama_pos pos = p0 + (llama_pos ) (n_replay + i ); 236llama_batch batch_one = llama_batch_init (1 ,0 ,1 ); 237common_batch_add (batch_one ,tok (1 ,pos ),pos , {1 }, true); 238ok = llama_decode (ctx_roll ,batch_one )== 0 ; 239ok = ok && llama_decode (ctx_ref ,batch_one )== 0 ; 240llama_batch_free (batch_one ); 241if (!ok ) { 242break ; 243 } 244 245const float * l_roll = llama_get_logits_ith (ctx_roll ,0 ); 246const float * l_ref = llama_get_logits_ith (ctx_ref ,0 ); 247ok = l_roll != nullptr && l_ref != nullptr ; 248for (int t = 0 ;ok && t < n_vocab ;++ t ) { 249diff_tail = std::max (diff_tail ,logit_diff (l_roll [t ],l_ref [t ])); 250 } 251 } 252 253if (!ok || diff_tail > eps ) { 254fprintf (stderr ,"%s : seq-1-only decode leaked seq 0 state (ok=%d, max diff %g)\n" , 255__func__ ,ok ?1 :0 , (double )diff_tail ); 256cleanup (); 257return false; 258 } 259 260fprintf (stderr ,"%s : seq-1-only decode independent of seq 0 (max diff %g)\n" ,__func__ , (double )diff_tail ); 261cleanup (); 262return true; 263} 264 265static int test_rollback (const common_params & params ,llama_model * model ,uint8_t fill ) { 266const llama_vocab * vocab = llama_model_get_vocab (model ); 267const int n_vocab = llama_vocab_n_tokens (vocab ); 268 269llama_context * ctx_src = make_ctx (params ,model ,fill ); 270llama_context * ctx_dst = make_ctx (params ,model ,fill ); 271if (ctx_src == nullptr || ctx_dst == nullptr ) { 272fprintf (stderr ,"%s : failed to init contexts\n" ,__func__ ); 273return 1 ; 274 } 275 276if (llama_n_rs_seq (ctx_src )== 0 ) { 277fprintf (stderr ,"%s : skipping because n_rs_seq is disabled\n" ,__func__ ); 278llama_free (ctx_src ); 279llama_free (ctx_dst ); 280return 0 ; 281 } 282 283 std::vector < llama_token > tokens ; 284if (llama_vocab_type (vocab )== LLAMA_VOCAB_TYPE_NONE ) { 285tokens = {1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 }; 286 }else { 287tokens = common_tokenize (ctx_src ,"The quick brown fox jumps over the lazy dog" , true); 288 } 289const uint32_t n_rs_seq = llama_n_rs_seq (ctx_src ); 290constexpr uint32_t n_rollback = 3 ; 291if (n_rs_seq < n_rollback ) { 292fprintf (stderr ,"%s : skipping because n_rs_seq is too small\n" ,__func__ ); 293llama_free (ctx_src ); 294llama_free (ctx_dst ); 295return 0 ; 296 } 297if (tokens .empty ()) { 298fprintf (stderr ,"%s : not enough prompt tokens\n" ,__func__ ); 299return 1 ; 300 } 301tokens .resize (n_rs_seq + 1 ,tokens .back ()); 302 303const uint32_t n_tokens = tokens .size (); 304const llama_pos rollback_pos = (llama_pos )n_tokens - n_rollback ; 305 306// Decode the full prompt on the source, then roll back three positions. 307// Replaying them crosses DSV4's ratio-4 compressor boundary. 308// Rollback leaves the recurrent memory in a snapshot state (rs_idx != 0). 309if (!decode_tokens (ctx_src ,tokens ,n_tokens )) { 310fprintf (stderr ,"%s : failed to decode prompt\n" ,__func__ ); 311return 1 ; 312 } 313if (!llama_memory_seq_rm (llama_get_memory (ctx_src ),0 ,rollback_pos ,-1 )) { 314fprintf (stderr ,"%s : rollback failed\n" ,__func__ ); 315return 1 ; 316 } 317 318// Save the rolled-back state and restore it into a fresh context. 319common_prompt_checkpoint ckpt ; 320ckpt .update_tgt (ctx_src ,0 ,0 ); 321ckpt .load_tgt (ctx_dst ,0 ,0 ); 322 323constexpr float eps = 1e-5f ; 324 std::vector < std::vector < float >> logits_src_replay (n_rollback ); 325const auto replay_and_compare = [& ](const char * mode ) { 326for (uint32_t i = 0 ;i < n_rollback ;++ i ) { 327const llama_pos pos = rollback_pos + i ; 328if (!decode_one (ctx_src ,tokens [pos ],pos )|| 329 !decode_one (ctx_dst ,tokens [pos ],pos )) { 330fprintf (stderr ,"%s : %s replay failed at position %d\n" ,__func__ ,mode ,pos ); 331return false; 332 } 333 334const float * logits_src = llama_get_logits_ith (ctx_src ,0 ); 335const float * logits_dst = llama_get_logits_ith (ctx_dst ,0 ); 336if (logits_src == nullptr || logits_dst == nullptr ) { 337fprintf (stderr ,"%s : missing %s logits at position %d\n" ,__func__ ,mode ,pos ); 338return false; 339 } 340 341logits_src_replay [i ].assign (logits_src ,logits_src + n_vocab ); 342for (int token = 0 ;token < n_vocab ;++ token ) { 343if (logit_diff (logits_src [token ],logits_dst [token ])> eps ) { 344fprintf (stderr ,"%s : %s logits mismatch at position %d, token %d (%g != %g)\n" , 345__func__ ,mode ,pos ,token , (double )logits_src [token ], (double )logits_dst [token ]); 346return false; 347 } 348 } 349 } 350return true; 351 }; 352if (!replay_and_compare ("full" )) { 353return 1 ; 354 } 355 356if (!llama_memory_seq_rm (llama_get_memory (ctx_src ),0 ,rollback_pos ,-1 )|| 357 !llama_memory_seq_rm (llama_get_memory (ctx_dst ),0 ,rollback_pos ,-1 )) { 358fprintf (stderr ,"%s : partial rollback failed\n" ,__func__ ); 359return 1 ; 360 } 361 362constexpr llama_state_seq_flags partial_flags = LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY ; 363common_prompt_checkpoint ckpt_partial ; 364ckpt_partial .update_tgt (ctx_src ,0 ,partial_flags ); 365ckpt_partial .load_tgt (ctx_dst ,0 ,partial_flags ); 366 367if (!replay_and_compare ("partial" )) { 368return 1 ; 369 } 370 371// Repeat the load into a context that already has its own rollback state: 372// groups 1..n_rs_seq hold a different prompt's history, and rs_idx[0] is 373// non-zero at load time. The restore must wipe that state and still match. 374llama_context * ctx_dirty = make_ctx (params ,model ,fill ); 375if (ctx_dirty == nullptr ) { 376fprintf (stderr ,"%s : failed to init dirty ctx\n" ,__func__ ); 377return 1 ; 378 } 379 380 std::vector < llama_token > noise = tokens ; 381for (auto & t :noise ) { 382t = (t + 1 ) %n_vocab ; 383if (t < 0 ) { 384t = 0 ; 385 } 386 } 387if (!decode_tokens (ctx_dirty ,noise ,n_tokens )) { 388fprintf (stderr ,"%s : dirty prompt decode failed\n" ,__func__ ); 389return 1 ; 390 } 391if (!llama_memory_seq_rm (llama_get_memory (ctx_dirty ),0 ,rollback_pos ,-1 )) { 392fprintf (stderr ,"%s : dirty rollback failed\n" ,__func__ ); 393return 1 ; 394 } 395 396ckpt .load_tgt (ctx_dirty ,0 ,0 ); 397 398for (uint32_t i = 0 ;i < n_rollback ;++ i ) { 399const llama_pos pos = rollback_pos + i ; 400if (!decode_one (ctx_dirty ,tokens [pos ],pos )) { 401fprintf (stderr ,"%s : dirty replay failed at position %d\n" ,__func__ ,pos ); 402return 1 ; 403 } 404 405const float * logits_dirty = llama_get_logits_ith (ctx_dirty ,0 ); 406if (logits_dirty == nullptr ) { 407fprintf (stderr ,"%s : missing dirty logits at position %d\n" ,__func__ ,pos ); 408return 1 ; 409 } 410 411for (int token = 0 ;token < n_vocab ;++ token ) { 412if (logit_diff (logits_src_replay [i ][token ],logits_dirty [token ])> eps ) { 413fprintf (stderr ,"%s : dirty-ctx logits mismatch at position %d, token %d (%g != %g)\n" , 414__func__ ,pos ,token , (double )logits_src_replay [i ][token ], (double )logits_dirty [token ]); 415return 1 ; 416 } 417 } 418 } 419 420fprintf (stderr ,"%s : recurrent rollback checkpoint restored successfully\n" ,__func__ ); 421llama_free (ctx_src ); 422llama_free (ctx_dst ); 423llama_free (ctx_dirty ); 424 425if (!test_multi_seq_split_replay (params ,model ,n_vocab ,fill )) { 426return 1 ; 427 } 428 429return 0 ; 430} 431 432int main (int argc ,char ** argv ) { 433 std::setlocale (LC_NUMERIC ,"C" ); 434 435common_params params ; 436params .sampling .seed = 1234 ; 437params .n_predict = 1 ; 438 439common_init (); 440 441if (!common_params_parse (argc ,argv ,params ,LLAMA_EXAMPLE_COMMON )) { 442return 1 ; 443 } 444 445ggml_backend_load_all (); 446 447common_init_result_ptr llama_init = common_init_from_params (params ); 448llama_model * model = llama_init -> model (); 449if (model == nullptr ) { 450fprintf (stderr ,"%s : failed to init model\n" ,__func__ ); 451return 1 ; 452 } 453 454if (!llama_model_is_recurrent (model )&& !llama_model_is_hybrid (model )) { 455fprintf (stderr ,"%s : skipping for non-recurrent model\n" ,__func__ ); 456return 0 ; 457 } 458 459for (uint8_t fill : {0 ,0x3e }) { 460fprintf (stderr ,"%s : testing with cache fill 0x%02x\n" ,__func__ ,fill ); 461if (test_rollback (params ,model ,fill )!= 0 ) { 462return 1 ; 463 } 464 } 465 466return 0 ; 467}