2023-09-28 19:04:36 +03:00
// A basic application simulating a server with multiple clients.
2023-11-23 12:34:20 +01:00
// The clients submit requests to the server and they are processed in parallel.
2023-09-28 19:04:36 +03:00
2024-09-09 23:36:09 +02:00
#include "arg.h"
2023-09-28 19:04:36 +03:00
#include "common.h"
2024-09-09 23:36:09 +02:00
#include "sampling.h"
2024-09-15 20:46:12 +03:00
#include "log.h"
2023-09-28 19:04:36 +03:00
#include "llama.h"
#include <cmath>
#include <cstdio>
#include <string>
#include <vector>
2023-10-06 14:16:38 +01:00
#include <ctime>
2025-03-04 17:53:26 +01:00
#include <algorithm>
2023-09-28 19:04:36 +03:00
// trim whitespace from the beginning and end of a string
static std :: string trim ( const std :: string & str ) {
size_t start = 0 ;
size_t end = str . size ();
while ( start < end && isspace ( str [ start ])) {
start += 1 ;
}
while ( end > start && isspace ( str [ end - 1 ])) {
end -= 1 ;
}
return str . substr ( start , end - start );
}
static std :: string k_system =
R "(Transcript of a never ending dialog, where the User interacts with an Assistant.
The Assistant is helpful , kind , honest , good at writing , and never fails to answer the User ' s requests immediately and with precision .
2025-05-17 12:58:55 +03:00
User :
Recommend a nice restaurant in the area .
Assistant :
I 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 .
User :
Who is Richard Feynman ?
Assistant :
Richard 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?" .
) ";
static std :: vector < std :: string > k_questions = {
"What is the tallest mountain in the world?" ,
"Who was the first person to win two Nobel Prizes?" ,
"Which country invented paper?" ,
"What organ is primarily responsible for pumping blood throughout the body?" ,
"Which planet is known for its prominent ring system?" ,
"Who directed the movie 'Inception'?" ,
"What is the freezing point of water in Fahrenheit?" ,
"Which animal is known to have the longest lifespan?" ,
"What language has the most native speakers worldwide?" ,
"What is the capital city of Canada?" ,
"Who is credited with inventing the World Wide Web?" ,
"Which metal is liquid at room temperature?" ,
"What is the term for an animal that eats both plants and meat?" ,
"Who painted 'The Starry Night'?" ,
"What gas do humans exhale that plants use for photosynthesis?" ,
"What year did World War II end?" ,
"Which continent has the most countries?" ,
"Who wrote the novel 'Frankenstein'?" ,
"What does DNA stand for?" ,
"What is the main ingredient in traditional Japanese miso soup?"
};
static std :: vector < std :: string > k_answers = {
"The tallest mountain in the world is Mount Everest." ,
"Marie Curie was the first person to win two Nobel Prizes." ,
"Paper was invented in China." ,
"The heart is the organ responsible for pumping blood." ,
"Saturn is known for its prominent ring system." ,
"Christopher Nolan directed the movie 'Inception'." ,
"The freezing point of water in Fahrenheit is 32°F." ,
"The bowhead whale is known to have the longest lifespan among mammals." ,
"Mandarin Chinese has the most native speakers in the world." ,
"The capital city of Canada is Ottawa." ,
"Tim Berners-Lee is credited with inventing the World Wide Web." ,
"Mercury is the metal that is liquid at room temperature." ,
"An animal that eats both plants and meat is called an omnivore." ,
"'The Starry Night' was painted by Vincent van Gogh." ,
"Humans exhale carbon dioxide, which plants use in photosynthesis." ,
"World War II ended in 1945." ,
"Africa is the continent with the most countries." ,
"The novel 'Frankenstein' was written by Mary Shelley." ,
"DNA stands for Deoxyribonucleic Acid." ,
"The main ingredient in traditional Japanese miso soup is fermented soybean paste."
};
2023-09-28 19:04:36 +03:00
static std :: vector < std :: string > k_prompts = {
"What is the meaning of life?" ,
"Tell me an interesting fact about llamas." ,
"What is the best way to cook a steak?" ,
"Are you familiar with the Special Theory of Relativity and can you explain it to me?" ,
"Recommend some interesting books to read." ,
"What is the best way to learn a new language?" ,
"How to get a job at Google?" ,
"If you could have any superpower, what would it be?" ,
2025-05-17 12:58:55 +03:00
"I want to learn how to play the piano. What would be the best way to do it?" ,
2023-09-28 19:04:36 +03:00
};
struct client {
2023-10-18 16:21:57 +03:00
~ client () {
2024-09-07 15:16:19 +03:00
if ( smpl ) {
2024-10-10 22:57:42 +02:00
common_sampler_free ( smpl );
2023-10-18 16:21:57 +03:00
}
}
2023-09-28 19:04:36 +03:00
int32_t id = 0 ;
llama_seq_id seq_id = - 1 ;
llama_token sampled ;
int64_t t_start_prompt ;
int64_t t_start_gen ;
2025-05-17 12:58:55 +03:00
int32_t n_past = 0 ;
2023-09-28 19:04:36 +03:00
int32_t n_prompt = 0 ;
int32_t n_decoded = 0 ;
int32_t i_batch = - 1 ;
std :: string input ;
std :: string prompt ;
std :: string response ;
2024-10-10 22:57:42 +02:00
struct common_sampler * smpl = nullptr ;
2023-09-28 19:04:36 +03:00
};
2023-10-06 14:16:38 +01:00
static void print_date_time () {
std :: time_t current_time = std :: time ( nullptr );
std :: tm * local_time = std :: localtime ( & current_time );
char buffer [ 80 ];
strftime ( buffer , sizeof ( buffer ), "%Y-%m-%d %H:%M:%S" , local_time );
2024-09-15 20:46:12 +03:00
LOG_INF ( " \n " );
LOG_INF ( " \033 [35mrun parameters as of %s \033 [0m \n " , buffer );
LOG_INF ( " \n " );
2023-10-06 14:16:38 +01:00
}
// Define a split string function to ...
static std :: vector < std :: string > split_string ( const std :: string & input , char delimiter ) {
std :: vector < std :: string > tokens ;
std :: istringstream stream ( input );
std :: string token ;
while ( std :: getline ( stream , token , delimiter )) {
tokens . push_back ( token );
}
return tokens ;
}
2023-09-28 19:04:36 +03:00
int main ( int argc , char ** argv ) {
srand ( 1234 );
2024-10-10 22:57:42 +02:00
common_params params ;
2023-09-28 19:04:36 +03:00
2025-04-02 14:32:59 +03:00
params . n_predict = 128 ;
2025-06-01 11:42:16 +03:00
params . n_junk = 1 ;
2025-04-02 14:32:59 +03:00
2024-10-10 22:57:42 +02:00
if ( ! common_params_parse ( argc , argv , params , LLAMA_EXAMPLE_PARALLEL )) {
2023-09-28 19:04:36 +03:00
return 1 ;
}
2024-10-10 22:57:42 +02:00
common_init ();
2024-09-15 20:46:12 +03:00
2023-09-28 19:04:36 +03:00
// number of simultaneous "clients" to simulate
const int32_t n_clients = params . n_parallel ;
2024-03-08 17:31:00 -05:00
// dedicate one sequence to the system prompt
params . n_parallel += 1 ;
2023-09-28 19:04:36 +03:00
// requests to simulate
const int32_t n_seq = params . n_sequences ;
// insert new requests as soon as the previous one is done
const bool cont_batching = params . cont_batching ;
2025-05-17 12:58:55 +03:00
// is the system prompt shared in the cache
const bool is_sp_shared = params . is_pp_shared ;
// extra text to insert in each client's prompt in order to make it larger
2025-06-01 11:42:16 +03:00
const int32_t n_junk = std :: max ( 1 , params . n_junk );
2025-05-17 12:58:55 +03:00
2025-07-18 17:33:41 +03:00
// signed seed, use negative values to indicate different seeds for the different clients
const int32_t & sseed = params . sampling . seed ;
2023-09-28 19:04:36 +03:00
// init llama.cpp
2024-02-16 01:31:07 -08:00
llama_backend_init ();
llama_numa_init ( params . numa );
2023-09-28 19:04:36 +03:00
// load the target model
2025-12-14 10:11:13 +02:00
auto llama_init = common_init_from_params ( params );
2024-08-06 00:14:10 +08:00
2025-12-14 10:11:13 +02:00
auto * model = llama_init -> model ();
auto * ctx = llama_init -> context ();
2023-09-28 19:04:36 +03:00
2025-06-06 14:11:15 +03:00
auto * mem = llama_get_memory ( ctx );
2025-01-12 11:32:42 +02:00
const llama_vocab * vocab = llama_model_get_vocab ( model );
2023-10-06 14:16:38 +01:00
// load the prompts from an external file if there are any
if ( params . prompt . empty ()) {
2024-09-15 20:46:12 +03:00
LOG_INF ( " \033 [32mNo new questions so proceed with build-in defaults. \033 [0m \n " );
2023-10-06 14:16:38 +01:00
} else {
// Output each line of the input params.prompts vector and copy to k_prompts
int index = 0 ;
2024-09-15 20:46:12 +03:00
LOG_INF ( " \033 [32mNow printing the external prompt file %s \033 [0m \n\n " , params . prompt_file . c_str ());
2023-10-06 14:16:38 +01:00
std :: vector < std :: string > prompts = split_string ( params . prompt , '\n' );
for ( const auto & prompt : prompts ) {
k_prompts . resize ( index + 1 );
k_prompts [ index ] = prompt ;
index ++ ;
2024-09-15 20:46:12 +03:00
LOG_INF ( "%3d prompt: %s \n " , index , prompt . c_str ());
2023-10-06 14:16:38 +01:00
}
}
2024-09-15 20:46:12 +03:00
LOG_INF ( " \n\n " );
2023-09-28 19:04:36 +03:00
2023-10-18 16:21:57 +03:00
const int n_ctx = llama_n_ctx ( ctx );
2023-09-28 19:04:36 +03:00
2025-07-18 17:33:41 +03:00
if ( sseed >= 0 ) {
LOG_INF ( "%s: initializing all samplers with the same RNG seed: %d (use a negative seed to have different seeds) \n " , __func__ , sseed );
} else {
LOG_INF ( "%s: initializing samplers with different RNG seeds, starting from %d \n " , __func__ , sseed );
}
2023-09-28 19:04:36 +03:00
std :: vector < client > clients ( n_clients );
for ( size_t i = 0 ; i < clients . size (); ++ i ) {
auto & client = clients [ i ];
client . id = i ;
2024-11-25 09:58:41 +02:00
client . smpl = common_sampler_init ( model , params . sampling );
2025-07-18 17:33:41 +03:00
if ( sseed < 0 ) {
params . sampling . seed -- ;
}
2023-09-28 19:04:36 +03:00
}
std :: vector < llama_token > tokens_system ;
2025-05-17 12:58:55 +03:00
2024-10-10 22:57:42 +02:00
tokens_system = common_tokenize ( ctx , k_system , true );
2023-09-28 19:04:36 +03:00
const int32_t n_tokens_system = tokens_system . size ();
llama_seq_id g_seq_id = 0 ;
// the max batch size is as large as the context to handle cases where we get very long input prompt from multiple
// users. regardless of the size, the main loop will chunk the batch into a maximum of params.n_batch tokens at a time
2023-10-18 16:21:57 +03:00
llama_batch batch = llama_batch_init ( n_ctx , 0 , 1 );
2023-09-28 19:04:36 +03:00
int32_t n_total_prompt = 0 ;
int32_t n_total_gen = 0 ;
int32_t n_cache_miss = 0 ;
const auto t_main_start = ggml_time_us ();
2024-09-15 20:46:12 +03:00
LOG_INF ( "%s: Simulating parallel requests from clients: \n " , __func__ );
LOG_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 );
LOG_INF ( " \n " );
2023-09-28 19:04:36 +03:00
2025-05-17 12:58:55 +03:00
if ( is_sp_shared ) {
2024-09-15 20:46:12 +03:00
LOG_INF ( "%s: Evaluating the system prompt ... \n " , __func__ );
2023-09-28 19:04:36 +03:00
2023-10-18 16:21:57 +03:00
for ( int32_t i = 0 ; i < n_tokens_system ; ++ i ) {
2024-10-10 22:57:42 +02:00
common_batch_add ( batch , tokens_system [ i ], i , { 0 }, false );
2023-09-28 19:04:36 +03:00
}
2023-09-28 21:42:38 +02:00
if ( llama_decode ( ctx , batch ) != 0 ) {
2024-09-15 20:46:12 +03:00
LOG_ERR ( "%s: llama_decode() failed \n " , __func__ );
2023-09-28 19:04:36 +03:00
return 1 ;
}
// assign the system KV cache to all parallel sequences
2024-03-08 17:31:00 -05:00
for ( int32_t i = 1 ; i <= n_clients ; ++ i ) {
2025-06-06 14:11:15 +03:00
llama_memory_seq_cp ( mem , 0 , i , - 1 , - 1 );
2023-09-28 19:04:36 +03:00
}
2024-09-15 20:46:12 +03:00
LOG_INF ( " \n " );
2023-09-28 19:04:36 +03:00
}
2024-09-15 20:46:12 +03:00
LOG_INF ( "Processing requests ... \n\n " );
2023-09-28 19:04:36 +03:00
while ( true ) {
2024-10-10 22:57:42 +02:00
common_batch_clear ( batch );
2023-09-28 19:04:36 +03:00
// decode any currently ongoing sequences
for ( auto & client : clients ) {
if ( client . seq_id == - 1 ) {
continue ;
}
client . i_batch = batch . n_tokens ;
2025-05-17 12:58:55 +03:00
common_batch_add ( batch , client . sampled , client . n_past ++ , { client . id + 1 }, true );
2023-10-18 16:21:57 +03:00
client . n_decoded += 1 ;
2023-09-28 19:04:36 +03:00
}
if ( batch . n_tokens == 0 ) {
// all sequences have ended - clear the entire KV cache
2024-03-08 17:31:00 -05:00
for ( int i = 1 ; i <= n_clients ; ++ i ) {
2025-06-06 14:11:15 +03:00
llama_memory_seq_rm ( mem , i , - 1 , - 1 );
2024-03-08 17:31:00 -05:00
// but keep the system prompt
2025-06-06 14:11:15 +03:00
llama_memory_seq_cp ( mem , 0 , i , - 1 , - 1 );
2023-09-28 19:04:36 +03:00
}
2024-09-15 20:46:12 +03:00
LOG_INF ( "%s: clearing the KV cache \n " , __func__ );
2023-09-28 19:04:36 +03:00
}
// insert new sequences for decoding
if ( cont_batching || batch . n_tokens == 0 ) {
for ( auto & client : clients ) {
if ( client . seq_id == - 1 && g_seq_id < n_seq ) {
client . seq_id = g_seq_id ;
client . t_start_prompt = ggml_time_us ();
client . t_start_gen = 0 ;
client . input = k_prompts [ rand () % k_prompts . size ()];
client . response = "" ;
2025-05-17 12:58:55 +03:00
// construct the prompt:
// [system prompt] + [junk] + [user prompt]
client . n_past = 0 ;
client . prompt = "" ;
if ( is_sp_shared ) {
client . n_past = n_tokens_system ;
} else {
client . prompt += k_system ;
}
2025-05-30 19:38:07 +03:00
const int n_junk_cur = rand () % n_junk ;
for ( int i = 0 ; i < n_junk_cur ; ++ i ) {
2025-05-17 12:58:55 +03:00
const int r = rand () % k_questions . size ();
client . prompt += "User: \n " + k_questions [ r ] + " \n Assistant: \n " + k_answers [ r ] + " \n " ;
}
client . prompt += "User: \n " + client . input + " \n Assistant: \n " ;
2024-10-10 22:57:42 +02:00
common_sampler_reset ( client . smpl );
2023-09-28 19:04:36 +03:00
// do not prepend BOS because we have a system prompt!
std :: vector < llama_token > tokens_prompt ;
2024-10-10 22:57:42 +02:00
tokens_prompt = common_tokenize ( ctx , client . prompt , false );
2023-09-28 19:04:36 +03:00
for ( size_t i = 0 ; i < tokens_prompt . size (); ++ i ) {
2025-05-17 12:58:55 +03:00
common_batch_add ( batch , tokens_prompt [ i ], client . n_past ++ , { client . id + 1 }, false );
2023-09-28 19:04:36 +03:00
}
// extract the logits only for the last token
if ( batch . n_tokens > 0 ) {
batch . logits [ batch . n_tokens - 1 ] = true ;
}
client . n_prompt = tokens_prompt . size ();
client . n_decoded = 0 ;
client . i_batch = batch . n_tokens - 1 ;
2025-07-16 16:35:42 +03:00
LOG_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 );
2023-09-28 19:04:36 +03:00
g_seq_id += 1 ;
// insert new requests one-by-one
//if (cont_batching) {
// break;
//}
}
}
}
if ( batch . n_tokens == 0 ) {
break ;
}
// process in chunks of params.n_batch
int32_t n_batch = params . n_batch ;
2025-05-31 10:24:04 +03:00
int32_t i_next = 0 ;
for ( int32_t i = 0 ; i < batch . n_tokens ; i = i_next ) {
2023-09-28 19:04:36 +03:00
// experiment: process in powers of 2
//if (i + n_batch > (int32_t) batch.n_tokens && n_batch > 32) {
// n_batch /= 2;
// i -= n_batch;
// continue;
//}
2025-05-31 10:24:04 +03:00
const int32_t n_tokens = std :: min ( n_batch , batch . n_tokens - i );
2023-09-28 19:04:36 +03:00
llama_batch batch_view = {
n_tokens ,
2023-10-18 16:21:57 +03:00
batch . token + i ,
2023-09-28 19:04:36 +03:00
nullptr ,
2023-10-18 16:21:57 +03:00
batch . pos + i ,
batch . n_seq_id + i ,
batch . seq_id + i ,
batch . logits + i ,
2023-09-28 19:04:36 +03:00
};
2023-09-28 21:42:38 +02:00
const int ret = llama_decode ( ctx , batch_view );
2023-09-28 19:04:36 +03:00
if ( ret != 0 ) {
if ( n_batch == 1 || ret < 0 ) {
// if you get here, it means the KV cache is full - try increasing it via the context size
2024-09-15 20:46:12 +03:00
LOG_ERR ( "%s : failed to decode the batch, n_batch = %d, ret = %d \n " , __func__ , n_batch , ret );
2023-09-28 19:04:36 +03:00
return 1 ;
}
2025-05-31 12:55:57 +03:00
LOG_WRN ( "%s : failed to decode the batch, retrying with n_batch = %d \n " , __func__ , n_batch / 2 );
2023-09-28 19:04:36 +03:00
n_cache_miss += 1 ;
// retry with half the batch size to try to find a free slot in the KV cache
n_batch /= 2 ;
continue ;
}
2024-09-15 20:46:12 +03:00
LOG_DBG ( "%s : decoded batch of %d tokens \n " , __func__ , n_tokens );
2023-09-28 19:04:36 +03:00
2025-05-31 10:24:04 +03:00
// move the head of the batch forward with the number of tokens we just processed
i_next = i + n_tokens ;
// on successful decode, restore the original batch size
n_batch = params . n_batch ;
2023-09-28 19:04:36 +03:00
for ( auto & client : clients ) {
if ( client . i_batch < ( int ) i || client . i_batch >= ( int ) ( i + n_tokens )) {
continue ;
}
//printf("client %d, seq %d, token %d, pos %d, batch %d\n",
// client.id, client.seq_id, client.sampled, client.n_decoded, client.i_batch);
2024-10-10 22:57:42 +02:00
const llama_token id = common_sampler_sample ( client . smpl , ctx , client . i_batch - i );
2023-10-18 16:21:57 +03:00
2024-10-10 22:57:42 +02:00
common_sampler_accept ( client . smpl , id , true );
2023-09-28 19:04:36 +03:00
if ( client . n_decoded == 1 ) {
// start measuring generation time after the first token to make sure all concurrent clients
// have their prompt already processed
client . t_start_gen = ggml_time_us ();
}
2024-10-10 22:57:42 +02:00
const std :: string token_str = common_token_to_piece ( ctx , id );
2023-10-18 16:21:57 +03:00
2023-09-28 19:04:36 +03:00
client . response += token_str ;
client . sampled = id ;
//printf("client %d, seq %d, token %d, pos %d, batch %d: %s\n",
// client.id, client.seq_id, id, client.n_decoded, client.i_batch, token_str.c_str());
if ( client . n_decoded > 2 &&
2025-05-17 12:58:55 +03:00
( llama_vocab_is_eog ( vocab , id ) ||
( params . n_predict > 0 && client . n_decoded >= params . n_predict ) ||
client . response . find ( "User:" ) != std :: string :: npos )) {
2023-09-28 19:04:36 +03:00
// basic reverse prompt
const size_t pos = client . response . find ( "User:" );
if ( pos != std :: string :: npos ) {
client . response = client . response . substr ( 0 , pos );
}
// delete only the generated part of the sequence, i.e. keep the system prompt in the cache
2025-06-06 14:11:15 +03:00
llama_memory_seq_rm ( mem , client . id + 1 , - 1 , - 1 );
llama_memory_seq_cp ( mem , 0 , client . id + 1 , - 1 , - 1 );
2023-09-28 19:04:36 +03:00
const auto t_main_end = ggml_time_us ();
2024-09-15 20:46:12 +03:00
LOG_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\n Input: %s \n\033 [35mResponse: %s \033 [0m \n\n " ,
2023-10-06 14:16:38 +01:00
client . id , client . seq_id , n_seq , client . n_prompt , client . n_decoded ,
2023-09-28 19:04:36 +03:00
( t_main_end - client . t_start_prompt ) / 1e6 ,
( double ) ( client . n_prompt + client . n_decoded ) / ( t_main_end - client . t_start_prompt ) * 1e6 ,
n_cache_miss ,
:: trim ( client . input ). c_str (),
:: trim ( client . response ). c_str ());
n_total_prompt += client . n_prompt ;
n_total_gen += client . n_decoded ;
2023-10-18 16:21:57 +03:00
2023-09-28 19:04:36 +03:00
client . seq_id = - 1 ;
}
client . i_batch = - 1 ;
}
}
}
const auto t_main_end = ggml_time_us ();
2023-10-06 14:16:38 +01:00
print_date_time ();
2024-09-15 20:46:12 +03:00
LOG_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 );
2023-10-06 14:16:38 +01:00
if ( params . prompt_file . empty ()) {
params . prompt_file = "used built-in defaults" ;
}
2024-09-15 20:46:12 +03:00
LOG_INF ( "External prompt file: \033 [32m%s \033 [0m \n " , params . prompt_file . c_str ());
2025-04-01 23:44:05 +02:00
LOG_INF ( "Model and path used: \033 [32m%s \033 [0m \n\n " , params . model . path . c_str ());
2023-10-06 14:16:38 +01:00
2024-09-15 20:46:12 +03:00
LOG_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 );
LOG_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 );
LOG_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 );
LOG_INF ( "Cache misses: %6d \n " , n_cache_miss );
2023-09-28 19:04:36 +03:00
2024-09-15 20:46:12 +03:00
LOG_INF ( " \n " );
2023-09-28 19:04:36 +03:00
2024-09-07 15:16:19 +03:00
// TODO: print sampling/grammar timings for all clients
2024-09-13 09:53:38 +03:00
llama_perf_context_print ( ctx );
2023-09-28 19:04:36 +03:00
llama_batch_free ( batch );
llama_backend_free ();
2024-09-15 20:46:12 +03:00
LOG ( " \n\n " );
2023-09-28 19:04:36 +03:00
return 0 ;
}