char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
41361c859
char/aliceai-t5-moe
1// A basic application simulating a server with multiple clients. 2// The clients submit requests to the server and they are processed in parallel. 3 4#include "arg.h" 5#include "common.h" 6#include "sampling.h" 7#include "log.h" 8#include "llama.h" 9 10#include <algorithm> 11#include <clocale> 12#include <cmath> 13#include <cstdio> 14#include <string> 15#include <vector> 16#include <ctime> 17 18// trim whitespace from the beginning and end of a string 19static std::string trim (const std::string & str ) { 20size_t start = 0 ; 21size_t end = str .size (); 22 23while (start < end && isspace (str [start ])) { 24start += 1 ; 25 } 26 27while (end > start && isspace (str [end - 1 ])) { 28end -= 1 ; 29 } 30 31return str .substr (start ,end - start ); 32} 33 34static std::string k_system = 35R"(Transcript of a never ending dialog, where the User interacts with an Assistant. 36The Assistant is helpful, kind, honest, good at writing, and never fails to answer the User's requests immediately and with precision. 37 38User: 39Recommend a nice restaurant in the area. 40Assistant: 41I recommend the restaurant "The Golden Duck". It is a 5 star restaurant with a great view of the city. The food is delicious and the service is excellent. The prices are reasonable and the portions are generous. The restaurant is located at 123 Main Street, New York, NY 10001. The phone number is (212) 555-1234. The hours are Monday through Friday from 11:00 am to 10:00 pm. The restaurant is closed on Saturdays and Sundays. 42User: 43Who is Richard Feynman? 44Assistant: 45Richard Feynman was an American physicist who is best known for his work in quantum mechanics and particle physics. He was awarded the Nobel Prize in Physics in 1965 for his contributions to the development of quantum electrodynamics. He was a popular lecturer and author, and he wrote several books, including "Surely You're Joking, Mr. Feynman!" and "What Do You Care What Other People Think?". 46)" ; 47 48static std::vector < std::string > k_questions = { 49"What is the tallest mountain in the world?" , 50"Who was the first person to win two Nobel Prizes?" , 51"Which country invented paper?" , 52"What organ is primarily responsible for pumping blood throughout the body?" , 53"Which planet is known for its prominent ring system?" , 54"Who directed the movie 'Inception'?" , 55"What is the freezing point of water in Fahrenheit?" , 56"Which animal is known to have the longest lifespan?" , 57"What language has the most native speakers worldwide?" , 58"What is the capital city of Canada?" , 59"Who is credited with inventing the World Wide Web?" , 60"Which metal is liquid at room temperature?" , 61"What is the term for an animal that eats both plants and meat?" , 62"Who painted 'The Starry Night'?" , 63"What gas do humans exhale that plants use for photosynthesis?" , 64"What year did World War II end?" , 65"Which continent has the most countries?" , 66"Who wrote the novel 'Frankenstein'?" , 67"What does DNA stand for?" , 68"What is the main ingredient in traditional Japanese miso soup?" 69}; 70 71static std::vector < std::string > k_answers = { 72"The tallest mountain in the world is Mount Everest." , 73"Marie Curie was the first person to win two Nobel Prizes." , 74"Paper was invented in China." , 75"The heart is the organ responsible for pumping blood." , 76"Saturn is known for its prominent ring system." , 77"Christopher Nolan directed the movie 'Inception'." , 78"The freezing point of water in Fahrenheit is 32°F." , 79"The bowhead whale is known to have the longest lifespan among mammals." , 80"Mandarin Chinese has the most native speakers in the world." , 81"The capital city of Canada is Ottawa." , 82"Tim Berners-Lee is credited with inventing the World Wide Web." , 83"Mercury is the metal that is liquid at room temperature." , 84"An animal that eats both plants and meat is called an omnivore." , 85"'The Starry Night' was painted by Vincent van Gogh." , 86"Humans exhale carbon dioxide, which plants use in photosynthesis." , 87"World War II ended in 1945." , 88"Africa is the continent with the most countries." , 89"The novel 'Frankenstein' was written by Mary Shelley." , 90"DNA stands for Deoxyribonucleic Acid." , 91"The main ingredient in traditional Japanese miso soup is fermented soybean paste." 92}; 93 94static std::vector < std::string > k_prompts = { 95"What is the meaning of life?" , 96"Tell me an interesting fact about llamas." , 97"What is the best way to cook a steak?" , 98"Are you familiar with the Special Theory of Relativity and can you explain it to me?" , 99"Recommend some interesting books to read." , 100"What is the best way to learn a new language?" , 101"How to get a job at Google?" , 102"If you could have any superpower, what would it be?" , 103"I want to learn how to play the piano. What would be the best way to do it?" , 104}; 105 106struct client { 107 ~client () { 108if (smpl ) { 109common_sampler_free (smpl ); 110 } 111 } 112 113int32_t id = 0 ; 114 115llama_seq_id seq_id = -1 ; 116 117llama_token sampled ; 118 119int64_t t_start_prompt ; 120int64_t t_start_gen ; 121 122int32_t n_past = 0 ; 123int32_t n_prompt = 0 ; 124int32_t n_decoded = 0 ; 125int32_t i_batch = -1 ; 126 127 std::string input ; 128 std::string prompt ; 129 std::string response ; 130 131struct common_sampler * smpl = nullptr ; 132}; 133 134static void print_date_time () { 135 std::time_t current_time = std::time (nullptr ); 136 std::tm * local_time = std::localtime (& current_time ); 137char buffer [80 ]; 138strftime (buffer ,sizeof (buffer ),"%Y-%m-%d %H:%M:%S" ,local_time ); 139 140LOG_INF ("\n" ); 141LOG_INF ("\033[35mrun parameters as of %s\033[0m\n" ,buffer ); 142LOG_INF ("\n" ); 143} 144 145// Define a split string function to ... 146static std::vector < std::string > split_string (const std::string & input ,char delimiter ) { 147 std::vector < std::string > tokens ; 148 std::istringstream stream (input ); 149 std::string token ; 150while (std::getline (stream ,token ,delimiter )) { 151tokens .push_back (token ); 152 } 153return tokens ; 154} 155 156int main (int argc ,char ** argv ) { 157 std::setlocale (LC_NUMERIC ,"C" ); 158 159srand (1234 ); 160 161common_params params ; 162 163params .n_predict = 128 ; 164params .n_junk = 1 ; 165 166common_init (); 167 168if (!common_params_parse (argc ,argv ,params ,LLAMA_EXAMPLE_PARALLEL )) { 169return 1 ; 170 } 171 172// number of simultaneous "clients" to simulate 173const int32_t n_clients = params .n_parallel ; 174 175// dedicate one sequence to the system prompt 176params .n_parallel += 1 ; 177 178// requests to simulate 179const int32_t n_seq = params .n_sequences ; 180 181// insert new requests as soon as the previous one is done 182const bool cont_batching = params .cont_batching ; 183 184// is the system prompt shared in the cache 185const bool is_sp_shared = params .is_pp_shared ; 186 187// extra text to insert in each client's prompt in order to make it larger 188const int32_t n_junk = std::max (1 ,params .n_junk ); 189 190// signed seed, use negative values to indicate different seeds for the different clients 191const int32_t & sseed = params .sampling .seed ; 192 193// init llama.cpp 194llama_backend_init (); 195llama_numa_init (params .numa ); 196 197// load the target model 198auto llama_init = common_init_from_params (params ); 199 200auto * model = llama_init -> model (); 201auto * ctx = llama_init -> context (); 202 203auto * mem = llama_get_memory (ctx ); 204 205const llama_vocab * vocab = llama_model_get_vocab (model ); 206 207// load the prompts from an external file if there are any 208if (params .prompt .empty ()) { 209LOG_INF ("\033[32mNo new questions so proceed with build-in defaults.\033[0m\n" ); 210 }else { 211// Output each line of the input params.prompts vector and copy to k_prompts 212int index = 0 ; 213LOG_INF ("\033[32mNow printing the external prompt file %s\033[0m\n\n" ,params .prompt_file .c_str ()); 214 215 std::vector < std::string > prompts = split_string (params .prompt ,'\n' ); 216for (const auto & prompt :prompts ) { 217k_prompts .resize (index + 1 ); 218k_prompts [index ]= prompt ; 219index ++ ; 220LOG_INF ("%3d prompt: %s\n" ,index ,prompt .c_str ()); 221 } 222 } 223 224LOG_INF ("\n\n" ); 225 226const int n_ctx = llama_n_ctx (ctx ); 227 228if (sseed >=0 ) { 229LOG_INF ("%s: initializing all samplers with the same RNG seed: %d (use a negative seed to have different seeds)\n" ,__func__ ,sseed ); 230 }else { 231LOG_INF ("%s: initializing samplers with different RNG seeds, starting from %d\n" ,__func__ ,sseed ); 232 } 233 234 std::vector < client > clients (n_clients ); 235for (size_t i = 0 ;i < clients .size ();++ i ) { 236auto & client = clients [i ]; 237client .id = i ; 238client .smpl = common_sampler_init (model ,params .sampling ); 239 240if (sseed < 0 ) { 241params .sampling .seed -- ; 242 } 243 } 244 245 std::vector < llama_token > tokens_system ; 246 247tokens_system = common_tokenize (ctx ,k_system , true); 248const int32_t n_tokens_system = tokens_system .size (); 249 250llama_seq_id g_seq_id = 0 ; 251 252// the max batch size is as large as the context to handle cases where we get very long input prompt from multiple 253// users. regardless of the size, the main loop will chunk the batch into a maximum of params.n_batch tokens at a time 254llama_batch batch = llama_batch_init (n_ctx ,0 ,1 ); 255 256int32_t n_total_prompt = 0 ; 257int32_t n_total_gen = 0 ; 258int32_t n_cache_miss = 0 ; 259 260const auto t_main_start = ggml_time_us (); 261 262LOG_INF ("%s: Simulating parallel requests from clients:\n" ,__func__ ); 263LOG_INF ("%s: n_parallel = %d, n_sequences = %d, cont_batching = %d, system tokens = %d\n" ,__func__ ,n_clients ,n_seq ,cont_batching ,n_tokens_system ); 264LOG_INF ("\n" ); 265 266if (is_sp_shared ) { 267LOG_INF ("%s: Evaluating the system prompt ...\n" ,__func__ ); 268 269for (int32_t i = 0 ;i < n_tokens_system ;++ i ) { 270common_batch_add (batch ,tokens_system [i ],i , {0 }, false); 271 } 272 273if (llama_decode (ctx ,batch )!= 0 ) { 274LOG_ERR ("%s: llama_decode() failed\n" ,__func__ ); 275return 1 ; 276 } 277 278// assign the system KV cache to all parallel sequences 279for (int32_t i = 1 ;i <=n_clients ;++ i ) { 280llama_memory_seq_cp (mem ,0 ,i ,-1 ,-1 ); 281 } 282 283LOG_INF ("\n" ); 284 } 285 286LOG_INF ("Processing requests ...\n\n" ); 287 288while (true) { 289common_batch_clear (batch ); 290 291// decode any currently ongoing sequences 292for (auto & client :clients ) { 293if (client .seq_id == -1 ) { 294continue ; 295 } 296 297client .i_batch = batch .n_tokens ; 298 299common_batch_add (batch ,client .sampled ,client .n_past ++ , {client .id + 1 }, true); 300 301client .n_decoded += 1 ; 302 } 303 304if (batch .n_tokens == 0 ) { 305// all sequences have ended - clear the entire KV cache 306for (int i = 1 ;i <=n_clients ;++ i ) { 307llama_memory_seq_rm (mem ,i ,-1 ,-1 ); 308// but keep the system prompt 309llama_memory_seq_cp (mem ,0 ,i ,-1 ,-1 ); 310 } 311 312LOG_INF ("%s: clearing the KV cache\n" ,__func__ ); 313 } 314 315// insert new sequences for decoding 316if (cont_batching || batch .n_tokens == 0 ) { 317for (auto & client :clients ) { 318if (client .seq_id == -1 && g_seq_id < n_seq ) { 319client .seq_id = g_seq_id ; 320 321client .t_start_prompt = ggml_time_us (); 322client .t_start_gen = 0 ; 323 324client .input = k_prompts [rand () %k_prompts .size ()]; 325client .response = "" ; 326 327// construct the prompt: 328// [system prompt] + [junk] + [user prompt] 329client .n_past = 0 ; 330client .prompt = "" ; 331if (is_sp_shared ) { 332client .n_past = n_tokens_system ; 333 }else { 334client .prompt += k_system ; 335 } 336 337const int n_junk_cur = rand () %n_junk ; 338 339for (int i = 0 ;i < n_junk_cur ;++ i ) { 340const int r = rand () %k_questions .size (); 341client .prompt += "User:\n" + k_questions [r ]+ "\nAssistant:\n " + k_answers [r ]+ "\n" ; 342 } 343client .prompt += "User:\n" + client .input + "\nAssistant:\n" ; 344 345common_sampler_reset (client .smpl ); 346 347// do not prepend BOS because we have a system prompt! 348 std::vector < llama_token > tokens_prompt ; 349tokens_prompt = common_tokenize (ctx ,client .prompt , false); 350 351for (size_t i = 0 ;i < tokens_prompt .size ();++ i ) { 352common_batch_add (batch ,tokens_prompt [i ],client .n_past ++ , {client .id + 1 }, false); 353 } 354 355// extract the logits only for the last token 356if (batch .n_tokens > 0 ) { 357batch .logits [batch .n_tokens - 1 ]= true; 358 } 359 360client .n_prompt = tokens_prompt .size (); 361client .n_decoded = 0 ; 362client .i_batch = batch .n_tokens - 1 ; 363 364LOG_INF ("\033[31mClient %3d, seq %4d, junk = %4d, prompt = %d, started decoding ...\033[0m\n" ,client .id ,client .seq_id ,n_junk_cur ,client .n_prompt ); 365 366g_seq_id += 1 ; 367 368// insert new requests one-by-one 369//if (cont_batching) { 370// break; 371//} 372 } 373 } 374 } 375 376if (batch .n_tokens == 0 ) { 377break ; 378 } 379 380// process in chunks of params.n_batch 381int32_t n_batch = params .n_batch ; 382 383int32_t i_next = 0 ; 384 385for (int32_t i = 0 ;i < batch .n_tokens ;i = i_next ) { 386// experiment: process in powers of 2 387//if (i + n_batch > (int32_t) batch.n_tokens && n_batch > 32) { 388// n_batch /= 2; 389// i -= n_batch; 390// continue; 391//} 392 393const int32_t n_tokens = std::min (n_batch ,batch .n_tokens - i ); 394 395llama_batch batch_view = { 396n_tokens , 397batch .token + i , 398nullptr , 399batch .pos + i , 400batch .n_seq_id + i , 401batch .seq_id + i , 402batch .logits + i , 403 }; 404 405const int ret = llama_decode (ctx ,batch_view ); 406if (ret != 0 ) { 407if (n_batch == 1 || ret < 0 ) { 408// if you get here, it means the KV cache is full - try increasing it via the context size 409LOG_ERR ("%s : failed to decode the batch, n_batch = %d, ret = %d\n" ,__func__ ,n_batch ,ret ); 410return 1 ; 411 } 412 413LOG_WRN ("%s : failed to decode the batch, retrying with n_batch = %d\n" ,__func__ ,n_batch /2 ); 414 415n_cache_miss += 1 ; 416 417// retry with half the batch size to try to find a free slot in the KV cache 418n_batch /=2 ; 419 420continue ; 421 } 422 423LOG_DBG ("%s : decoded batch of %d tokens\n" ,__func__ ,n_tokens ); 424 425// move the head of the batch forward with the number of tokens we just processed 426i_next = i + n_tokens ; 427 428// on successful decode, restore the original batch size 429n_batch = params .n_batch ; 430 431for (auto & client :clients ) { 432if (client .i_batch < (int )i || client .i_batch >= (int ) (i + n_tokens )) { 433continue ; 434 } 435 436//printf("client %d, seq %d, token %d, pos %d, batch %d\n", 437// client.id, client.seq_id, client.sampled, client.n_decoded, client.i_batch); 438 439const llama_token id = common_sampler_sample (client .smpl ,ctx ,client .i_batch - i ); 440 441common_sampler_accept (client .smpl ,id , true); 442 443if (client .n_decoded == 1 ) { 444// start measuring generation time after the first token to make sure all concurrent clients 445// have their prompt already processed 446client .t_start_gen = ggml_time_us (); 447 } 448 449const std::string token_str = common_token_to_piece (ctx ,id ); 450 451client .response += token_str ; 452client .sampled = id ; 453 454//printf("client %d, seq %d, token %d, pos %d, batch %d: %s\n", 455// client.id, client.seq_id, id, client.n_decoded, client.i_batch, token_str.c_str()); 456 457if (client .n_decoded > 2 && 458 (llama_vocab_is_eog (vocab ,id )|| 459 (params .n_predict > 0 && client .n_decoded >=params .n_predict )|| 460client .response .find ("User:" )!= std::string::npos )) { 461// basic reverse prompt 462const size_t pos = client .response .find ("User:" ); 463if (pos != std::string::npos ) { 464client .response = client .response .substr (0 ,pos ); 465 } 466 467// delete only the generated part of the sequence, i.e. keep the system prompt in the cache 468llama_memory_seq_rm (mem ,client .id + 1 ,-1 ,-1 ); 469llama_memory_seq_cp (mem ,0 ,client .id + 1 ,-1 ,-1 ); 470 471const auto t_main_end = ggml_time_us (); 472 473LOG_INF ("\033[31mClient %3d, seq %3d/%3d, prompt %4d t, response %4d t, time %5.2f s, speed %5.2f t/s, cache miss %d \033[0m \n\nInput: %s\n\033[35mResponse: %s\033[0m\n\n" , 474client .id ,client .seq_id ,n_seq ,client .n_prompt ,client .n_decoded , 475 (t_main_end - client .t_start_prompt ) /1e6 , 476 (double ) (client .n_prompt + client .n_decoded ) / (t_main_end - client .t_start_prompt )* 1e6 , 477n_cache_miss , 478 ::trim (client .input ).c_str (), 479 ::trim (client .response ).c_str ()); 480 481n_total_prompt += client .n_prompt ; 482n_total_gen += client .n_decoded ; 483 484client .seq_id = -1 ; 485 } 486 487client .i_batch = -1 ; 488 } 489 } 490 } 491 492const auto t_main_end = ggml_time_us (); 493 494print_date_time (); 495 496LOG_INF ("%s: n_parallel = %d, n_sequences = %d, cont_batching = %d, system tokens = %d\n" ,__func__ ,n_clients ,n_seq ,cont_batching ,n_tokens_system ); 497if (params .prompt_file .empty ()) { 498params .prompt_file = "used built-in defaults" ; 499 } 500LOG_INF ("External prompt file: \033[32m%s\033[0m\n" ,params .prompt_file .c_str ()); 501LOG_INF ("Model and path used: \033[32m%s\033[0m\n\n" ,params .model .path .c_str ()); 502 503LOG_INF ("Total prompt tokens: %6d, speed: %5.2f t/s\n" ,n_total_prompt , (double ) (n_total_prompt ) / (t_main_end - t_main_start )* 1e6 ); 504LOG_INF ("Total gen tokens: %6d, speed: %5.2f t/s\n" ,n_total_gen , (double ) (n_total_gen ) / (t_main_end - t_main_start )* 1e6 ); 505LOG_INF ("Total speed (AVG): %6s speed: %5.2f t/s\n" ,"" , (double ) (n_total_prompt + n_total_gen ) / (t_main_end - t_main_start )* 1e6 ); 506LOG_INF ("Cache misses: %6d\n" ,n_cache_miss ); 507 508LOG_INF ("\n" ); 509 510// TODO: print sampling/grammar timings for all clients 511llama_perf_context_print (ctx ); 512 513llama_batch_free (batch ); 514 515llama_backend_free (); 516 517LOG ("\n\n" ); 518 519return 0 ; 520}