char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
745aa5319
char/aliceai-t5-moe
1import Foundation 2import llama 3 4enum LlamaError : Error { 5case couldNotInitializeContext 6} 7 8func llama_batch_clear ( _ batch : inout llama_batch ) { 9batch . n_tokens = 0 10} 11 12func llama_batch_add ( _ batch : inout llama_batch , _ id : llama_token , _ pos : llama_pos , _ seq_ids : [ llama_seq_id ], _ logits : Bool ) { 13batch . token [ Int ( batch . n_tokens )] = id 14batch . pos [ Int ( batch . n_tokens )] = pos 15batch . n_seq_id [ Int ( batch . n_tokens )] = Int32 ( seq_ids . count ) 16for i in 0 ..< seq_ids . count { 17batch . seq_id [ Int ( batch . n_tokens )] ! [ Int ( i )] = seq_ids [ i ] 18} 19batch . logits [ Int ( batch . n_tokens )] = logits ? 1 : 0 20 21batch . n_tokens += 1 22} 23 24actorLlamaContext { 25private var model : OpaquePointer 26private var context : OpaquePointer 27private var vocab : OpaquePointer 28private var sampling : UnsafeMutablePointer < llama_sampler > 29private var batch : llama_batch 30private var tokens_list : [ llama_token ] 31var is_done : Bool = false 32 33/// This variable is used to store temporarily invalid cchars 34private var temporary_invalid_cchars : [ CChar ] 35 36var n_len : Int32 = 1024 37var n_cur : Int32 = 0 38 39var n_decode : Int32 = 0 40 41init ( model : OpaquePointer , context : OpaquePointer ) { 42self . model = model 43self . context = context 44self . tokens_list = [] 45self . batch = llama_batch_init ( 512 , 0 , 1 ) 46self . temporary_invalid_cchars = [] 47let sparams = llama_sampler_chain_default_params () 48self . sampling = llama_sampler_chain_init ( sparams ) 49llama_sampler_chain_add ( self . sampling , llama_sampler_init_temp ( 0.4 )) 50llama_sampler_chain_add ( self . sampling , llama_sampler_init_dist ( 1234 )) 51vocab = llama_model_get_vocab ( model ) 52} 53 54deinit { 55llama_sampler_free ( sampling ) 56llama_batch_free ( batch ) 57llama_model_free ( model ) 58llama_free ( context ) 59llama_backend_free () 60} 61 62static func create_context ( path : String ) throws -> LlamaContext { 63llama_backend_init () 64var model_params = llama_model_default_params () 65 66#if targetEnvironment ( simulator ) 67model_params . n_gpu_layers = 0 68( "Running on simulator, force use n_gpu_layers = 0" ) 69#endif 70let model = llama_model_load_from_file ( path , model_params ) 71guard let model else { 72( "Could not load model at \( path ) " ) 73throw LlamaError . couldNotInitializeContext 74} 75 76let n_threads = max ( 1 , min ( 8 , ProcessInfo . processInfo . processorCount - 2 )) 77( "Using \( n_threads ) threads" ) 78 79var ctx_params = llama_context_default_params () 80ctx_params . n_ctx = 2048 81ctx_params . n_threads = Int32 ( n_threads ) 82ctx_params . n_threads_batch = Int32 ( n_threads ) 83 84let context = llama_init_from_model ( model , ctx_params ) 85guard let context else { 86( "Could not load context!" ) 87throw LlamaError . couldNotInitializeContext 88} 89 90return LlamaContext ( model : model , context : context ) 91} 92 93func model_info () -> String { 94let result = UnsafeMutablePointer < Int8 > . allocate ( capacity : 256 ) 95result . initialize ( repeating : Int8 ( 0 ), count : 256 ) 96defer { 97result . deallocate () 98} 99 100// TODO: this is probably very stupid way to get the string from C 101 102let nChars = llama_model_desc ( model , result , 256 ) 103let bufferPointer = UnsafeBufferPointer ( start : result , count : Int ( nChars )) 104 105var SwiftString = "" 106for char in bufferPointer { 107SwiftString . append ( Character ( UnicodeScalar ( UInt8 ( char )))) 108} 109 110return SwiftString 111} 112 113func get_n_tokens () -> Int32 { 114return batch . n_tokens ; 115} 116 117func completion_init ( text : String ) { 118( "attempting to complete \" \( text ) \"" ) 119 120tokens_list = tokenize ( text : text , add_bos : true ) 121temporary_invalid_cchars = [] 122 123let n_ctx = llama_n_ctx ( context ) 124let n_kv_req = tokens_list . count + ( Int ( n_len ) - tokens_list . count ) 125 126( "\n n_len = \( n_len ) , n_ctx = \( n_ctx ) , n_kv_req = \( n_kv_req ) " ) 127 128if n_kv_req > n_ctx { 129( "error: n_kv_req > n_ctx, the required KV cache size is not big enough" ) 130} 131 132for id in tokens_list { 133( String ( cString : token_to_piece ( token : id ) + [ 0 ])) 134} 135 136llama_batch_clear ( & batch ) 137 138for i1 in 0 ..< tokens_list . count { 139let i = Int ( i1 ) 140llama_batch_add ( & batch , tokens_list [ i ], Int32 ( i ), [ 0 ], false ) 141} 142batch . logits [ Int ( batch . n_tokens ) - 1 ] = 1 // true 143 144if llama_decode ( context , batch ) != 0 { 145( "llama_decode() failed" ) 146} 147 148n_cur = batch . n_tokens 149} 150 151func completion_loop () -> String { 152var new_token_id : llama_token = 0 153 154new_token_id = llama_sampler_sample ( sampling , context , batch . n_tokens - 1 ) 155 156if llama_vocab_is_eog ( vocab , new_token_id ) || n_cur == n_len { 157( "\n" ) 158is_done = true 159let new_token_str = String ( cString : temporary_invalid_cchars + [ 0 ]) 160temporary_invalid_cchars . removeAll () 161return new_token_str 162} 163 164let new_token_cchars = token_to_piece ( token : new_token_id ) 165temporary_invalid_cchars . append ( contentsOf : new_token_cchars ) 166let new_token_str : String 167if let string = String ( validatingUTF8 : temporary_invalid_cchars + [ 0 ]) { 168temporary_invalid_cchars . removeAll () 169new_token_str = string 170} else if ( 0 ..< temporary_invalid_cchars . count ). contains ( where : { $0 != 0 && String ( validatingUTF8 : Array ( temporary_invalid_cchars . suffix ( $0 )) + [ 0 ]) != nil }) { 171// in this case, at least the suffix of the temporary_invalid_cchars can be interpreted as UTF8 string 172let string = String ( cString : temporary_invalid_cchars + [ 0 ]) 173temporary_invalid_cchars . removeAll () 174new_token_str = string 175} else { 176new_token_str = "" 177} 178( new_token_str ) 179// tokens_list.append(new_token_id) 180 181llama_batch_clear ( & batch ) 182llama_batch_add ( & batch , new_token_id , n_cur , [ 0 ], true ) 183 184n_decode += 1 185n_cur += 1 186 187if llama_decode ( context , batch ) != 0 { 188( "failed to evaluate llama!" ) 189} 190 191return new_token_str 192} 193 194func bench ( pp : Int , tg : Int , pl : Int , nr : Int = 1 ) -> String { 195var pp_avg : Double = 0 196var tg_avg : Double = 0 197 198var pp_std : Double = 0 199var tg_std : Double = 0 200 201for _ in 0 ..< nr { 202// bench prompt processing 203 204llama_batch_clear ( & batch ) 205 206let n_tokens = pp 207 208for i in 0 ..< n_tokens { 209llama_batch_add ( & batch , 0 , Int32 ( i ), [ 0 ], false ) 210} 211batch . logits [ Int ( batch . n_tokens ) - 1 ] = 1 // true 212 213llama_memory_clear ( llama_get_memory ( context ), false ) 214 215let t_pp_start = DispatchTime . now (). uptimeNanoseconds / 1000 ; 216 217if llama_decode ( context , batch ) != 0 { 218( "llama_decode() failed during prompt" ) 219} 220llama_synchronize ( context ) 221 222let t_pp_end = DispatchTime . now (). uptimeNanoseconds / 1000 ; 223 224// bench text generation 225 226llama_memory_clear ( llama_get_memory ( context ), false ) 227 228let t_tg_start = DispatchTime . now (). uptimeNanoseconds / 1000 ; 229 230for i in 0 ..< tg { 231llama_batch_clear ( & batch ) 232 233for j in 0 ..< pl { 234llama_batch_add ( & batch , 0 , Int32 ( i ), [ Int32 ( j )], true ) 235} 236 237if llama_decode ( context , batch ) != 0 { 238( "llama_decode() failed during text generation" ) 239} 240llama_synchronize ( context ) 241} 242 243let t_tg_end = DispatchTime . now (). uptimeNanoseconds / 1000 ; 244 245llama_memory_clear ( llama_get_memory ( context ), false ) 246 247let t_pp = Double ( t_pp_end - t_pp_start ) / 1000000.0 248let t_tg = Double ( t_tg_end - t_tg_start ) / 1000000.0 249 250let speed_pp = Double ( pp ) / t_pp 251let speed_tg = Double ( pl*tg ) / t_tg 252 253pp_avg += speed_pp 254tg_avg += speed_tg 255 256pp_std += speed_pp * speed_pp 257tg_std += speed_tg * speed_tg 258 259( "pp \( speed_pp ) t/s, tg \( speed_tg ) t/s" ) 260} 261 262pp_avg /= Double ( nr ) 263tg_avg /= Double ( nr ) 264 265if nr > 1 { 266pp_std = sqrt ( pp_std / Double ( nr - 1 ) - pp_avg * pp_avg * Double ( nr ) / Double ( nr - 1 )) 267tg_std = sqrt ( tg_std / Double ( nr - 1 ) - tg_avg * tg_avg * Double ( nr ) / Double ( nr - 1 )) 268} else { 269pp_std = 0 270tg_std = 0 271} 272 273let model_desc = model_info () ; 274let model_size = String ( format : "%.2f GiB" , Double ( llama_model_size ( model )) / 1024.0 / 1024.0 / 1024.0 ) ; 275let model_n_params = String ( format : "%.2f B" , Double ( llama_model_n_params ( model )) / 1e9 ) ; 276let backend = "Metal" ; 277let pp_avg_str = String ( format : "%.2f" , pp_avg ) ; 278let tg_avg_str = String ( format : "%.2f" , tg_avg ) ; 279let pp_std_str = String ( format : "%.2f" , pp_std ) ; 280let tg_std_str = String ( format : "%.2f" , tg_std ) ; 281 282var result = "" 283 284result += String ( "| model | size | params | backend | test | t/s |\n" ) 285result += String ( "| --- | --- | --- | --- | --- | --- |\n" ) 286result += String ( "| \( model_desc ) | \( model_size ) | \( model_n_params ) | \( backend ) | pp \( pp ) | \( pp_avg_str ) ± \( pp_std_str ) |\n" ) 287result += String ( "| \( model_desc ) | \( model_size ) | \( model_n_params ) | \( backend ) | tg \( tg ) | \( tg_avg_str ) ± \( tg_std_str ) |\n" ) 288 289return result ; 290} 291 292func clear () { 293tokens_list . removeAll () 294temporary_invalid_cchars . removeAll () 295llama_memory_clear ( llama_get_memory ( context ), true ) 296} 297 298private func tokenize ( text : String , add_bos : Bool ) -> [ llama_token ] { 299let utf8Count = text . utf8 . count 300let n_tokens = utf8Count + ( add_bos ? 1 : 0 ) + 1 301let tokens = UnsafeMutablePointer < llama_token > . allocate ( capacity : n_tokens ) 302let tokenCount = llama_tokenize ( vocab , text , Int32 ( utf8Count ), tokens , Int32 ( n_tokens ), add_bos , false ) 303 304var swiftTokens : [ llama_token ] = [] 305for i in 0 ..< tokenCount { 306swiftTokens . append ( tokens [ Int ( i )]) 307} 308 309tokens . deallocate () 310 311return swiftTokens 312} 313 314/// - note: The result does not contain null-terminator 315private func token_to_piece ( token : llama_token ) -> [ CChar ] { 316let result = UnsafeMutablePointer < Int8 > . allocate ( capacity : 8 ) 317result . initialize ( repeating : Int8 ( 0 ), count : 8 ) 318defer { 319result . deallocate () 320} 321let nTokens = llama_token_to_piece ( vocab , token , result , 8 , 0 , false ) 322 323if nTokens < 0 { 324let newResult = UnsafeMutablePointer < Int8 > . allocate ( capacity : Int ( - nTokens )) 325newResult . initialize ( repeating : Int8 ( 0 ), count : Int ( - nTokens )) 326defer { 327newResult . deallocate () 328} 329let nNewTokens = llama_token_to_piece ( vocab , token , newResult , - nTokens , 0 , false ) 330let bufferPointer = UnsafeBufferPointer ( start : newResult , count : Int ( nNewTokens )) 331return Array ( bufferPointer ) 332} else { 333let bufferPointer = UnsafeBufferPointer ( start : result , count : Int ( nTokens )) 334return Array ( bufferPointer ) 335} 336} 337}