char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
947fd9bb2
char/aliceai-t5-moe
1#include "server-task.h" 2#include "server-queue.h" 3 4#include "log.h" 5 6#include <algorithm> 7#include <chrono> 8#include <thread> 9 10#define QUE_INF (fmt , ...) LOG_INF("que %12.*s: " fmt, 12, __func__, __VA_ARGS__) 11#define QUE_WRN (fmt , ...) LOG_WRN("que %12.*s: " fmt, 12, __func__, __VA_ARGS__) 12#define QUE_ERR (fmt , ...) LOG_ERR("que %12.*s: " fmt, 12, __func__, __VA_ARGS__) 13#define QUE_DBG (fmt , ...) LOG_DBG("que %12.*s: " fmt, 12, __func__, __VA_ARGS__) 14 15#define RES_INF (fmt , ...) LOG_INF("res %12.*s: " fmt, 12, __func__, __VA_ARGS__) 16#define RES_WRN (fmt , ...) LOG_WRN("res %12.*s: " fmt, 12, __func__, __VA_ARGS__) 17#define RES_ERR (fmt , ...) LOG_ERR("res %12.*s: " fmt, 12, __func__, __VA_ARGS__) 18#define RES_DBG (fmt , ...) LOG_DBG("res %12.*s: " fmt, 12, __func__, __VA_ARGS__) 19 20// 21// server_queue 22// 23 24static bool task_resets_idle_timer (server_task_type type ) { 25return type != SERVER_TASK_TYPE_METRICS ; 26} 27 28int server_queue::post (server_task && task ,bool front ) { 29 std::unique_lock < std::mutex > lock (mutex_tasks ); 30GGML_ASSERT (task .id != -1 ); 31// if this is cancel task make sure to clean up pending tasks 32if (task .type == SERVER_TASK_TYPE_CANCEL ) { 33cleanup_pending_task (task .id_target ); 34 } 35const int task_id = task .id ; 36const bool reset_timer = task_resets_idle_timer (task .type ); 37QUE_DBG ("new task, id = %d, front = %d\n" ,task_id ,front ); 38if (front ) { 39queue_tasks .push_front (std::move (task )); 40 }else { 41queue_tasks .push_back (std::move (task )); 42 } 43if (reset_timer ) { 44time_last_task = ggml_time_ms (); 45 } 46condition_tasks .notify_one (); 47return task_id ; 48} 49 50int server_queue::post (std::vector < server_task > && tasks ,bool front ) { 51 std::unique_lock < std::mutex > lock (mutex_tasks ); 52bool reset_timer = false; 53for (auto & task :tasks ) { 54if (task .id == -1 ) { 55task .id = id ++ ; 56 } 57// if this is cancel task make sure to clean up pending tasks 58if (task .type == SERVER_TASK_TYPE_CANCEL ) { 59cleanup_pending_task (task .id_target ); 60 } 61reset_timer |=task_resets_idle_timer (task .type ); 62QUE_DBG ("new task, id = %d/%d, front = %d\n" ,task .id , (int )tasks .size (),front ); 63if (front ) { 64queue_tasks .push_front (std::move (task )); 65 }else { 66queue_tasks .push_back (std::move (task )); 67 } 68 } 69if (reset_timer ) { 70time_last_task = ggml_time_ms (); 71 } 72condition_tasks .notify_one (); 73return 0 ; 74} 75 76void server_queue::defer (server_task && task ) { 77 std::unique_lock < std::mutex > lock (mutex_tasks ); 78QUE_DBG ("defer task, id = %d\n" ,task .id ); 79queue_tasks_deferred .push_back (std::move (task )); 80time_last_task = ggml_time_ms (); 81condition_tasks .notify_one (); 82} 83 84int server_queue::get_new_id () { 85 std::unique_lock < std::mutex > lock (mutex_tasks ); 86int new_id = id ++ ; 87return new_id ; 88} 89 90void server_queue::pop_deferred_task (int id_slot ) { 91 std::unique_lock < std::mutex > lock (mutex_tasks ); 92if (!queue_tasks_deferred .empty ()) { 93// try to find a task that uses the specified slot 94bool found = false; 95for (auto it = queue_tasks_deferred .begin ();it != queue_tasks_deferred .end ();++ it ) { 96if (it -> id_slot == id_slot ) { 97QUE_DBG ("pop deferred task (use slot %d), id_task = %d\n" ,id_slot ,it -> id ); 98queue_tasks .emplace_front (std::move (* it )); 99queue_tasks_deferred .erase (it ); 100found = true; 101break ; 102 } 103 } 104// if not tasks found using the slot, just pop the first deferred task (default behavior) 105if (!found ) { 106QUE_DBG ("pop deferred task, id_task = %d\n" ,queue_tasks_deferred .front ().id ); 107queue_tasks .emplace_front (std::move (queue_tasks_deferred .front ())); 108queue_tasks_deferred .pop_front (); 109 } 110 } 111time_last_task = ggml_time_ms (); 112condition_tasks .notify_one (); 113} 114 115void server_queue::wait_until_no_sleep () { 116 std::unique_lock < std::mutex > lock (mutex_tasks ); 117if (!sleeping ) { 118return ; 119 }else { 120if (!req_stop_sleeping ) { 121QUE_DBG ("%s" ,"requesting to stop sleeping\n" ); 122req_stop_sleeping = true; 123condition_tasks .notify_one ();// only main thread is waiting on this 124 } 125QUE_DBG ("%s" ,"waiting until no sleep\n" ); 126condition_tasks .wait (lock , [& ]{ 127return !sleeping ; 128 }); 129 } 130} 131 132void server_queue::terminate () { 133 std::unique_lock < std::mutex > lock (mutex_tasks ); 134running = false; 135condition_tasks .notify_all (); 136} 137 138bool server_queue::process_new_tasks (bool is_yielding ) { 139while (true) { 140 std::unique_lock < std::mutex > lock (mutex_tasks ); 141if (!running ) { 142QUE_DBG ("%s" ,"terminate\n" ); 143return true; 144 } 145if (queue_tasks .empty ()) { 146return false; 147 } 148server_task task = std::move (queue_tasks .front ()); 149queue_tasks .pop_front (); 150lock .unlock (); 151 152QUE_DBG ("processing task, id = %d\n" ,task .id ); 153if (!callback_new_task (std::move (task ),is_yielding )) { 154// set it aside, do not put it back in the queue, else we offer it again in a loop 155GGML_ASSERT (is_yielding && "a task can only be declined while yielding" ); 156QUE_DBG ("task declined, id = %d\n" ,task .id ); 157lock .lock (); 158queue_tasks_unhandled .push_back (std::move (task )); 159 } 160 } 161} 162 163void server_queue::worker_loop () { 164while (true) { 165 { 166 std::unique_lock < std::mutex > lock (mutex_tasks ); 167// wait on busy instead of yielding - busy stays set even when the yield already ended 168worker .cv .wait (lock , [& ]{ 169return worker .stop || worker .busy ; 170 }); 171if (worker .stop ) { 172return ; 173 } 174 } 175 176// process tasks while the yield is active 177while (true) { 178bool terminated = false; 179try { 180// note: do not hold any lock here, the callback may post new tasks 181terminated = process_new_tasks (true); 182 }catch (...) { 183 std::unique_lock < std::mutex > lock (mutex_tasks ); 184worker .exception = std::current_exception (); 185break ; 186 } 187 188 std::unique_lock < std::mutex > lock (mutex_tasks ); 189if (terminated || worker .stop || !worker .yielding ) { 190break ; 191 } 192if (!queue_tasks .empty ()) { 193continue ;// a new task arrived in the meantime 194 } 195condition_tasks .wait (lock , [& ]{ 196return worker .stop || !running || !worker .yielding || !queue_tasks .empty (); 197 }); 198 } 199 200// signal to yield_to_queue() that no more tasks will be processed 201 { 202 std::unique_lock < std::mutex > lock (mutex_tasks ); 203worker .busy = false; 204 } 205condition_tasks .notify_all (); 206 } 207} 208 209void server_queue::worker_stop () { 210if (!worker .thread .joinable ()) { 211return ; 212 } 213 { 214 std::unique_lock < std::mutex > lock (mutex_tasks ); 215worker .stop = true; 216 } 217worker .cv .notify_one (); 218condition_tasks .notify_all (); 219worker .thread .join (); 220} 221 222void server_queue::yield_to_queue (std::function < void ()> && work ) { 223GGML_ASSERT (worker .thread .joinable ()&& "yield_to_queue() requires start_loop() to be running" ); 224 225QUE_DBG ("%s" ,"yielding to queue\n" ); 226 227 { 228 std::unique_lock < std::mutex > lock (mutex_tasks ); 229GGML_ASSERT (!worker .busy && "yield_to_queue() cannot be nested" ); 230worker .busy = true; 231worker .yielding = true; 232 } 233worker .cv .notify_one (); 234 235// run the work on the current thread, so that all ggml compute stays on the same thread 236 std::exception_ptr exception ; 237try { 238work (); 239 }catch (...) { 240exception = std::current_exception (); 241 } 242 243 { 244 std::unique_lock < std::mutex > lock (mutex_tasks ); 245 246// the yield is over, wait for the worker to finish its current task 247worker .yielding = false; 248condition_tasks .notify_all (); 249condition_tasks .wait (lock , [& ]{ 250return !worker .busy ; 251 }); 252 253// put the declined tasks back, keeping their order 254while (!queue_tasks_unhandled .empty ()) { 255queue_tasks .push_front (std::move (queue_tasks_unhandled .back ())); 256queue_tasks_unhandled .pop_back (); 257 } 258 259// make sure to avoid idle timeout here 260time_last_task = ggml_time_ms (); 261 262// an exception from work() takes precedence over the one from the worker 263if (!exception ) { 264 std::swap (exception ,worker .exception ); 265 }else { 266worker .exception = nullptr ; 267 } 268 } 269 270QUE_DBG ("%s" ,"done yielding to queue\n" ); 271 272// note: rethrow only after the declined tasks are back in the queue, so they are not lost 273if (exception ) { 274 std::rethrow_exception (exception ); 275 } 276} 277 278void server_queue::start_loop (int64_t idle_sleep_ms ) { 279running = true; 280time_last_task = ggml_time_ms (); 281 282// spawn the worker thread used by yield_to_queue() 283GGML_ASSERT (!worker .thread .joinable ()&& "start_loop() is already running" ); 284worker .stop = false; 285worker .busy = false; 286worker .yielding = false; 287worker .thread = std::thread ([this ]() {worker_loop (); }); 288 289constexpr auto max_wait_time = std::chrono::seconds (1 ); 290auto should_sleep = [& ]()-> bool { 291// caller must hold mutex_tasks 292if (idle_sleep_ms < 0 ) { 293return false; 294 } 295int64_t now = ggml_time_ms (); 296return (now - time_last_task ) >=idle_sleep_ms ; 297 }; 298 299while (true) { 300QUE_DBG ("%s" ,"processing new tasks\n" ); 301if (process_new_tasks (false)) { 302break ;// terminate 303 } 304 305// all tasks in the current loop is processed, slots data is now ready 306QUE_DBG ("%s" ,"update slots\n" ); 307 308// this will run the main inference process for all slots 309const int64_t t_update_slots = ggml_time_ms (); 310callback_update_slots (); 311 { 312// update_slots() may take a while to finish, we need to make sure it's not counted as idle 313// shift instead of reset, so that non-task_resets_idle_timer tasks do not delay the sleep 314 std::unique_lock < std::mutex > lock (mutex_tasks ); 315const int64_t now = ggml_time_ms (); 316time_last_task = std::min (now ,time_last_task + (now - t_update_slots )); 317 } 318 319QUE_DBG ("%s" ,"waiting for new tasks\n" ); 320while (true) { 321 std::unique_lock < std::mutex > lock (mutex_tasks ); 322if (!running || !queue_tasks .empty ()) { 323break ;// go back to process new tasks or terminate 324 } 325 326// no tasks, check for sleeping state 327if (should_sleep ()) { 328QUE_INF ("%s" ,"entering sleeping state\n" ); 329sleeping = true; 330// Call order cb0 -> cb1 -> cb{N} 331for (auto & cb :callback_sleeping_state ) { 332cb (true); 333 } 334req_stop_sleeping = false; 335// wait until we are requested to exit sleeping state 336condition_tasks .wait (lock , [& ]{ 337return (!running || req_stop_sleeping ); 338 }); 339if (!running ) {// may changed during sleep 340break ;// terminate 341 } 342QUE_INF ("%s" ,"exiting sleeping state\n" ); 343req_stop_sleeping = false; 344// Call order cb{N} -> cb1 -> cb0 345for (size_t i = callback_sleeping_state .size ();i > 0 ;i -- ) { 346callback_sleeping_state [i - 1 ](false); 347 } 348sleeping = false; 349time_last_task = ggml_time_ms (); 350condition_tasks .notify_all ();// notify wait_until_no_sleep() 351break ;// process new tasks 352 }else { 353// wait for new tasks or timeout for checking sleeping condition 354bool res = condition_tasks .wait_for (lock ,max_wait_time , [& ]{ 355return (!queue_tasks .empty ()|| !running ); 356 }); 357if (res ) { 358break ;// new task arrived or terminate 359 } 360// otherwise, loop again to check sleeping condition 361 } 362 } 363 } 364 365worker_stop (); 366} 367 368void server_queue::cleanup_pending_task (int id_target ) { 369// no need lock because this is called exclusively by post() 370auto rm_func = [id_target ](const server_task & task ) { 371return task .id == id_target ; 372 }; 373queue_tasks .erase ( 374 std::remove_if (queue_tasks .begin (),queue_tasks .end (),rm_func ), 375queue_tasks .end ()); 376queue_tasks_deferred .erase ( 377 std::remove_if (queue_tasks_deferred .begin (),queue_tasks_deferred .end (),rm_func ), 378queue_tasks_deferred .end ()); 379// a task declined while yielding is not in queue_tasks yet, but it can still be cancelled 380queue_tasks_unhandled .erase ( 381 std::remove_if (queue_tasks_unhandled .begin (),queue_tasks_unhandled .end (),rm_func ), 382queue_tasks_unhandled .end ()); 383} 384 385// 386// server_response 387// 388 389void server_response::add_waiting_task_id (int id_task ) { 390RES_DBG ("add task %d to waiting list. current waiting = %d (before add)\n" ,id_task , (int )waiting_task_ids .size ()); 391 392 std::unique_lock < std::mutex > lock (mutex_results ); 393waiting_task_ids .insert (id_task ); 394} 395 396void server_response::add_waiting_task_ids (const std::unordered_set < int > & id_tasks ) { 397 std::unique_lock < std::mutex > lock (mutex_results ); 398 399for (const auto & id_task :id_tasks ) { 400RES_DBG ("add task %d to waiting list. current waiting = %d (before add)\n" ,id_task , (int )waiting_task_ids .size ()); 401waiting_task_ids .insert (id_task ); 402 } 403} 404 405void server_response::remove_waiting_task_id (int id_task ) { 406RES_DBG ("remove task %d from waiting list. current waiting = %d (before remove)\n" ,id_task , (int )waiting_task_ids .size ()); 407 408 std::unique_lock < std::mutex > lock (mutex_results ); 409waiting_task_ids .erase (id_task ); 410// make sure to clean up all pending results 411queue_results .erase ( 412 std::remove_if (queue_results .begin (),queue_results .end (), [id_task ](const server_task_result_ptr & res ) { 413return res -> id == id_task ; 414 }), 415queue_results .end ()); 416} 417 418void server_response::remove_waiting_task_ids (const std::unordered_set < int > & id_tasks ) { 419 std::unique_lock < std::mutex > lock (mutex_results ); 420 421for (const auto & id_task :id_tasks ) { 422RES_DBG ("remove task %d from waiting list. current waiting = %d (before remove)\n" ,id_task , (int )waiting_task_ids .size ()); 423waiting_task_ids .erase (id_task ); 424 } 425} 426 427server_task_result_ptr server_response::recv (const std::unordered_set < int > & id_tasks ) { 428while (true) { 429 std::unique_lock < std::mutex > lock (mutex_results ); 430condition_results .wait (lock , [& ]{ 431if (!running ) { 432RES_DBG ("%s : queue result stop\n" ,"recv" ); 433 std::terminate ();// we cannot return here since the caller is HTTP code 434 } 435return !queue_results .empty (); 436 }); 437 438for (size_t i = 0 ;i < queue_results .size ();i ++ ) { 439if (id_tasks .find (queue_results [i ]-> id )!= id_tasks .end ()) { 440server_task_result_ptr res = std::move (queue_results [i ]); 441queue_results .erase (queue_results .begin ()+ i ); 442return res ; 443 } 444 } 445 } 446 447// should never reach here 448} 449 450server_task_result_ptr server_response::recv_with_timeout (const std::unordered_set < int > & id_tasks ,int timeout ) { 451while (true) { 452 std::unique_lock < std::mutex > lock (mutex_results ); 453 454for (int i = 0 ;i < (int )queue_results .size ();i ++ ) { 455if (id_tasks .find (queue_results [i ]-> id )!= id_tasks .end ()) { 456server_task_result_ptr res = std::move (queue_results [i ]); 457queue_results .erase (queue_results .begin ()+ i ); 458return res ; 459 } 460 } 461 462 std::cv_status cr_res = condition_results .wait_for (lock , std::chrono::seconds (timeout )); 463if (!running ) { 464RES_DBG ("%s : queue result stop\n" ,__func__ ); 465 std::terminate ();// we cannot return here since the caller is HTTP code 466 } 467if (cr_res == std::cv_status::timeout ) { 468return nullptr ; 469 } 470 } 471 472// should never reach here 473} 474 475server_task_result_ptr server_response::recv (int id_task ) { 476 std::unordered_set < int > id_tasks = {id_task }; 477return recv (id_tasks ); 478} 479 480void server_response::send (server_task_result_ptr && result ) { 481RES_DBG ("sending result for task id = %d\n" ,result -> id ); 482 483 std::unique_lock < std::mutex > lock (mutex_results ); 484for (const auto & id_task :waiting_task_ids ) { 485if (result -> id == id_task ) { 486RES_DBG ("task id = %d pushed to result queue\n" ,result -> id ); 487 488queue_results .emplace_back (std::move (result )); 489condition_results .notify_all (); 490return ; 491 } 492 } 493} 494 495void server_response::broadcast (server_task_result_ptr && result ) { 496 std::unique_lock < std::mutex > lock (mutex_results ); 497for (const auto & id_task :waiting_task_ids ) { 498RES_DBG ("task id = %d pushed to result queue\n" ,id_task ); 499server_task_result_ptr res_copy (result -> clone ()); 500res_copy -> id = id_task ;// override id with target task id 501queue_results .emplace_back (std::move (res_copy )); 502 } 503condition_results .notify_all (); 504} 505 506void server_response::terminate () { 507running = false; 508condition_results .notify_all (); 509} 510 511// 512// server_response_reader 513// 514 515void server_response_reader::post_task (server_task && task ,bool front ) { 516GGML_ASSERT (id_tasks .empty ()&& "post_task() can only be called once per reader" ); 517GGML_ASSERT (!task .is_parent ()&& "not supported, use post_tasks() instead" ); 518task .index = 0 ; 519id_tasks .insert (task .id ); 520states .push_back (task .create_state ()); 521queue_results .add_waiting_task_id (task .id ); 522queue_tasks .post (std::move (task ),front ); 523} 524 525void server_response_reader::post_tasks (std::vector < server_task > && tasks ,bool front ) { 526GGML_ASSERT (id_tasks .empty ()&& "post_tasks() can only be called once per reader" ); 527id_tasks = server_task::get_list_id (tasks ); 528states .reserve (tasks .size ()); 529size_t index = 0 ; 530for (auto & task :tasks ) { 531task .index = index ++ ; 532states .push_back (task .create_state ()); 533// for child tasks 534for (auto & child_task :task .child_tasks ) { 535child_task .index = index ++ ; 536states .push_back (child_task .create_state ()); 537 } 538 } 539GGML_ASSERT (states .size ()== id_tasks .size ()); 540queue_results .add_waiting_task_ids (id_tasks ); 541queue_tasks .post (std::move (tasks ),front ); 542} 543 544bool server_response_reader::has_next ()const { 545return !cancelled && received_count < id_tasks .size (); 546} 547 548// return nullptr if should_stop() is true before receiving a result 549// note: if one error is received, it will stop further processing and return error result 550server_task_result_ptr server_response_reader::next (const std::function < bool ()> & should_stop ) { 551while (true) { 552server_task_result_ptr result = queue_results .recv_with_timeout (id_tasks ,polling_interval_seconds ); 553if (result == nullptr ) { 554// timeout, check stop condition 555if (should_stop ()) { 556return nullptr ; 557 } 558 }else { 559if (result -> is_error ()) { 560stop ();// cancel remaining tasks 561SRV_DBG ("%s" ,"received error result, stopping further processing\n" ); 562return result ; 563 } 564if (!states .empty ()) { 565// update the generation state if needed 566const size_t idx = result -> index ; 567GGML_ASSERT (idx < states .size ()); 568result -> update (states [idx ]); 569 } 570if (result -> is_stop ()) { 571received_count ++ ; 572 } 573return result ; 574 } 575 } 576 577// should not reach here 578} 579 580server_response_reader::batch_response server_response_reader::wait_for_all (const std::function < bool ()> & should_stop ) { 581batch_response batch_res ; 582batch_res .results .clear (); 583batch_res .results .resize (id_tasks .size ()); 584while (has_next ()) { 585auto res = next (should_stop ); 586if (res == nullptr ) { 587batch_res .is_terminated = true; 588return batch_res ; 589 } 590if (res -> is_error ()) { 591batch_res .error = std::move (res ); 592return batch_res ; 593 } 594const size_t idx = res -> index ; 595GGML_ASSERT (idx < batch_res .results .size ()&& "index out of range" ); 596GGML_ASSERT (batch_res .results [idx ]== nullptr && "duplicate result received" ); 597batch_res .results [idx ]= std::move (res ); 598 } 599return batch_res ; 600} 601 602void server_response_reader::stop () { 603queue_results .remove_waiting_task_ids (id_tasks ); 604if (has_next ()&& !cancelled ) { 605// if tasks is not finished yet, cancel them 606cancelled = true; 607 std::vector < server_task > cancel_tasks ; 608cancel_tasks .reserve (id_tasks .size ()); 609for (const auto & id_task :id_tasks ) { 610SRV_WRN ("cancel task, id_task = %d\n" ,id_task ); 611server_task task (SERVER_TASK_TYPE_CANCEL ); 612task .id_target = id_task ; 613queue_results .remove_waiting_task_id (id_task ); 614cancel_tasks .push_back (std::move (task )); 615 } 616// push to beginning of the queue, so it has highest priority 617queue_tasks .post (std::move (cancel_tasks ), true); 618 }else { 619SRV_DBG ("%s" ,"all tasks already finished, no need to cancel\n" ); 620 } 621}