2025-01-03 10:18:53 +02:00
#include "llama-context.h"
2026-04-09 16:42:19 +02:00
#include "ggml.h"
2025-11-28 12:02:56 +01:00
#include "llama-arch.h"
2026-05-16 20:06:23 +08:00
#include "llama-graph.h"
2025-01-12 11:32:42 +02:00
#include "llama-impl.h"
2025-06-13 13:47:55 +03:00
#include "llama-batch.h"
2025-03-13 12:35:44 +02:00
#include "llama-io.h"
2025-06-05 15:29:22 +03:00
#include "llama-memory.h"
2025-01-12 11:32:42 +02:00
#include "llama-mmap.h"
2025-03-13 12:35:44 +02:00
#include "llama-model.h"
2026-03-12 13:26:00 +01:00
#include "llama-ext.h"
2026-04-09 16:42:19 +02:00
#include "llama.h"
2025-01-12 11:32:42 +02:00
2025-03-13 12:35:44 +02:00
#include <cinttypes>
2025-12-14 08:34:56 +02:00
#include <cmath>
2025-05-31 10:24:04 +03:00
#include <cstring>
#include <limits>
#include <stdexcept>
2026-07-08 18:18:09 +08:00
#include <string>
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
//
// llama_context
//
2025-01-03 10:18:53 +02:00
2026-05-16 20:06:23 +08:00
static llm_graph_type ctx_type_to_graph_type ( llama_context_type ctx_type ) {
switch ( ctx_type ) {
case LLAMA_CONTEXT_TYPE_DEFAULT : return LLM_GRAPH_TYPE_DEFAULT ;
case LLAMA_CONTEXT_TYPE_MTP : return LLM_GRAPH_TYPE_DECODER_MTP ;
}
throw std :: runtime_error ( "Unsupported ctx type" );
}
2026-07-08 18:18:09 +08:00
struct llm_fused_op_probe {
llm_fused_op op ;
const char * name ;
uint32_t n_tokens_per_seq ;
};
static const llm_fused_op_probe llm_fused_op_flash_attn_probe = {
/*.op =*/ LLM_FUSED_OP_FLASH_ATTN ,
/*.name =*/ "Flash Attention" ,
/*.n_tokens_per_seq =*/ 1 ,
};
static const llm_fused_op_probe llm_fused_op_gdn_ar_probe = {
/*.op =*/ LLM_FUSED_OP_GDN_AR ,
/*.name =*/ "fused Gated Delta Net (autoregressive)" ,
/*.n_tokens_per_seq =*/ 1 ,
};
static const llm_fused_op_probe llm_fused_op_gdn_ch_probe = {
/*.op =*/ LLM_FUSED_OP_GDN_CH ,
/*.name =*/ "fused Gated Delta Net (chunked)" ,
/*.n_tokens_per_seq =*/ 16 ,
};
2026-07-11 11:39:07 +02:00
static const llm_fused_op_probe llm_fused_op_lid_probe = {
/*.op =*/ LLM_FUSED_OP_LIGHTNING_INDEXER ,
/*.name =*/ "Lightning Indexer" ,
/*.n_tokens_per_seq =*/ 1 ,
};
2026-07-17 00:33:33 +08:00
static const llm_fused_op_probe llm_fused_op_dsv4_hc_pre_probe = {
/*.op =*/ LLM_FUSED_OP_DSV4_HC_PRE ,
/*.name =*/ "fused DeepSeek V4 HC pre" ,
/*.n_tokens_per_seq =*/ 1 ,
};
static const llm_fused_op_probe llm_fused_op_dsv4_hc_comb_probe = {
/*.op =*/ LLM_FUSED_OP_DSV4_HC_COMB ,
/*.name =*/ "fused DeepSeek V4 HC comb" ,
/*.n_tokens_per_seq =*/ 1 ,
};
static const llm_fused_op_probe llm_fused_op_dsv4_hc_post_probe = {
/*.op =*/ LLM_FUSED_OP_DSV4_HC_POST ,
/*.name =*/ "fused DeepSeek V4 HC post" ,
/*.n_tokens_per_seq =*/ 1 ,
};
2025-03-13 12:35:44 +02:00
llama_context :: llama_context (
const llama_model & model ,
llama_context_params params ) :
2025-06-13 13:47:55 +03:00
model ( model ),
2026-02-16 09:21:11 +02:00
cvec ( std :: make_unique < llama_adapter_cvec > ()),
loras ( std :: make_unique < llama_adapter_loras > ()),
2025-06-20 10:14:14 +03:00
balloc ( std :: make_unique < llama_batch_allocr > ( model . hparams . n_pos_per_embd ())) {
2025-11-06 14:05:47 +01:00
// TODO warning when creating llama_context with awkward ctx size that is not a power of 2,
// may need to be backend-dependent
2025-03-13 12:35:44 +02:00
LLAMA_LOG_INFO ( "%s: constructing llama_context \n " , __func__ );
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
t_start_us = model . t_start_us ;
t_load_us = model . t_load_us ;
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
const auto & hparams = model . hparams ;
2025-01-03 10:18:53 +02:00
2025-05-25 16:34:36 +03:00
cparams . n_seq_max = std :: max ( 1u , params . n_seq_max );
2025-06-15 10:08:58 +03:00
if ( cparams . n_seq_max > LLAMA_MAX_SEQ ) {
throw std :: runtime_error ( "n_seq_max must be <= " + std :: to_string ( LLAMA_MAX_SEQ ));
2025-05-25 16:34:36 +03:00
}
2026-05-16 20:06:23 +08:00
cparams . n_rs_seq = params . n_rs_seq ;
if ( cparams . n_rs_seq > 0 && ! llm_arch_supports_rs_rollback ( model . arch )) {
LLAMA_LOG_DEBUG ( "%s: n_rs_seq=%u requested but model arch does not support recurrent partial rollback; clamping to 0 \n " ,
__func__ , cparams . n_rs_seq );
cparams . n_rs_seq = 0 ;
}
2026-06-04 01:29:09 +08:00
cparams . n_threads = params . n_threads ;
cparams . n_threads_batch = params . n_threads_batch ;
cparams . yarn_ext_factor = params . yarn_ext_factor >= 0.0f ? params . yarn_ext_factor : hparams . yarn_ext_factor ;
cparams . yarn_attn_factor = params . yarn_attn_factor >= 0.0f ? params . yarn_attn_factor : hparams . yarn_attn_factor ;
cparams . yarn_beta_fast = params . yarn_beta_fast >= 0.0f ? params . yarn_beta_fast : hparams . yarn_beta_fast ;
cparams . yarn_beta_slow = params . yarn_beta_slow >= 0.0f ? params . yarn_beta_slow : hparams . yarn_beta_slow ;
cparams . embeddings = params . embeddings ;
cparams . embeddings_nextn = false ;
cparams . embeddings_nextn_masked = false ;
cparams . offload_kqv = params . offload_kqv ;
cparams . no_perf = params . no_perf ;
cparams . warmup = false ;
2026-08-02 20:55:34 +08:00
// +1: id n_layer() taps the output of the last layer ("input" of the head)
cparams . embeddings_layer_inp . resize ( hparams . n_layer () + 1 , false );
embd_layer_inp . resize ( hparams . n_layer () + 1 );
2026-06-12 09:21:06 +02:00
2026-06-07 20:50:54 +08:00
cparams . ctx_type = params . ctx_type ;
cparams . pooling_type = params . pooling_type ;
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
cparams . n_ctx = params . n_ctx == 0 ? hparams . n_ctx_train : params . n_ctx ;
cparams . rope_freq_base = params . rope_freq_base == 0.0f ? hparams . rope_freq_base_train : params . rope_freq_base ;
cparams . rope_freq_scale = params . rope_freq_scale == 0.0f ? hparams . rope_freq_scale_train : params . rope_freq_scale ;
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
cparams . n_ctx_orig_yarn = params . yarn_orig_ctx != 0 ? params . yarn_orig_ctx :
hparams . n_ctx_orig_yarn != 0 ? hparams . n_ctx_orig_yarn :
hparams . n_ctx_train ;
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
cparams . cb_eval = params . cb_eval ;
cparams . cb_eval_user_data = params . cb_eval_user_data ;
2025-01-03 10:18:53 +02:00
2026-06-07 20:50:54 +08:00
cparams . ctx_other = nullptr ;
// TODO: more generic
if ( model . arch == LLM_ARCH_GEMMA4_ASSISTANT ) {
if ( params . ctx_other == nullptr ) {
// TODO: change from runtime_error to llama_exception to avoid printing error message
2026-06-12 09:21:06 +02:00
throw std :: runtime_error ( "Gemma4Assistant requires ctx_other to be set (this warning is normal during memory fitting)" );
2026-06-07 20:50:54 +08:00
}
cparams . ctx_other = params . ctx_other ;
}
2026-05-16 20:06:23 +08:00
2026-06-28 15:01:34 +02:00
if ( model . arch == LLM_ARCH_EAGLE3 || model . arch == LLM_ARCH_DFLASH ) {
2026-06-12 09:21:06 +02:00
if ( model . tok_embd == nullptr || model . output == nullptr ) {
if ( params . ctx_other == nullptr ) {
2026-06-28 15:01:34 +02:00
throw std :: runtime_error ( model . arch_name () + " requires ctx_other to be set (this warning is normal during memory fitting)" );
2026-06-12 09:21:06 +02:00
}
cparams . ctx_other = params . ctx_other ;
}
}
2026-01-04 21:22:16 +01:00
// Initialize backend samplers here so they are part of the sampling graph
// before the reserve passes run later in this function. This avoids a later
// re-reserve when graph nodes change.
if ( params . samplers != nullptr && params . n_samplers > 0 ) {
for ( size_t i = 0 ; i < params . n_samplers ; ++ i ) {
const auto & config = params . samplers [ i ];
if ( llama_sampler_chain_get ( config . sampler , - 1 ) == nullptr ) {
throw std :: runtime_error ( "the backend samplers must be of type llama_sampler_chain" );
}
if ( set_sampler ( config . seq_id , config . sampler )) {
const int n_samplers = llama_sampler_chain_n ( config . sampler );
LLAMA_LOG_INFO ( "%s: setting backend sampler for seq_id %d (n = %d) \n " , __func__ , config . seq_id , n_samplers );
}
}
}
2025-03-13 12:35:44 +02:00
auto rope_scaling_type = params . rope_scaling_type ;
if ( rope_scaling_type == LLAMA_ROPE_SCALING_TYPE_UNSPECIFIED ) {
rope_scaling_type = hparams . rope_scaling_type_train ;
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
if ( rope_scaling_type == LLAMA_ROPE_SCALING_TYPE_NONE ) {
cparams . rope_freq_scale = 1.0f ; // never scale if scaling type is none
}
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
if ( cparams . yarn_ext_factor < 0.0f ) { // negative indicates 'not set'
cparams . yarn_ext_factor = rope_scaling_type == LLAMA_ROPE_SCALING_TYPE_YARN ? 1.0f : 0.0f ;
}
2025-12-14 08:34:56 +02:00
if ( cparams . yarn_ext_factor != 0 ) {
static auto get_mscale = []( float scale , float mscale ) {
return scale <= 1.0f ? 1.0f : ( 0.1f * mscale * logf ( scale ) + 1.0f );
};
const float factor = 1.0f / cparams . rope_freq_scale ;
// ref: https://github.com/huggingface/transformers/blob/6d00f6b0a5679c36510f203e4226e36f517c3032/src/transformers/modeling_rope_utils.py#L336-L348
if ( hparams . rope_yarn_log_mul != 0.0f ) {
// note: here we assume `mscale == 1.0f`
// TODO: start reading the actual value of mscale and handle the case where it is not 1.0f
float mscale = 1.0f ;
const float mscale_all_dims = hparams . rope_yarn_log_mul ;
// [TAG_DEEPSEEK2_YARN_LOG_MUL_FIX]
// special-case DEEPSEEK v2:
// https://huggingface.co/deepseek-ai/DeepSeek-V2-Lite-Chat/blob/main/config.json#L42-L43
if ( model . arch == LLM_ARCH_DEEPSEEK2 && mscale_all_dims != 1.0f ) {
mscale = mscale_all_dims ;
}
cparams . yarn_attn_factor = get_mscale ( factor , mscale ) / get_mscale ( factor , mscale_all_dims );
LLAMA_LOG_WARN ( "%s: setting new yarn_attn_factor = %.4f (mscale == %.1f, mscale_all_dim = %.1f) \n " ,
__func__ , cparams . yarn_attn_factor , mscale , mscale_all_dims );
} else {
cparams . yarn_attn_factor = get_mscale ( factor , 1.0f );
}
// when YARN is applied with yarn_ext_factor != 0.0f, we need to cancel this factor:
// https://github.com/ggml-org/llama.cpp/blob/a81a569577cc38b32558958b048228150be63eae/ggml/src/ggml-cpu/ops.cpp#L5541-L5544
//
// ref: https://github.com/ggml-org/llama.cpp/discussions/7416
// https://github.com/ggml-org/llama.cpp/pull/17945
cparams . yarn_attn_factor *= 1.0f / ( 1.0f + 0.1f * logf ( factor ));
}
2025-03-13 12:35:44 +02:00
cparams . yarn_attn_factor *= hparams . rope_attn_factor ;
if ( cparams . pooling_type == LLAMA_POOLING_TYPE_UNSPECIFIED ) {
if ( hparams . pooling_type == LLAMA_POOLING_TYPE_UNSPECIFIED ) {
cparams . pooling_type = LLAMA_POOLING_TYPE_NONE ;
} else {
cparams . pooling_type = hparams . pooling_type ;
}
}
if ( params . attention_type == LLAMA_ATTENTION_TYPE_UNSPECIFIED ) {
cparams . causal_attn = hparams . causal_attn ;
2025-01-03 10:18:53 +02:00
} else {
2025-03-13 12:35:44 +02:00
cparams . causal_attn = params . attention_type == LLAMA_ATTENTION_TYPE_CAUSAL ;
}
2025-08-30 16:32:10 +02:00
cparams . flash_attn = params . flash_attn_type != LLAMA_FLASH_ATTN_TYPE_DISABLED ;
2026-01-15 16:39:17 +02:00
cparams . auto_fa = params . flash_attn_type == LLAMA_FLASH_ATTN_TYPE_AUTO ;
2025-08-30 16:32:10 +02:00
2026-03-07 15:41:10 +08:00
cparams . fused_gdn_ar = true ;
2026-03-11 22:46:40 +02:00
cparams . fused_gdn_ch = true ;
cparams . auto_fgdn = true ;
2026-03-07 15:41:10 +08:00
2026-07-11 11:39:07 +02:00
cparams . fused_lid = true ;
cparams . auto_flid = true ;
2026-07-17 00:33:33 +08:00
cparams . fused_dsv4_hc_pre = true ;
cparams . fused_dsv4_hc_comb = true ;
cparams . fused_dsv4_hc_post = true ;
cparams . auto_fhc = true ;
2025-03-13 12:35:44 +02:00
// with causal attention, the batch size is limited by the context size
cparams . n_batch = cparams . causal_attn ? std :: min ( cparams . n_ctx , params . n_batch ) : params . n_batch ;
cparams . n_ubatch = std :: min ( cparams . n_batch , params . n_ubatch == 0 ? params . n_batch : params . n_ubatch );
2025-05-20 08:05:46 +03:00
2026-06-12 09:21:06 +02:00
cparams . n_outputs_max = params . n_outputs_max == 0 || llama_model_has_encoder ( & model ) ? cparams . n_batch : params . n_outputs_max ;
2026-06-01 23:01:38 +08:00
2025-05-11 20:18:39 +08:00
cparams . op_offload = params . op_offload ;
2025-07-16 16:35:42 +03:00
cparams . kv_unified = params . kv_unified ;
2026-03-05 08:50:21 +01:00
// initialized later
2026-01-15 16:39:17 +02:00
cparams . pipeline_parallel = false ;
2025-08-01 06:38:12 +03:00
{
const char * LLAMA_GRAPH_REUSE_DISABLE = getenv ( "LLAMA_GRAPH_REUSE_DISABLE" );
graph_reuse_disable = LLAMA_GRAPH_REUSE_DISABLE ? ( atoi ( LLAMA_GRAPH_REUSE_DISABLE ) != 0 ) : graph_reuse_disable ;
if ( graph_reuse_disable ) {
LLAMA_LOG_WARN ( "%s: graph reuse disabled \n " , __func__ );
}
}
2025-11-07 20:03:25 +02:00
// ref: https://github.com/ggml-org/llama.cpp/pull/17046#discussion_r2503085732
cparams . n_ctx = GGML_PAD ( cparams . n_ctx , 256 );
2025-11-02 18:14:04 +02:00
if ( cparams . kv_unified ) {
cparams . n_ctx_seq = cparams . n_ctx ;
} else {
cparams . n_ctx_seq = cparams . n_ctx / cparams . n_seq_max ;
2025-11-07 20:03:25 +02:00
cparams . n_ctx_seq = GGML_PAD ( cparams . n_ctx_seq , 256 );
2025-11-02 18:14:04 +02:00
if ( cparams . n_ctx_seq == 0 ) {
throw std :: runtime_error ( "n_ctx_seq == 0" );
}
if ( cparams . n_ctx != cparams . n_ctx_seq * cparams . n_seq_max ) {
cparams . n_ctx = cparams . n_ctx_seq * cparams . n_seq_max ;
LLAMA_LOG_WARN ( "%s: n_ctx is not divisible by n_seq_max - rounding down to %u \n " , __func__ , cparams . n_ctx );
}
}
2025-03-13 12:35:44 +02:00
LLAMA_LOG_INFO ( "%s: n_seq_max = %u \n " , __func__ , cparams . n_seq_max );
LLAMA_LOG_INFO ( "%s: n_ctx = %u \n " , __func__ , cparams . n_ctx );
2025-11-02 18:14:04 +02:00
LLAMA_LOG_INFO ( "%s: n_ctx_seq = %u \n " , __func__ , cparams . n_ctx_seq );
2025-03-13 12:35:44 +02:00
LLAMA_LOG_INFO ( "%s: n_batch = %u \n " , __func__ , cparams . n_batch );
LLAMA_LOG_INFO ( "%s: n_ubatch = %u \n " , __func__ , cparams . n_ubatch );
LLAMA_LOG_INFO ( "%s: causal_attn = %d \n " , __func__ , cparams . causal_attn );
2025-08-30 16:32:10 +02:00
LLAMA_LOG_INFO ( "%s: flash_attn = %s \n " , __func__ , llama_flash_attn_type_name ( params . flash_attn_type ));
2025-07-16 16:35:42 +03:00
LLAMA_LOG_INFO ( "%s: kv_unified = %s \n " , __func__ , cparams . kv_unified ? "true" : "false" );
2025-03-13 12:35:44 +02:00
LLAMA_LOG_INFO ( "%s: freq_base = %.1f \n " , __func__ , cparams . rope_freq_base );
LLAMA_LOG_INFO ( "%s: freq_scale = %g \n " , __func__ , cparams . rope_freq_scale );
2026-05-16 20:06:23 +08:00
LLAMA_LOG_INFO ( "%s: n_rs_seq = %u \n " , __func__ , cparams . n_rs_seq );
2026-06-01 22:26:58 +03:00
LLAMA_LOG_INFO ( "%s: n_outputs_max = %u \n " , __func__ , cparams . n_outputs_max );
2025-03-13 12:35:44 +02:00
2025-11-02 18:14:04 +02:00
if ( cparams . n_ctx_seq < hparams . n_ctx_train ) {
2026-06-28 08:52:15 +03:00
LLAMA_LOG_INFO ( "%s: n_ctx_seq (%u) < n_ctx_train (%u) -- the full capacity of the model will not be utilized \n " ,
2025-11-02 18:14:04 +02:00
__func__ , cparams . n_ctx_seq , hparams . n_ctx_train );
2025-03-13 12:35:44 +02:00
}
2025-11-02 18:14:04 +02:00
if ( cparams . n_ctx_seq > hparams . n_ctx_train ) {
LLAMA_LOG_WARN ( "%s: n_ctx_seq (%u) > n_ctx_train (%u) -- possible training context overflow \n " ,
__func__ , cparams . n_ctx_seq , hparams . n_ctx_train );
2025-03-13 12:35:44 +02:00
}
if ( ! hparams . vocab_only ) {
// GPU backends
2026-04-09 16:42:19 +02:00
for ( const auto & dev : model . devices ) {
ggml_backend_t backend = ggml_backend_dev_init ( dev . dev , nullptr );
2025-03-13 12:35:44 +02:00
if ( backend == nullptr ) {
2026-04-09 16:42:19 +02:00
throw std :: runtime_error ( format ( "failed to initialize %s backend" , ggml_backend_dev_name ( dev . dev )));
2025-03-13 12:35:44 +02:00
}
backends . emplace_back ( backend );
}
// add ACCEL backends (such as BLAS)
for ( size_t i = 0 ; i < ggml_backend_dev_count (); ++ i ) {
ggml_backend_dev_t dev = ggml_backend_dev_get ( i );
if ( ggml_backend_dev_type ( dev ) == GGML_BACKEND_DEVICE_TYPE_ACCEL ) {
ggml_backend_t backend = ggml_backend_dev_init ( dev , nullptr );
if ( backend == nullptr ) {
throw std :: runtime_error ( format ( "failed to initialize %s backend" , ggml_backend_dev_name ( dev )));
}
backends . emplace_back ( backend );
}
}
// add CPU backend
backend_cpu = ggml_backend_init_by_type ( GGML_BACKEND_DEVICE_TYPE_CPU , nullptr );
if ( backend_cpu == nullptr ) {
throw std :: runtime_error ( "failed to initialize CPU backend" );
}
backends . emplace_back ( backend_cpu );
// create a list of the set_n_threads functions in the backends
for ( auto & backend : backends ) {
ggml_backend_dev_t dev = ggml_backend_get_device ( backend . get ());
ggml_backend_reg_t reg = dev ? ggml_backend_dev_backend_reg ( dev ) : nullptr ;
if ( reg ) {
auto ggml_backend_set_n_threads_fn = ( ggml_backend_set_n_threads_t ) ggml_backend_reg_get_proc_address ( reg , "ggml_backend_set_n_threads" );
if ( ggml_backend_set_n_threads_fn ) {
set_n_threads_fns . emplace_back ( backend . get (), ggml_backend_set_n_threads_fn );
}
}
}
llama_set_abort_callback ( this , params . abort_callback , params . abort_callback_data );
// graph outputs buffer
{
2026-01-28 05:59:30 +01:00
if ( output_reserve ( params . n_seq_max ) < params . n_seq_max ) {
2025-03-13 12:35:44 +02:00
throw std :: runtime_error ( "failed to reserve initial output buffer" );
}
LLAMA_LOG_INFO ( "%s: %10s output buffer size = %8.2f MiB \n " , __func__ ,
ggml_backend_buffer_name ( buf_output . get ()),
ggml_backend_buffer_get_size ( buf_output . get ()) / 1024.0 / 1024.0 );
}
}
// init the memory module
if ( ! hparams . vocab_only ) {
2025-05-02 17:48:36 +03:00
llama_memory_params params_mem = {
2026-06-07 20:50:54 +08:00
/*.type_k =*/ params . type_k ,
/*.type_v =*/ params . type_v ,
/*.swa_full =*/ params . swa_full ,
/*.ctx_type =*/ cparams . ctx_type ,
/*.mem_other =*/ llama_get_memory ( cparams . ctx_other ),
2025-05-02 17:48:36 +03:00
};
2025-03-13 12:35:44 +02:00
2025-05-02 17:48:36 +03:00
memory . reset ( model . create_memory ( params_mem , cparams ));
2025-03-13 12:35:44 +02:00
}
// init backends
if ( ! hparams . vocab_only ) {
LLAMA_LOG_DEBUG ( "%s: enumerating backends \n " , __func__ );
backend_buft . clear ();
backend_ptrs . clear ();
2025-12-15 09:24:59 +01:00
backend_buf_exp_size . clear ();
2025-03-13 12:35:44 +02:00
for ( auto & backend : backends ) {
auto * buft = ggml_backend_get_default_buffer_type ( backend . get ());
auto backend_type = ggml_backend_dev_type ( ggml_backend_get_device ( backend . get ()));
if ( backend_type == GGML_BACKEND_DEVICE_TYPE_CPU && ! model . devices . empty ()) {
// use the host buffer of the first device CPU for faster transfer of the intermediate state
2026-04-09 16:42:19 +02:00
const auto & dev = model . devices [ 0 ];
auto * host_buft = ggml_backend_dev_host_buffer_type ( dev . dev );
2025-03-13 12:35:44 +02:00
if ( host_buft ) {
buft = host_buft ;
}
}
backend_buft . push_back ( buft );
backend_ptrs . push_back ( backend . get ());
2025-12-15 09:24:59 +01:00
backend_buf_exp_size . push_back ( 0 );
2025-03-13 12:35:44 +02:00
}
LLAMA_LOG_DEBUG ( "%s: backend_ptrs.size() = %zu \n " , __func__ , backend_ptrs . size ());
// TODO: move these checks to ggml_backend_sched
// enabling pipeline parallelism in the scheduler increases memory usage, so it is only done when necessary
bool pipeline_parallel =
model . n_devices () > 1 &&
2026-06-06 06:06:47 +02:00
model . n_gpu_layers () > model . hparams . n_layer_all &&
2025-12-27 20:18:35 +01:00
model . split_mode () == LLAMA_SPLIT_MODE_LAYER &&
2025-04-02 14:52:01 +02:00
cparams . offload_kqv &&
! model . has_tensor_overrides ();
2025-03-13 12:35:44 +02:00
// pipeline parallelism requires support for async compute and events in all devices
if ( pipeline_parallel ) {
for ( auto & backend : backends ) {
auto dev_type = ggml_backend_dev_type ( ggml_backend_get_device ( backend . get ()));
if ( dev_type == GGML_BACKEND_DEVICE_TYPE_CPU ) {
// ignore CPU backend
2026-02-02 14:29:44 +02:00
// TODO: should we ignore ACCEL types too?
2025-03-13 12:35:44 +02:00
continue ;
}
auto * dev = ggml_backend_get_device ( backend . get ());
ggml_backend_dev_props props ;
ggml_backend_dev_get_props ( dev , & props );
if ( ! props . caps . async || ! props . caps . events ) {
// device does not support async compute or events
pipeline_parallel = false ;
break ;
}
}
}
2026-01-15 16:39:17 +02:00
cparams . pipeline_parallel = pipeline_parallel ;
2025-03-13 12:35:44 +02:00
2026-01-15 16:39:17 +02:00
if ( cparams . pipeline_parallel ) {
2026-01-15 19:35:57 +02:00
LLAMA_LOG_INFO ( "%s: pipeline parallelism enabled \n " , __func__ );
2025-03-13 12:35:44 +02:00
}
2026-01-15 16:39:17 +02:00
sched_reserve ();
if ( ! cparams . flash_attn ) {
if ( ggml_is_quantized ( params . type_v )) {
throw std :: runtime_error ( "quantized V cache was requested, but this requires Flash Attention" );
2025-08-31 06:49:03 -07:00
}
}
2025-01-03 10:18:53 +02:00
}
2026-01-04 21:22:16 +01:00
// Initialize the full vocabulary token ids for backend samplers.
{
const int n_vocab = model . vocab . n_tokens ();
sampling . token_ids_full_vocab . resize ( n_vocab );
for ( int i = 0 ; i < n_vocab ; ++ i ) {
sampling . token_ids_full_vocab [ i ] = i ;
}
}
2025-01-03 10:18:53 +02:00
}
2025-05-12 14:44:49 +02:00
llama_context ::~ llama_context () {
2026-07-31 00:48:00 +08:00
// wait for any pending asynchronous copies into the output buffers before they are freed
synchronize ();
2025-12-22 11:00:37 +01:00
if ( ! model . hparams . no_alloc ) {
for ( size_t i = 0 ; i < backend_ptrs . size (); ++ i ) {
ggml_backend_t backend = backend_ptrs [ i ];
ggml_backend_buffer_type_t buft = backend_buft [ i ];
2025-12-15 09:24:59 +01:00
2025-12-22 11:00:37 +01:00
const size_t size_exp = backend_buf_exp_size [ i ];
const size_t size_act = ggml_backend_sched_get_buffer_size ( sched . get (), backend );
if ( size_exp == size_act ) {
LLAMA_LOG_DEBUG ( "%s: %10s compute buffer size is %8.4f MiB, matches expectation of %8.4f MiB \n " ,
__func__ , ggml_backend_buft_name ( buft ), size_act / ( 1024.0 * 1024.0 ), size_exp / ( 1024.0 * 1024.0 ));
} else {
LLAMA_LOG_WARN ( "%s: %10s compute buffer size of %8.4f MiB, does not match expectation of %8.4f MiB \n " ,
__func__ , ggml_backend_buft_name ( buft ), size_act / ( 1024.0 * 1024.0 ), size_exp / ( 1024.0 * 1024.0 ));
}
}
}
2025-05-12 14:44:49 +02:00
ggml_opt_free ( opt_ctx );
}
2025-03-13 12:35:44 +02:00
2026-07-08 18:18:09 +08:00
void llama_context :: resolve_fused_ops ( const llama_memory_context_i * mctx , uint32_t n_seqs ) {
const char * func = __func__ ;
auto resolve = [ & ]( const llm_fused_op_probe & probe , bool & enabled ) {
if ( ! enabled ) {
return ;
}
const uint32_t n_tokens_probe = probe . n_tokens_per_seq * n_seqs ;
auto * gf = graph_reserve ( n_tokens_probe , n_seqs , n_tokens_probe , mctx , true );
if ( ! gf ) {
throw std :: runtime_error ( std :: string ( "failed to reserve graph for " ) + probe . name + " check" );
}
bool device_mismatch = false ;
for ( const auto & node : get_gf_res_reserve () -> get_fused_nodes ()) {
if ( node . op != probe . op ) {
continue ;
}
GGML_ASSERT ( node . il >= 0 );
ggml_backend_t backend_fused = ggml_backend_sched_get_tensor_backend ( sched . get (), node . tensor );
ggml_backend_dev_t device_fused = backend_fused ? ggml_backend_get_device ( backend_fused ) : nullptr ;
// TODO: make this descriptor-specific; model.dev_layer() preserves the current behavior,
// but is still wrong for cases like --no-kv-offload.
ggml_backend_dev_t device_layer = model . dev_layer ( node . il );
if ( device_fused != device_layer ) {
LLAMA_LOG_WARN ( "%s: layer %d is assigned to device %s but %s "
"is assigned to device %s (usually due to missing support) \n " ,
func , node . il ,
device_layer ? ggml_backend_dev_name ( device_layer ) : "none" ,
probe . name ,
device_fused ? ggml_backend_dev_name ( device_fused ) : "none" );
device_mismatch = true ;
break ;
}
}
if ( device_mismatch ) {
enabled = false ;
LLAMA_LOG_WARN ( "%s: %s not supported, set to disabled \n " , func , probe . name );
} else {
enabled = true ;
LLAMA_LOG_INFO ( "%s: %s enabled \n " , func , probe . name );
}
};
if ( cparams . auto_fa ) {
resolve ( llm_fused_op_flash_attn_probe , cparams . flash_attn );
cparams . auto_fa = false ;
}
if ( cparams . auto_fgdn ) {
LLAMA_LOG_INFO ( "%s: resolving fused Gated Delta Net support: \n " , func );
resolve ( llm_fused_op_gdn_ar_probe , cparams . fused_gdn_ar );
resolve ( llm_fused_op_gdn_ch_probe , cparams . fused_gdn_ch );
cparams . auto_fgdn = false ;
}
2026-07-11 11:39:07 +02:00
if ( cparams . auto_flid ) {
LLAMA_LOG_INFO ( "%s: resolving fused Lightning Indexer support: \n " , func );
resolve ( llm_fused_op_lid_probe , cparams . fused_lid );
cparams . auto_flid = false ;
}
2026-07-17 00:33:33 +08:00
if ( cparams . auto_fhc ) {
LLAMA_LOG_INFO ( "%s: resolving fused DeepSeek V4 HC support: \n " , func );
resolve ( llm_fused_op_dsv4_hc_pre_probe , cparams . fused_dsv4_hc_pre );
resolve ( llm_fused_op_dsv4_hc_comb_probe , cparams . fused_dsv4_hc_comb );
resolve ( llm_fused_op_dsv4_hc_post_probe , cparams . fused_dsv4_hc_post );
cparams . auto_fhc = false ;
}
2026-07-08 18:18:09 +08:00
}
2026-01-15 16:39:17 +02:00
void llama_context :: sched_reserve () {
if ( ! sched_need_reserve ) {
return ;
}
sched_need_reserve = false ;
LLAMA_LOG_INFO ( "%s: reserving ... \n " , __func__ );
synchronize ();
const int64_t t_start_us = ggml_time_us ();
const uint32_t n_seqs = cparams . n_seq_max ;
const uint32_t n_tokens = std :: min ( cparams . n_ctx , cparams . n_ubatch );
const size_t max_nodes = this -> graph_max_nodes ( n_tokens );
LLAMA_LOG_DEBUG ( "%s: max_nodes = %zu \n " , __func__ , max_nodes );
gf_res_prev . reset ( new llm_graph_result ( max_nodes ));
gf_res_reserve . reset ( new llm_graph_result ( max_nodes ));
sched . reset ( ggml_backend_sched_new ( backend_ptrs . data (), backend_buft . data (), backend_ptrs . size (), max_nodes , cparams . pipeline_parallel , cparams . op_offload ));
llama_memory_context_ptr mctx ;
if ( memory ) {
LLAMA_LOG_DEBUG ( "%s: reserving full memory module \n " , __func__ );
mctx = memory -> init_full ();
if ( ! mctx ) {
throw std :: runtime_error ( "failed to initialize memory module" );
}
}
// avoid reserving graphs with zero outputs - assume one output per sequence
const int n_outputs = n_seqs ;
LLAMA_LOG_DEBUG ( "%s: worst-case: n_tokens = %d, n_seqs = %d, n_outputs = %d \n " , __func__ , n_tokens , n_seqs , n_outputs );
2026-07-08 18:18:09 +08:00
resolve_fused_ops ( mctx . get (), n_seqs );
2026-03-07 15:41:10 +08:00
2026-01-15 16:39:17 +02:00
// reserve worst-case graph
int n_splits_pp = - 1 ;
int n_nodes_pp = - 1 ;
int n_splits_tg = - 1 ;
int n_nodes_tg = - 1 ;
2026-06-01 23:01:38 +08:00
const uint32_t n_outputs_pp = std :: min ( n_tokens , cparams . n_outputs_max );
2026-01-15 16:39:17 +02:00
// reserve pp (prompt processing) graph first so that buffers are only allocated once
{
2026-06-01 23:01:38 +08:00
auto * gf = graph_reserve ( n_tokens , n_seqs , n_outputs_pp , mctx . get (),
2026-01-15 16:39:17 +02:00
model . hparams . no_alloc , model . hparams . no_alloc ? backend_buf_exp_size . data () : nullptr );
if ( ! gf ) {
if ( cparams . pipeline_parallel ) {
LLAMA_LOG_WARN ( "%s: compute buffer allocation failed, retrying without pipeline parallelism \n " , __func__ );
cparams . pipeline_parallel = false ;
sched . reset ( ggml_backend_sched_new ( backend_ptrs . data (), backend_buft . data (), backend_ptrs . size (), max_nodes , false , cparams . op_offload ));
2026-06-01 23:01:38 +08:00
gf = graph_reserve ( n_tokens , n_seqs , n_outputs_pp , mctx . get ());
2026-01-15 16:39:17 +02:00
}
if ( ! gf ) {
throw std :: runtime_error ( "failed to allocate compute pp buffers" );
}
}
n_splits_pp = ggml_backend_sched_get_n_splits ( sched . get ());
n_nodes_pp = ggml_graph_n_nodes ( gf );
}
// reserve with tg (token generation) graph to get the number of splits and nodes
{
auto * gf = graph_reserve ( n_seqs , n_seqs , n_seqs , mctx . get (), model . hparams . no_alloc );
if ( ! gf ) {
throw std :: runtime_error ( "failed to allocate compute tg buffers" );
}
n_splits_tg = ggml_backend_sched_get_n_splits ( sched . get ());
n_nodes_tg = ggml_graph_n_nodes ( gf );
}
// reserve again with pp graph to avoid ggml-alloc reallocations during inference
{
2026-03-31 12:50:51 +01:00
// TODO: not sure if the following graph would be worst case for multi-stream KV caches:
2026-01-15 16:39:17 +02:00
//
// auto * gf = graph_reserve(n_tokens, 1, n_tokens, mctx.get());
//
2026-06-01 23:01:38 +08:00
auto * gf = graph_reserve ( n_tokens , n_seqs , n_outputs_pp , mctx . get (), model . hparams . no_alloc );
2026-01-15 16:39:17 +02:00
if ( ! gf ) {
throw std :: runtime_error ( "failed to allocate compute pp buffers" );
}
}
for ( size_t i = 0 ; i < backend_ptrs . size (); ++ i ) {
ggml_backend_t backend = backend_ptrs [ i ];
ggml_backend_buffer_type_t buft = backend_buft [ i ];
if ( ! model . hparams . no_alloc ) {
backend_buf_exp_size [ i ] = ggml_backend_sched_get_buffer_size ( sched . get (), backend );
}
if ( backend_buf_exp_size [ i ] > 1 ) {
LLAMA_LOG_INFO ( "%s: %10s compute buffer size = %8.2f MiB \n " , __func__ ,
ggml_backend_buft_name ( buft ),
backend_buf_exp_size [ i ] / 1024.0 / 1024.0 );
}
}
if ( n_nodes_pp == n_nodes_tg ) {
LLAMA_LOG_INFO ( "%s: graph nodes = %d \n " , __func__ , n_nodes_pp );
} else {
LLAMA_LOG_INFO ( "%s: graph nodes = %d (with bs=%d), %d (with bs=1) \n " , __func__ , n_nodes_pp , n_tokens , n_nodes_tg );
}
if ( n_splits_pp == n_splits_tg ) {
LLAMA_LOG_INFO ( "%s: graph splits = %d \n " , __func__ , n_splits_pp );
} else {
LLAMA_LOG_INFO ( "%s: graph splits = %d (with bs=%d), %d (with bs=1) \n " , __func__ , n_splits_pp , n_tokens , n_splits_tg );
}
const int64_t t_end_us = ggml_time_us ();
2026-01-15 19:35:57 +02:00
LLAMA_LOG_INFO ( "%s: reserve took %.2f ms, sched copies = %d \n " ,
__func__ , ( t_end_us - t_start_us ) / 1000.0 , ggml_backend_sched_get_n_copies ( sched . get ()));
2026-01-15 16:39:17 +02:00
}
2025-03-13 12:35:44 +02:00
void llama_context :: synchronize () {
2026-01-15 16:39:17 +02:00
if ( ! sched ) {
return ;
}
2025-03-13 12:35:44 +02:00
ggml_backend_sched_synchronize ( sched . get ());
// FIXME: if multiple single tokens are evaluated without a synchronization,
// the stats will be added to the prompt evaluation stats
// this should only happen when using batch size 1 to evaluate a batch
// add the evaluation to the stats
if ( n_queued_tokens == 1 ) {
if ( ! cparams . no_perf ) {
t_eval_us += ggml_time_us () - t_compute_start_us ;
}
n_eval ++ ;
} else if ( n_queued_tokens > 1 ) {
if ( ! cparams . no_perf ) {
t_p_eval_us += ggml_time_us () - t_compute_start_us ;
}
n_p_eval += n_queued_tokens ;
}
// get a more accurate load time, upon first eval
if ( n_queued_tokens > 0 && ! has_evaluated_once ) {
t_load_us = ggml_time_us () - t_start_us ;
has_evaluated_once = true ;
}
n_queued_tokens = 0 ;
t_compute_start_us = 0 ;
}
const llama_model & llama_context :: get_model () const {
return model ;
}
2025-05-02 17:48:36 +03:00
const llama_cparams & llama_context :: get_cparams () const {
return cparams ;
}
ggml_backend_sched_t llama_context :: get_sched () const {
return sched . get ();
}
2025-03-13 12:35:44 +02:00
uint32_t llama_context :: n_ctx () const {
return cparams . n_ctx ;
}
2025-11-02 18:14:04 +02:00
uint32_t llama_context :: n_ctx_seq () const {
return cparams . n_ctx_seq ;
2025-03-13 12:35:44 +02:00
}
uint32_t llama_context :: n_batch () const {
return cparams . n_batch ;
}
uint32_t llama_context :: n_ubatch () const {
return cparams . n_ubatch ;
}
uint32_t llama_context :: n_seq_max () const {
return cparams . n_seq_max ;
}
uint32_t llama_context :: n_threads () const {
return cparams . n_threads ;
}
uint32_t llama_context :: n_threads_batch () const {
return cparams . n_threads_batch ;
}
2025-06-05 15:29:22 +03:00
llama_memory_t llama_context :: get_memory () const {
return memory . get ();
2025-03-13 12:35:44 +02:00
}
2025-08-21 19:13:45 +03:00
bool llama_context :: memory_update ( bool optimize ) {
2025-05-31 10:24:04 +03:00
if ( ! memory ) {
2025-05-31 12:55:57 +03:00
return false ;
2025-05-31 10:24:04 +03:00
}
2025-03-13 12:35:44 +02:00
2025-06-04 18:58:20 +03:00
{
2025-06-21 08:03:46 +03:00
const auto mctx = memory -> init_update ( this , optimize );
switch ( mctx -> get_status ()) {
2025-06-04 18:58:20 +03:00
case LLAMA_MEMORY_STATUS_SUCCESS :
{
// noop
} break ;
case LLAMA_MEMORY_STATUS_NO_UPDATE :
{
// no updates need to be performed
return false ;
}
case LLAMA_MEMORY_STATUS_FAILED_PREPARE :
case LLAMA_MEMORY_STATUS_FAILED_COMPUTE :
{
LLAMA_LOG_ERROR ( "%s: failed to prepare memory update \n " , __func__ );
return false ;
}
}
2025-07-17 19:08:33 +03:00
// reset the previous graph result to make sure that it won't be reused
// TODO: change the mctx->apply() to return information if a graph reserve is needed
// reset the graph result only if the memory module did reset the scheduler
gf_res_prev -> reset ();
2025-06-21 08:03:46 +03:00
if ( ! mctx -> apply ()) {
2025-06-04 18:58:20 +03:00
LLAMA_LOG_ERROR ( "%s: failed to apply memory update \n " , __func__ );
}
2025-01-03 10:18:53 +02:00
}
2025-05-31 12:55:57 +03:00
2025-06-05 15:29:22 +03:00
// if the memory module did any computation, we have to reserve a new worst-case graph
{
2025-06-21 08:03:46 +03:00
const auto mctx = memory -> init_full ();
if ( ! mctx ) {
throw std :: runtime_error ( "failed to initialize memory context" );
2025-06-05 15:29:22 +03:00
}
2025-05-31 12:55:57 +03:00
2025-11-28 07:33:23 -08:00
const uint32_t n_seqs = cparams . n_seq_max ;
2025-06-05 15:29:22 +03:00
const uint32_t n_tokens = std :: min ( cparams . n_ctx , cparams . n_ubatch );
2025-05-31 12:55:57 +03:00
2026-06-01 23:01:38 +08:00
const uint32_t n_outputs_max = std :: min ( n_tokens , cparams . n_outputs_max );
auto * gf = graph_reserve ( n_tokens , n_seqs , n_outputs_max , mctx . get ());
2025-06-05 15:29:22 +03:00
if ( ! gf ) {
LLAMA_LOG_ERROR ( "%s: failed to reserve graph after the memory update \n " , __func__ );
}
2025-05-31 12:55:57 +03:00
}
return true ;
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
enum llama_pooling_type llama_context :: pooling_type () const {
return cparams . pooling_type ;
}
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
float * llama_context :: get_logits () {
2025-07-24 16:31:48 +03:00
output_reorder ();
2026-02-11 05:38:13 +01:00
return logits . data ;
2025-03-13 12:35:44 +02:00
}
2026-01-04 21:22:16 +01:00
int64_t llama_context :: output_resolve_row ( int32_t i ) const {
int64_t j = - 1 ;
// support negative indices (last output row)
if ( i < 0 ) {
j = n_outputs + i ;
if ( j < 0 ) {
throw std :: runtime_error ( format ( "negative index out of range [0, %d)" , n_outputs ));
}
} else if (( size_t ) i >= output_ids . size ()) {
throw std :: runtime_error ( format ( "out of range [0, %zu)" , output_ids . size ()));
} else {
// use output_ids to translate the batch token index into a row number
// that holds this token's data.
j = output_ids [ i ];
}
if ( j < 0 ) {
// the batch token was not configured to output anything
throw std :: runtime_error ( format ( "batch.logits[%d] != true" , i ));
}
if ( j >= n_outputs ) {
throw std :: runtime_error ( format ( "corrupt output buffer (j=%" PRId64 ", n_outputs=%d)" , j , n_outputs ));
}
return j ;
}
2025-03-13 12:35:44 +02:00
float * llama_context :: get_logits_ith ( int32_t i ) {
2025-07-24 16:31:48 +03:00
output_reorder ();
2025-03-13 12:35:44 +02:00
try {
2026-02-11 05:38:13 +01:00
if ( logits . data == nullptr ) {
2025-03-13 12:35:44 +02:00
throw std :: runtime_error ( "no logits" );
}
2026-02-19 09:48:08 +01:00
const int64_t j = output_resolve_row ( i );
2026-02-11 05:38:13 +01:00
return logits . data + j * model . vocab . n_tokens ();
2025-03-13 12:35:44 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: invalid logits id %d, reason: %s \n " , __func__ , i , err . what ());
#ifndef NDEBUG
GGML_ABORT ( "fatal error" );
#else
return nullptr ;
#endif
}
}
float * llama_context :: get_embeddings () {
2025-07-24 16:31:48 +03:00
output_reorder ();
2026-02-11 05:38:13 +01:00
return embd . data ;
2025-03-13 12:35:44 +02:00
}
2026-01-04 21:22:16 +01:00
llama_token * llama_context :: get_sampled_tokens () const {
2026-02-11 05:38:13 +01:00
return sampling . sampled . data ;
2026-01-04 21:22:16 +01:00
}
2025-03-13 12:35:44 +02:00
float * llama_context :: get_embeddings_ith ( int32_t i ) {
2025-07-24 16:31:48 +03:00
output_reorder ();
2025-03-13 12:35:44 +02:00
try {
2026-02-11 05:38:13 +01:00
if ( embd . data == nullptr ) {
2025-03-13 12:35:44 +02:00
throw std :: runtime_error ( "no embeddings" );
}
2026-02-19 09:48:08 +01:00
const int64_t j = output_resolve_row ( i );
2026-01-25 15:48:56 +02:00
const uint32_t n_embd_out = model . hparams . n_embd_out ();
2026-02-11 05:38:13 +01:00
return embd . data + j * n_embd_out ;
2025-03-13 12:35:44 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: invalid embeddings id %d, reason: %s \n " , __func__ , i , err . what ());
#ifndef NDEBUG
GGML_ABORT ( "fatal error" );
#else
return nullptr ;
#endif
}
}
float * llama_context :: get_embeddings_seq ( llama_seq_id seq_id ) {
auto it = embd_seq . find ( seq_id );
if ( it == embd_seq . end ()) {
return nullptr ;
}
return it -> second . data ();
}
2026-06-04 01:29:09 +08:00
float * llama_context :: get_embeddings_nextn () {
2026-05-16 20:06:23 +08:00
output_reorder ();
2026-06-04 01:29:09 +08:00
return embd_nextn . data ;
2026-05-16 20:06:23 +08:00
}
2026-06-04 01:29:09 +08:00
float * llama_context :: get_embeddings_nextn_ith ( int32_t i ) {
2026-05-16 20:06:23 +08:00
output_reorder ();
try {
2026-06-04 01:29:09 +08:00
if ( embd_nextn . data == nullptr ) {
throw std :: runtime_error ( "no nextn embeddings" );
2026-05-16 20:06:23 +08:00
}
2026-06-07 20:50:54 +08:00
const uint32_t n_embd = model . hparams . n_embd_out ();
2026-05-17 23:30:25 +08:00
2026-06-04 01:29:09 +08:00
if ( ! cparams . embeddings_nextn_masked ) {
// unmasked: nextn rows are stored densely, indexed by raw token position.
if ( i < 0 || ( size_t )( i + 1 ) * n_embd > embd_nextn . size ) {
throw std :: runtime_error ( format ( "out of range [0, %zu)" , embd_nextn . size / n_embd ));
2026-05-17 23:30:25 +08:00
}
2026-06-04 01:29:09 +08:00
return embd_nextn . data + ( size_t ) i * n_embd ;
2026-05-17 23:30:25 +08:00
}
const int64_t j = output_resolve_row ( i );
2026-06-04 01:29:09 +08:00
return embd_nextn . data + j * n_embd ;
2026-05-16 20:06:23 +08:00
} catch ( const std :: exception & err ) {
2026-06-04 01:29:09 +08:00
LLAMA_LOG_ERROR ( "%s: invalid nextn embeddings id %d, reason: %s \n " , __func__ , i , err . what ());
2026-05-16 20:06:23 +08:00
#ifndef NDEBUG
GGML_ABORT ( "fatal error" );
#else
return nullptr ;
#endif
}
}
2026-06-12 09:21:06 +02:00
float * llama_context :: get_embeddings_layer_inp ( uint32_t lid ) {
output_reorder ();
GGML_ASSERT ( lid < embd_layer_inp . size () && embd_layer_inp [ lid ]. has_data ());
return embd_layer_inp [ lid ]. data ;
}
2026-01-04 21:22:16 +01:00
llama_token llama_context :: get_sampled_token_ith ( int32_t idx ) {
output_reorder ();
2026-02-11 05:38:13 +01:00
if ( ! sampling . sampled . has_data ()) {
2026-01-04 21:22:16 +01:00
return LLAMA_TOKEN_NULL ;
}
try {
const int64_t row = output_resolve_row ( idx );
2026-02-11 05:38:13 +01:00
GGML_ASSERT ( row < ( int64_t ) sampling . sampled . size );
return sampling . sampled . data [ row ];
2026-01-04 21:22:16 +01:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: invalid backend sampled token id %d, reason: %s \n " , __func__ , idx , err . what ());
return LLAMA_TOKEN_NULL ;
}
}
float * llama_context :: get_sampled_probs_ith ( int32_t idx ) {
output_reorder ();
2026-02-11 05:38:13 +01:00
if ( ! sampling . probs . has_data ()) {
2026-01-04 21:22:16 +01:00
return nullptr ;
}
try {
const int64_t row = output_resolve_row ( idx );
if (( size_t ) row >= sampling . probs_count . size () || sampling . probs_count [ row ] == 0 ) {
return nullptr ;
}
2026-02-11 05:38:13 +01:00
return sampling . probs . data + row * model . vocab . n_tokens ();
2026-01-04 21:22:16 +01:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: invalid backend sampled probs id %d, reason: %s \n " , __func__ , idx , err . what ());
return nullptr ;
}
}
float * llama_context :: get_sampled_logits_ith ( int32_t idx ) {
output_reorder ();
2026-02-11 05:38:13 +01:00
if ( ! sampling . logits . has_data ()) {
2026-01-04 21:22:16 +01:00
return nullptr ;
}
try {
const int64_t row = output_resolve_row ( idx );
if (( size_t ) row >= sampling . logits_count . size () || sampling . logits_count [ row ] == 0 ) {
return nullptr ;
}
2026-02-11 05:38:13 +01:00
return sampling . logits . data + row * model . vocab . n_tokens ();
2026-01-04 21:22:16 +01:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: invalid backend sampled logits id %d, reason: %s \n " , __func__ , idx , err . what ());
return nullptr ;
}
}
const llama_token * llama_context :: get_sampled_candidates_ith ( int32_t idx ) {
output_reorder ();
try {
const int64_t row = output_resolve_row ( idx );
2026-02-11 05:38:13 +01:00
if ( sampling . candidates . has_data () &&
2026-01-04 21:22:16 +01:00
( size_t ) row < sampling . candidates_count . size () &&
sampling . candidates_count [ row ] > 0 ) {
2026-02-11 05:38:13 +01:00
return sampling . candidates . data + row * model . vocab . n_tokens ();
2026-01-04 21:22:16 +01:00
}
} catch ( const std :: exception & err ) {
// fallback to full vocab list
2026-02-15 14:57:40 +02:00
GGML_UNUSED ( err );
2026-01-04 21:22:16 +01:00
}
return sampling . token_ids_full_vocab . data ();
}
size_t llama_context :: get_sampled_candidates_count ( int32_t idx ) {
output_reorder ();
2026-02-11 05:38:13 +01:00
if ( ! sampling . candidates . has_data ()) {
2026-01-04 21:22:16 +01:00
return 0 ;
}
try {
const int64_t row = output_resolve_row ( idx );
if (( size_t ) row >= sampling . candidates_count . size ()) {
return 0 ;
}
return sampling . candidates_count [ row ];
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: invalid backend sampled candidates count id %d, reason: %s \n " , __func__ , idx , err . what ());
return 0 ;
}
}
size_t llama_context :: get_sampled_logits_count ( int32_t idx ) {
output_reorder ();
2026-02-11 05:38:13 +01:00
if ( ! sampling . logits . has_data ()) {
2026-01-04 21:22:16 +01:00
return model . vocab . n_tokens ();
}
try {
const int64_t row = output_resolve_row ( idx );
if (( size_t ) row >= sampling . logits_count . size ()) {
return 0 ;
}
return sampling . logits_count [ row ];
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: invalid backend sampled logits count id %d, reason: %s \n " , __func__ , idx , err . what ());
return 0 ;
}
}
size_t llama_context :: get_sampled_probs_count ( int32_t idx ) {
output_reorder ();
2026-02-11 05:38:13 +01:00
if ( ! sampling . probs . has_data ()) {
2026-01-04 21:22:16 +01:00
return 0 ;
}
try {
const int64_t row = output_resolve_row ( idx );
if (( size_t ) row >= sampling . probs_count . size ()) {
return 0 ;
}
return sampling . probs_count [ row ];
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: invalid backend sampled probs count id %d, reason: %s \n " , __func__ , idx , err . what ());
return 0 ;
}
}
2025-03-13 12:35:44 +02:00
void llama_context :: attach_threadpool (
ggml_threadpool_t threadpool ,
ggml_threadpool_t threadpool_batch ) {
LLAMA_LOG_DEBUG ( "%s: call \n " , __func__ );
this -> threadpool = threadpool ;
this -> threadpool_batch = threadpool_batch ? threadpool_batch : threadpool ;
}
void llama_context :: detach_threadpool () {
LLAMA_LOG_DEBUG ( "%s: call \n " , __func__ );
this -> threadpool = nullptr ;
this -> threadpool_batch = nullptr ;
}
void llama_context :: set_n_threads ( int32_t n_threads , int32_t n_threads_batch ) {
LLAMA_LOG_DEBUG ( "%s: n_threads = %d, n_threads_batch = %d \n " , __func__ , n_threads , n_threads_batch );
cparams . n_threads = n_threads ;
cparams . n_threads_batch = n_threads_batch ;
}
void llama_context :: set_abort_callback ( bool ( * abort_callback )( void * data ), void * abort_callback_data ) {
LLAMA_LOG_DEBUG ( "%s: call \n " , __func__ );
this -> abort_callback = abort_callback ;
this -> abort_callback_data = abort_callback_data ;
for ( auto & backend : backends ) {
auto * reg = ggml_backend_dev_backend_reg ( ggml_backend_get_device ( backend . get ()));
2026-04-09 16:42:19 +02:00
if ( reg ) {
auto * set_abort_callback_fn = ( ggml_backend_set_abort_callback_t ) ggml_backend_reg_get_proc_address ( reg , "ggml_backend_set_abort_callback" );
if ( set_abort_callback_fn ) {
set_abort_callback_fn ( backend . get (), this -> abort_callback , this -> abort_callback_data );
}
2025-03-13 12:35:44 +02:00
}
}
}
void llama_context :: set_embeddings ( bool value ) {
LLAMA_LOG_DEBUG ( "%s: value = %d \n " , __func__ , value );
cparams . embeddings = value ;
2026-01-15 16:39:17 +02:00
// TODO: not sure yet if we want to reserve here
//sched_need_reserve = true;
2025-03-13 12:35:44 +02:00
}
2026-06-04 01:29:09 +08:00
void llama_context :: set_embeddings_nextn ( bool value , bool masked ) {
2026-05-17 23:30:25 +08:00
LLAMA_LOG_DEBUG ( "%s: value = %d, masked = %d \n " , __func__ , value , masked );
2026-05-16 20:06:23 +08:00
2026-06-04 01:29:09 +08:00
cparams . embeddings_nextn = value ;
cparams . embeddings_nextn_masked = masked ;
2026-05-16 20:06:23 +08:00
}
2026-06-12 09:21:06 +02:00
void llama_context :: set_embeddings_layer_inp ( uint32_t lid , bool enable ) {
LLAMA_LOG_DEBUG ( "%s: lid = %d, enable = %d \n " , __func__ , lid , enable );
2026-08-02 20:55:34 +08:00
GGML_ASSERT ( lid <= model . hparams . n_layer ());
2026-06-12 09:21:06 +02:00
cparams . embeddings_layer_inp [ lid ] = enable ;
// note: without this reserve, the draft acceptance drops to zero. not sure why - this is unexpected
sched_need_reserve = true ;
}
2026-06-21 16:33:18 +08:00
void llama_context :: set_nextn_layer_offset ( int32_t offset ) {
cparams . nextn_layer_offset = offset ;
}
2025-03-13 12:35:44 +02:00
void llama_context :: set_causal_attn ( bool value ) {
LLAMA_LOG_DEBUG ( "%s: value = %d \n " , __func__ , value );
2026-01-15 16:39:17 +02:00
if ( cparams . causal_attn == value ) {
return ;
}
2025-03-13 12:35:44 +02:00
cparams . causal_attn = value ;
2026-01-15 16:39:17 +02:00
sched_need_reserve = true ;
2025-03-13 12:35:44 +02:00
}
2025-03-14 13:47:05 +01:00
void llama_context :: set_warmup ( bool value ) {
LLAMA_LOG_DEBUG ( "%s: value = %d \n " , __func__ , value );
2026-01-15 16:39:17 +02:00
if ( cparams . warmup == value ) {
return ;
}
2025-03-14 13:47:05 +01:00
cparams . warmup = value ;
2026-01-15 16:39:17 +02:00
2026-01-15 19:35:57 +02:00
// warmups are usually with small batches, so no need to reserve
//sched_need_reserve = true;
2025-03-14 13:47:05 +01:00
}
2026-01-04 21:22:16 +01:00
bool llama_context :: set_sampler ( llama_seq_id seq_id , llama_sampler * sampler ) {
2026-01-15 16:39:17 +02:00
if ( ! sampler && sampling . samplers . count ( seq_id ) == 0 ) {
return true ;
}
2026-01-04 21:22:16 +01:00
LLAMA_LOG_DEBUG ( "%s: seq_id = %d, sampler = %p \n " , __func__ , ( int ) seq_id , ( void * ) sampler );
2026-05-20 22:34:45 +05:30
if ( sampler && model . split_mode () == LLAMA_SPLIT_MODE_TENSOR ) {
static bool warned = false ;
if ( ! warned ) {
LLAMA_LOG_WARN ( "%s: backend sampling not supported with SPLIT_MODE_TENSOR; using CPU \n " , __func__ );
warned = true ;
}
if ( sampling . samplers . count ( seq_id ) > 0 ) {
sched_need_reserve = true ;
}
sampling . samplers . erase ( seq_id );
return false ;
}
2026-01-04 21:22:16 +01:00
const bool can_offload =
sampler &&
sampler -> iface -> backend_init &&
sampler -> iface -> backend_apply &&
llama_sampler_chain_n ( sampler ) > 0 ;
if ( sampler && can_offload ) {
2026-02-03 22:16:16 +02:00
auto * buft = ggml_backend_dev_buffer_type ( model . dev_output ());
2026-01-04 21:22:16 +01:00
sampler -> iface -> backend_init ( sampler , buft );
sampling . samplers [ seq_id ] = sampler ;
2026-01-15 16:39:17 +02:00
sched_need_reserve = true ;
2026-01-04 21:22:16 +01:00
return true ;
}
if ( sampler && ! can_offload ) {
LLAMA_LOG_WARN ( "%s: sampler '%s' for seq_id = %d, cannot be offloaded to the backend \n " , __func__ , llama_sampler_name ( sampler ), seq_id );
2026-01-15 16:39:17 +02:00
if ( sampling . samplers . count ( seq_id ) > 0 ) {
sched_need_reserve = true ;
}
2026-01-04 21:22:16 +01:00
sampling . samplers . erase ( seq_id );
return false ;
}
sampling . samplers . erase ( seq_id );
2026-01-15 16:39:17 +02:00
sched_need_reserve = true ;
2026-01-04 21:22:16 +01:00
return true ;
}
2026-02-14 03:06:27 -05:00
void llama_context :: set_adapters_lora ( llama_adapter_lora ** adapters , size_t n_adapters , float * scales ) {
LLAMA_LOG_DEBUG ( "%s: adapters = %p \n " , __func__ , ( void * ) adapters );
2025-03-13 12:35:44 +02:00
2026-02-14 03:06:27 -05:00
if ( adapters_lora_are_same ( adapters , n_adapters , scales )) {
2026-01-15 16:39:17 +02:00
return ;
}
2026-02-16 09:21:11 +02:00
loras . reset ( new llama_adapter_loras ());
2026-01-15 16:39:17 +02:00
2026-02-14 03:06:27 -05:00
for ( size_t i = 0 ; i < n_adapters ; i ++ ) {
if ( scales [ i ] != 0.0f ) {
2026-02-16 09:21:11 +02:00
loras -> insert ({ adapters [ i ], scales [ i ]});
2026-02-14 03:06:27 -05:00
}
}
2026-01-15 16:39:17 +02:00
sched_need_reserve = true ;
2025-03-13 12:35:44 +02:00
}
2026-02-14 03:06:27 -05:00
bool llama_context :: adapters_lora_are_same ( llama_adapter_lora ** adapters , size_t n_adapters , float * scales ) {
LLAMA_LOG_DEBUG ( "%s: adapters = %p \n " , __func__ , ( void * ) adapters );
2026-03-06 14:05:52 +01:00
// Adapters with a zero scale are never added to `loras`, so also ignore them for the comparison.
size_t n_non_zero = 0 ;
2026-02-14 03:06:27 -05:00
for ( size_t i = 0 ; i < n_adapters ; i ++ ) {
2026-03-06 14:05:52 +01:00
if ( scales [ i ] == 0.0f ) {
continue ;
}
n_non_zero ++ ;
2026-02-16 09:21:11 +02:00
auto it = loras -> find ( adapters [ i ]);
2026-02-14 03:06:27 -05:00
2026-02-16 09:21:11 +02:00
if ( it == loras -> end () || it -> second != scales [ i ]) {
2026-02-14 03:06:27 -05:00
return false ;
}
}
2026-03-06 14:05:52 +01:00
if ( n_non_zero != loras -> size ()) {
return false ;
}
2026-02-14 03:06:27 -05:00
return true ;
}
bool llama_context :: set_adapter_cvec (
2025-03-13 12:35:44 +02:00
const float * data ,
size_t len ,
int32_t n_embd ,
int32_t il_start ,
int32_t il_end ) {
LLAMA_LOG_DEBUG ( "%s: il_start = %d, il_end = %d \n " , __func__ , il_start , il_end );
2026-03-18 07:10:13 +01:00
bool res = cvec -> apply ( model , data , len , n_embd , il_start , il_end );
2026-01-15 16:39:17 +02:00
2026-03-18 07:10:13 +01:00
sched_need_reserve = true ;
return res ;
2025-03-13 12:35:44 +02:00
}
2025-07-18 08:29:28 +03:00
llm_graph_result * llama_context :: process_ubatch ( const llama_ubatch & ubatch , llm_graph_type gtype , llama_memory_context_i * mctx , ggml_status & ret ) {
2025-06-21 08:03:46 +03:00
if ( mctx && ! mctx -> apply ()) {
LLAMA_LOG_ERROR ( "%s: failed to apply memory context \n " , __func__ );
2025-05-31 10:24:04 +03:00
ret = GGML_STATUS_FAILED ;
return nullptr ;
}
2025-07-17 19:08:33 +03:00
auto * res = gf_res_prev . get ();
auto * gf = res -> get_gf ();
// the new graph parameters
// in order to correctly reuse a graph, it's full topology has to be uniquely determined by these parameters
const auto gparams = graph_params ( res , ubatch , mctx , gtype );
2025-08-01 06:38:12 +03:00
if ( ! graph_reuse_disable && res -> can_reuse ( gparams )) {
2025-07-17 19:08:33 +03:00
//LLAMA_LOG_DEBUG("%s: reusing previous graph\n", __func__);
2026-03-24 20:47:00 +08:00
// with pipeline parallelism, the previous graph_compute_async may still be running
// on the GPU. we must synchronize before set_inputs to avoid overwriting input tensors
// that the previous compute is still reading.
if ( cparams . pipeline_parallel ) {
ggml_backend_sched_synchronize ( sched . get ());
}
2025-07-17 19:08:33 +03:00
n_reused ++ ;
} else {
res -> reset ();
ggml_backend_sched_reset ( sched . get ());
ggml_backend_sched_set_eval_callback ( sched . get (), cparams . cb_eval , cparams . cb_eval_user_data );
//const auto t_start_us = ggml_time_us();
gf = model . build_graph ( gparams );
//LLAMA_LOG_INFO("graph build time: %.3f ms\n", (ggml_time_us() - t_start_us)/1000.0);
if ( ! gf ) {
LLAMA_LOG_ERROR ( "%s: failed to initialize graph \n " , __func__ );
ret = GGML_STATUS_FAILED ;
return nullptr ;
}
if ( ! ggml_backend_sched_alloc_graph ( sched . get (), gf )) {
LLAMA_LOG_ERROR ( "%s: failed to allocate graph \n " , __func__ );
ret = GGML_STATUS_ALLOC_FAILED ;
return nullptr ;
}
2025-05-31 10:24:04 +03:00
}
2025-07-17 19:08:33 +03:00
// set the input data for the input tensors
{
//const auto t_start_us = ggml_time_us();
2026-03-08 12:30:21 +01:00
// FIXME this call causes a crash if any model inputs were not used in the graph and were therefore not allocated
2025-07-17 19:08:33 +03:00
res -> set_inputs ( & ubatch );
//LLAMA_LOG_INFO("graph set inputs time: %.3f ms\n", (ggml_time_us() - t_start_us)/1000.0);
2025-05-31 10:24:04 +03:00
}
2025-07-17 19:08:33 +03:00
const auto status = graph_compute ( res -> get_gf (), ubatch . n_tokens > 1 );
2025-05-31 10:24:04 +03:00
if ( status != GGML_STATUS_SUCCESS ) {
LLAMA_LOG_ERROR ( "%s: failed to compute graph, compute status: %d \n " , __func__ , status );
ret = status ;
return nullptr ;
}
ret = GGML_STATUS_SUCCESS ;
return res ;
}
2025-06-13 13:47:55 +03:00
int llama_context :: encode ( const llama_batch & batch_inp ) {
2026-06-04 01:29:09 +08:00
// MTP hook batches carry both token (next-token id) and embd (h_nextn row),
2026-05-16 20:06:23 +08:00
// so accept either present rather than requiring exactly one.
GGML_ASSERT ( batch_inp . token || batch_inp . embd );
2025-06-20 10:14:14 +03:00
2025-06-13 13:47:55 +03:00
if ( batch_inp . n_tokens == 0 ) {
2025-03-13 12:35:44 +02:00
LLAMA_LOG_ERROR ( "%s: n_tokens == 0 \n " , __func__ );
return - 1 ;
}
2025-06-20 10:14:14 +03:00
const auto & hparams = model . hparams ;
2026-06-12 09:21:06 +02:00
// eagle3/DFlash: features as encoder input, and non-draft paths fall back to model's input dim
2026-06-17 16:29:49 +02:00
const int64_t n_embd = hparams . n_embd_inp_enc ();
2025-08-05 05:27:45 -04:00
const int64_t n_vocab = model . vocab . n_tokens ();
2025-06-20 10:14:14 +03:00
2025-05-02 17:48:36 +03:00
// note: during encode, we always pass the full sequence starting from pos = 0
2025-07-16 16:35:42 +03:00
if ( ! balloc -> init ( batch_inp , model . vocab , nullptr , n_embd , cparams . kv_unified ? LLAMA_MAX_SEQ : cparams . n_seq_max , true )) {
2025-06-13 13:47:55 +03:00
LLAMA_LOG_ERROR ( "%s: failed to initialize batch \n " , __func__ );
return - 1 ;
}
2025-03-13 12:35:44 +02:00
2025-06-20 10:14:14 +03:00
const uint32_t n_tokens = balloc -> get_n_tokens ();
2025-03-13 12:35:44 +02:00
2025-07-16 16:35:42 +03:00
// [TAG_NO_CACHE_PAD]
// TODO: add new split mode where we pad the input sequences so that ubatch.equal_seqs == true
2025-06-20 10:14:14 +03:00
const llama_ubatch ubatch = balloc -> split_simple ( n_tokens );
2025-03-13 12:35:44 +02:00
// micro-batching is not possible for non-causal encoding, so we process the batch in a single shot
2025-06-13 13:47:55 +03:00
GGML_ASSERT ( cparams . n_ubatch >= n_tokens && "encoder requires n_ubatch >= n_tokens" );
2025-03-13 12:35:44 +02:00
2026-07-31 00:48:00 +08:00
// TODO: this clear of the buffer can easily be forgotten - need something better
// sync first so any in-flight async copies into embd_seq complete before it is freed
if ( ! embd_seq . empty ()) {
synchronize ();
}
embd_seq . clear ();
2025-03-13 12:35:44 +02:00
if ( t_compute_start_us == 0 ) {
t_compute_start_us = ggml_time_us ();
}
2026-01-15 16:39:17 +02:00
sched_reserve ();
2025-03-13 12:35:44 +02:00
n_queued_tokens += n_tokens ;
// reserve output buffer
2026-01-28 05:59:30 +01:00
if ( output_reserve ( n_tokens ) < n_tokens ) {
2025-03-13 12:35:44 +02:00
LLAMA_LOG_ERROR ( "%s: could not reserve space for batch with %u outputs \n " , __func__ , n_tokens );
return - 2 ;
};
2025-06-13 13:47:55 +03:00
for ( uint32_t i = 0 ; i < n_tokens ; ++ i ) {
2025-03-13 12:35:44 +02:00
output_ids [ i ] = i ;
}
n_outputs = n_tokens ;
2025-03-18 13:05:49 +02:00
const auto causal_attn_org = cparams . causal_attn ;
// always use non-causal attention for encoder graphs
// TODO: this is a tmp solution until we have a proper way to support enc-dec models
// ref: https://github.com/ggml-org/llama.cpp/pull/12181#issuecomment-2730451223
cparams . causal_attn = false ;
2025-05-31 10:24:04 +03:00
ggml_status status ;
2025-07-17 19:08:33 +03:00
const auto * res = process_ubatch ( ubatch , LLM_GRAPH_TYPE_ENCODER , nullptr , status );
2025-03-13 12:35:44 +02:00
2025-03-18 13:05:49 +02:00
cparams . causal_attn = causal_attn_org ;
2025-05-31 10:24:04 +03:00
if ( ! res ) {
switch ( status ) {
case GGML_STATUS_ABORTED : return 2 ;
case GGML_STATUS_ALLOC_FAILED : return - 2 ;
case GGML_STATUS_FAILED : return - 3 ;
case GGML_STATUS_SUCCESS : GGML_ABORT ( "should not happen" );
}
2025-03-13 12:35:44 +02:00
}
2026-06-04 01:29:09 +08:00
auto * t_logits = res -> get_logits ();
auto * t_embd = res -> get_embd_pooled () ? res -> get_embd_pooled () : res -> get_embd ();
auto * t_h_nextn = cparams . embeddings_nextn ? res -> get_h_nextn () : nullptr ;
2025-03-13 12:35:44 +02:00
2025-07-14 21:01:41 +08:00
// extract logits
2026-02-11 05:38:13 +01:00
if ( logits . data && t_logits ) {
2025-07-14 21:01:41 +08:00
ggml_backend_t backend_res = ggml_backend_sched_get_tensor_backend ( sched . get (), t_logits );
GGML_ASSERT ( backend_res != nullptr );
2026-02-11 05:38:13 +01:00
GGML_ASSERT ( logits . data != nullptr );
2025-07-14 21:01:41 +08:00
2026-02-11 05:38:13 +01:00
ggml_backend_tensor_get_async ( backend_res , t_logits , logits . data , 0 , n_tokens * n_vocab * sizeof ( float ));
2025-07-14 21:01:41 +08:00
}
2025-03-13 12:35:44 +02:00
// extract embeddings
2026-02-11 05:38:13 +01:00
if ( embd . data && t_embd ) {
2025-03-13 12:35:44 +02:00
ggml_backend_t backend_embd = ggml_backend_sched_get_tensor_backend ( sched . get (), t_embd );
GGML_ASSERT ( backend_embd != nullptr );
switch ( cparams . pooling_type ) {
case LLAMA_POOLING_TYPE_NONE :
{
// extract token embeddings
2026-02-11 05:38:13 +01:00
GGML_ASSERT ( embd . data != nullptr );
2026-01-25 15:48:56 +02:00
const uint32_t n_embd_out = hparams . n_embd_out ();
2025-05-08 14:28:33 +03:00
2026-02-11 05:38:13 +01:00
GGML_ASSERT ( n_tokens * n_embd_out <= ( int64_t ) embd . size );
ggml_backend_tensor_get_async ( backend_embd , t_embd , embd . data , 0 , n_tokens * n_embd_out * sizeof ( float ));
2025-03-13 12:35:44 +02:00
} break ;
case LLAMA_POOLING_TYPE_MEAN :
case LLAMA_POOLING_TYPE_CLS :
case LLAMA_POOLING_TYPE_LAST :
{
// extract sequence embeddings
auto & embd_seq_out = embd_seq ;
2025-06-20 10:14:14 +03:00
for ( uint32_t s = 0 ; s < ubatch . n_seqs_unq ; ++ s ) {
const llama_seq_id seq_id = ubatch . seq_id_unq [ s ];
const int32_t seq_idx = ubatch . seq_idx [ seq_id ];
2025-03-13 12:35:44 +02:00
2026-03-21 18:35:00 +01:00
// use n_embd_out (not n_embd_inp) - the pooled embedding has the model's
// output dimension, which differs from input dimension for deepstack models (e.g. qwen3vl)
const uint32_t n_embd_out = hparams . n_embd_out ();
embd_seq_out [ seq_id ]. resize ( n_embd_out );
ggml_backend_tensor_get_async ( backend_embd , t_embd , embd_seq_out [ seq_id ]. data (), ( n_embd_out * seq_idx ) * sizeof ( float ), n_embd_out * sizeof ( float ));
2025-03-13 12:35:44 +02:00
}
} break ;
case LLAMA_POOLING_TYPE_RANK :
{
2025-06-06 09:03:25 +02:00
// extract the rerank score - n_cls_out floats per sequence
2025-05-08 14:28:33 +03:00
auto & embd_seq_out = embd_seq ;
2025-06-20 10:14:14 +03:00
2025-06-06 09:03:25 +02:00
const uint32_t n_cls_out = hparams . n_cls_out ;
2025-05-08 14:28:33 +03:00
2025-06-20 10:14:14 +03:00
for ( uint32_t s = 0 ; s < ubatch . n_seqs_unq ; ++ s ) {
const llama_seq_id seq_id = ubatch . seq_id_unq [ s ];
const int32_t seq_idx = ubatch . seq_idx [ seq_id ];
2025-06-06 09:03:25 +02:00
embd_seq_out [ seq_id ]. resize ( n_cls_out );
2025-06-20 10:14:14 +03:00
ggml_backend_tensor_get_async ( backend_embd , t_embd , embd_seq_out [ seq_id ]. data (), ( n_cls_out * seq_idx ) * sizeof ( float ), n_cls_out * sizeof ( float ));
2025-05-08 14:28:33 +03:00
}
} break ;
2025-03-13 12:35:44 +02:00
case LLAMA_POOLING_TYPE_UNSPECIFIED :
{
GGML_ABORT ( "unknown pooling type" );
}
}
}
2026-06-04 01:29:09 +08:00
// extract nextn embeddings (hidden state before the final output norm)
if ( embd_nextn . data && t_h_nextn && cparams . pooling_type == LLAMA_POOLING_TYPE_NONE ) {
ggml_backend_t backend_h = ggml_backend_sched_get_tensor_backend ( sched . get (), t_h_nextn );
2026-05-16 20:06:23 +08:00
GGML_ASSERT ( backend_h != nullptr );
2026-06-07 20:50:54 +08:00
const uint32_t n_embd = hparams . n_embd_out ();
2026-06-04 01:29:09 +08:00
GGML_ASSERT ( n_tokens * n_embd <= ( int64_t ) embd_nextn . size );
ggml_backend_tensor_get_async ( backend_h , t_h_nextn , embd_nextn . data , 0 , n_tokens * n_embd * sizeof ( float ));
2026-05-16 20:06:23 +08:00
}
2025-03-13 12:35:44 +02:00
// TODO: hacky solution
if ( model . arch == LLM_ARCH_T5 && t_embd ) {
//cross.t_embd = t_embd;
2025-03-18 21:35:19 +02:00
synchronize ();
2025-03-13 12:35:44 +02:00
cross . n_embd = t_embd -> ne [ 0 ];
cross . n_enc = t_embd -> ne [ 1 ];
cross . v_embd . resize ( cross . n_embd * cross . n_enc );
2026-02-11 05:38:13 +01:00
memcpy ( cross . v_embd . data (), embd . data , ggml_nbytes ( t_embd ));
2025-03-13 12:35:44 +02:00
2025-06-20 10:14:14 +03:00
const auto & batch = balloc -> get_batch ();
2025-03-13 12:35:44 +02:00
// remember the sequence ids used during the encoding - needed for cross attention later
cross . seq_ids_enc . resize ( n_tokens );
2025-06-13 13:47:55 +03:00
for ( uint32_t i = 0 ; i < n_tokens ; i ++ ) {
2025-03-19 21:01:57 +01:00
cross . seq_ids_enc [ i ]. clear ();
2025-06-20 10:14:14 +03:00
2025-06-13 13:47:55 +03:00
for ( int s = 0 ; s < batch . n_seq_id [ i ]; s ++ ) {
2025-06-20 10:14:14 +03:00
const llama_seq_id seq_id = batch . seq_id [ i ][ s ];
2025-03-13 12:35:44 +02:00
cross . seq_ids_enc [ i ]. insert ( seq_id );
}
}
}
return 0 ;
}
2026-01-04 21:22:16 +01:00
static std :: map < llama_seq_id , uint32_t > build_seq_to_output_row ( const llama_ubatch & ubatch , uint32_t row_offset ) {
std :: map < llama_seq_id , uint32_t > seq_to_row ;
// how many output tokens we have seen so far for this ubatch.
uint32_t local = 0 ;
for ( uint32_t i = 0 ; i < ubatch . n_tokens ; ++ i ) {
// skip tokens that are not output.
if ( ! ubatch . output [ i ]) {
continue ;
}
const llama_seq_id seq_id = ubatch . seq_id [ i ][ 0 ];
// row_offset is the number of output tokens before this ubatch.
seq_to_row [ seq_id ] = row_offset + local ;
++ local ;
}
return seq_to_row ;
}
static void copy_tensor_async_ints (
const std :: map < llama_seq_id , ggml_tensor *> & tensor_map ,
2026-02-11 05:38:13 +01:00
const buffer_view < llama_token > & sampled ,
2026-01-04 21:22:16 +01:00
const std :: map < llama_seq_id , uint32_t > & seq_to_row ,
ggml_backend_sched_t sched ) {
2026-02-11 05:38:13 +01:00
if ( ! sampled . has_data ()) {
2026-01-04 21:22:16 +01:00
return ;
}
for ( const auto & [ seq_id , tensor ] : tensor_map ) {
auto it = seq_to_row . find ( seq_id );
if ( it == seq_to_row . end ()) {
continue ;
}
const uint32_t row = it -> second ;
2026-02-11 05:38:13 +01:00
GGML_ASSERT ( row < sampled . size );
2026-01-04 21:22:16 +01:00
GGML_ASSERT ( ggml_is_contiguous ( tensor ) && "sampled tokens tensor must be contiguous for async copy" );
ggml_backend_t backend = ggml_backend_sched_get_tensor_backend ( sched , tensor );
2026-02-11 05:38:13 +01:00
ggml_backend_tensor_get_async ( backend , tensor , sampled . data + row , 0 , sizeof ( sampled . data [ row ]));
2026-01-04 21:22:16 +01:00
}
}
static void copy_tensor_async_floats (
const std :: map < llama_seq_id , ggml_tensor *> & tensor_map ,
2026-02-11 05:38:13 +01:00
const buffer_view < float > & dst ,
2026-01-04 21:22:16 +01:00
size_t stride ,
std :: vector < uint32_t > & counts ,
const std :: map < llama_seq_id , uint32_t > & seq_to_row ,
ggml_backend_sched_t sched ) {
2026-02-11 05:38:13 +01:00
if ( ! dst . has_data ()) {
2026-01-04 21:22:16 +01:00
return ;
}
for ( const auto & [ seq_id , tensor ] : tensor_map ) {
auto it = seq_to_row . find ( seq_id );
if ( it == seq_to_row . end ()) {
continue ;
}
const uint32_t row = it -> second ;
GGML_ASSERT ( row < counts . size ());
GGML_ASSERT ( ggml_is_contiguous ( tensor ) && "logits/probs tensor must be contiguous for async copy" );
ggml_backend_t backend = ggml_backend_sched_get_tensor_backend ( sched , tensor );
2026-02-11 05:38:13 +01:00
float * row_ptr = dst . data + ( size_t ) row * stride ;
2026-01-04 21:22:16 +01:00
ggml_backend_tensor_get_async ( backend , tensor , row_ptr , 0 , ggml_nbytes ( tensor ));
// Update the actual number of logits/probabilities that were written for this row.
counts [ row ] = ggml_nelements ( tensor );
}
}
static void copy_tensor_async_candidates (
const std :: map < llama_seq_id , ggml_tensor *> & tensor_map ,
2026-02-11 05:38:13 +01:00
const buffer_view < llama_token > & dst ,
2026-01-04 21:22:16 +01:00
size_t stride ,
std :: vector < uint32_t > & counts ,
const std :: map < llama_seq_id , uint32_t > & seq_to_row ,
ggml_backend_sched_t sched ) {
2026-02-11 05:38:13 +01:00
if ( ! dst . has_data ()) {
2026-01-04 21:22:16 +01:00
return ;
}
for ( const auto & [ seq_id , tensor ] : tensor_map ) {
auto it = seq_to_row . find ( seq_id );
if ( it == seq_to_row . end ()) {
continue ;
}
const uint32_t row = it -> second ;
GGML_ASSERT ( row < counts . size ());
GGML_ASSERT ( ggml_is_contiguous ( tensor ) && "candidates tensor must be contiguous for async copy" );
ggml_backend_t backend = ggml_backend_sched_get_tensor_backend ( sched , tensor );
2026-02-11 05:38:13 +01:00
llama_token * row_ptr = dst . data + ( size_t ) row * stride ;
2026-01-04 21:22:16 +01:00
ggml_backend_tensor_get_async ( backend , tensor , row_ptr , 0 , ggml_nbytes ( tensor ));
// Update the actual number of candidates that were written.
counts [ row ] = ggml_nelements ( tensor );
}
}
2026-01-28 05:59:30 +01:00
static bool needs_raw_logits ( const llama_ubatch & ubatch , const std :: map < llama_seq_id , llama_sampler *> & samplers ) {
for ( uint32_t i = 0 ; i < ubatch . n_tokens ; i ++ ) {
if ( ! ubatch . output [ i ]) {
continue ;
}
// Check if the output token has at least one sequence without a backend sampler.
for ( int32_t j = 0 ; j < ubatch . n_seq_id [ i ]; ++ j ) {
llama_seq_id seq_id = ubatch . seq_id [ i ][ j ];
if ( samplers . find ( seq_id ) == samplers . end ()) {
return true ;
}
}
}
return false ; // all sequences use backend sampling
}
2025-06-13 13:47:55 +03:00
int llama_context :: decode ( const llama_batch & batch_inp ) {
2026-06-04 01:29:09 +08:00
// MTP hook batches carry both token (next-token id) and embd (h_nextn row),
2026-05-16 20:06:23 +08:00
// so accept either present rather than requiring exactly one.
GGML_ASSERT ( batch_inp . token || batch_inp . embd );
2025-06-20 10:14:14 +03:00
2025-05-08 14:28:33 +03:00
if ( ! memory ) {
2025-05-26 14:03:54 +03:00
LLAMA_LOG_DEBUG ( "%s: cannot decode batches with this context (calling encode() instead) \n " , __func__ );
2025-06-13 13:47:55 +03:00
return encode ( batch_inp );
2025-05-08 14:28:33 +03:00
}
2025-06-13 13:47:55 +03:00
if ( batch_inp . n_tokens == 0 ) {
2025-03-13 12:35:44 +02:00
LLAMA_LOG_ERROR ( "%s: n_tokens == 0 \n " , __func__ );
return - 1 ;
}
const auto & vocab = model . vocab ;
const auto & hparams = model . hparams ;
2025-08-05 05:27:45 -04:00
const int64_t n_vocab = vocab . n_tokens ();
2026-08-02 20:55:34 +08:00
const bool mtp_embd = cparams . ctx_type == LLAMA_CONTEXT_TYPE_MTP && batch_inp . embd ;
const int64_t n_embd = mtp_embd ? hparams . n_embd_out () : hparams . n_embd_inp ();
2025-03-13 12:35:44 +02:00
2025-06-20 10:14:14 +03:00
// when computing embeddings, all tokens are output
2026-01-04 21:22:16 +01:00
const bool output_all = cparams . embeddings ;
const bool has_samplers = ! sampling . samplers . empty ();
2025-03-13 12:35:44 +02:00
2026-01-04 21:22:16 +01:00
const uint32_t n_seq_max = cparams . kv_unified ? LLAMA_MAX_SEQ : cparams . n_seq_max ;
// TODO: avoid this workaround in the future
if ( has_samplers && batch_inp . logits ) {
std :: vector < int32_t > seq_output_count ( n_seq_max , 0 );
for ( int32_t i = 0 ; i < batch_inp . n_tokens ; ++ i ) {
if ( batch_inp . logits [ i ] == 0 ) {
continue ;
}
const int ns = batch_inp . n_seq_id ? batch_inp . n_seq_id [ i ] : 1 ;
for ( int32_t s = 0 ; s < ns ; ++ s ) {
const llama_seq_id seq_id = batch_inp . seq_id ? batch_inp . seq_id [ i ][ s ] : 0 ;
seq_output_count [ seq_id ] ++ ;
if ( seq_output_count [ seq_id ] > 1 ) {
LLAMA_LOG_ERROR ( "%s: backend sampling requires at most one output token per sequence (seq_id %d had %d) \n " ,
__func__ , seq_id , seq_output_count [ seq_id ]);
return - 1 ;
}
}
}
}
if ( ! balloc -> init ( batch_inp , vocab , memory . get (), n_embd , n_seq_max , output_all )) {
2025-06-20 10:14:14 +03:00
LLAMA_LOG_ERROR ( "%s: failed to initialize batch \n " , __func__ );
return - 1 ;
}
2025-03-13 12:35:44 +02:00
2025-06-20 10:14:14 +03:00
const uint32_t n_tokens_all = balloc -> get_n_tokens ();
const uint32_t n_outputs_all = balloc -> get_n_outputs ();
2025-06-12 11:50:01 +03:00
2025-06-20 10:14:14 +03:00
if ( output_all ) {
2025-06-12 11:50:01 +03:00
// require that all tokens are output
if ( n_outputs_all != n_tokens_all ) {
2025-06-13 13:47:55 +03:00
LLAMA_LOG_ERROR ( "%s: pooled embedding requires that all tokens are output (n_outputs_all = %d, n_tokens_all = %d) \n " ,
2025-06-12 11:50:01 +03:00
__func__ , n_outputs_all , n_tokens_all );
return - 1 ;
}
}
2025-03-13 12:35:44 +02:00
GGML_ASSERT ( n_tokens_all <= cparams . n_batch );
GGML_ASSERT (( cparams . causal_attn || cparams . n_ubatch >= n_tokens_all ) && "non-causal attention requires n_ubatch >= n_tokens" );
2026-07-31 00:48:00 +08:00
// TODO: this clear of the buffer can easily be forgotten - need something better
// sync first so any in-flight async copies into embd_seq complete before it is freed
if ( ! embd_seq . empty ()) {
synchronize ();
}
embd_seq . clear ();
2025-03-13 12:35:44 +02:00
if ( t_compute_start_us == 0 ) {
t_compute_start_us = ggml_time_us ();
}
n_queued_tokens += n_tokens_all ;
2025-07-24 16:31:48 +03:00
output_swaps . clear ();
2025-03-13 12:35:44 +02:00
2026-01-15 16:39:17 +02:00
sched_reserve ();
2025-06-04 18:58:20 +03:00
bool did_optimize = false ;
2025-08-22 12:22:13 +03:00
// handle any pending shifts/copies
2025-08-21 19:13:45 +03:00
memory_update ( false );
2025-05-31 10:24:04 +03:00
2025-06-21 08:03:46 +03:00
llama_memory_context_ptr mctx ;
2025-05-31 10:24:04 +03:00
2025-05-31 12:55:57 +03:00
while ( true ) {
2025-06-21 08:03:46 +03:00
mctx = memory -> init_batch ( * balloc , cparams . n_ubatch , output_all );
if ( ! mctx ) {
2025-05-31 12:55:57 +03:00
return - 2 ;
}
2025-06-21 08:03:46 +03:00
switch ( mctx -> get_status ()) {
2025-05-31 12:55:57 +03:00
case LLAMA_MEMORY_STATUS_SUCCESS :
{
} break ;
2025-06-04 18:58:20 +03:00
case LLAMA_MEMORY_STATUS_NO_UPDATE :
{
2025-06-21 08:03:46 +03:00
LLAMA_LOG_ERROR ( "%s: unexpected memory context status: %d \n " , __func__ , mctx -> get_status ());
2025-06-04 18:58:20 +03:00
return - 2 ;
}
2025-05-31 12:55:57 +03:00
case LLAMA_MEMORY_STATUS_FAILED_PREPARE :
{
2025-06-04 18:58:20 +03:00
if ( ! did_optimize ) {
did_optimize = true ;
2025-05-31 12:55:57 +03:00
2025-08-21 19:13:45 +03:00
if ( memory_update ( true )) {
2025-06-20 10:14:14 +03:00
LLAMA_LOG_DEBUG ( "%s: retrying batch size %d after cache optimization \n " , __func__ , balloc -> get_n_tokens ());
2025-05-31 12:55:57 +03:00
continue ;
}
}
2025-06-20 10:14:14 +03:00
LLAMA_LOG_WARN ( "%s: failed to find a memory slot for batch of size %d \n " , __func__ , balloc -> get_n_tokens ());
2025-05-31 12:55:57 +03:00
return 1 ;
}
case LLAMA_MEMORY_STATUS_FAILED_COMPUTE :
{
2025-06-20 10:14:14 +03:00
LLAMA_LOG_ERROR ( "%s: compute failed while preparing batch of size %d \n " , __func__ , balloc -> get_n_tokens ());
2025-06-04 18:58:20 +03:00
2025-05-31 12:55:57 +03:00
return - 2 ;
}
}
break ;
2025-05-31 10:24:04 +03:00
}
2025-03-13 12:35:44 +02:00
// reserve output buffer
2026-01-28 05:59:30 +01:00
if ( output_reserve ( n_outputs_all ) < n_outputs_all ) {
2025-06-13 13:47:55 +03:00
LLAMA_LOG_ERROR ( "%s: could not reserve space for batch with %d outputs \n " , __func__ , n_outputs_all );
2025-03-13 12:35:44 +02:00
return - 2 ;
};
int64_t n_outputs_prev = 0 ;
2026-05-17 23:30:25 +08:00
int64_t n_tokens_prev = 0 ;
2025-03-13 12:35:44 +02:00
2025-05-31 10:24:04 +03:00
do {
2025-06-21 08:03:46 +03:00
const auto & ubatch = mctx -> get_ubatch ();
2025-03-13 12:35:44 +02:00
2025-06-12 11:50:01 +03:00
// count the outputs in this ubatch
2025-03-13 12:35:44 +02:00
{
int32_t n_outputs_new = 0 ;
if ( n_outputs_all == n_tokens_all ) {
n_outputs_new = ubatch . n_tokens ;
} else {
for ( uint32_t i = 0 ; i < ubatch . n_tokens ; i ++ ) {
n_outputs_new += ( int32_t ) ( ubatch . output [ i ] != 0 );
}
}
// needs to happen before the graph is built
n_outputs = n_outputs_new ;
}
2025-05-31 10:24:04 +03:00
ggml_status status ;
2026-05-16 20:06:23 +08:00
const auto * res = process_ubatch ( ubatch , ctx_type_to_graph_type ( cparams . ctx_type ), mctx . get (), status );
2025-03-13 12:35:44 +02:00
2025-05-31 10:24:04 +03:00
if ( ! res ) {
2025-08-26 12:47:00 +03:00
// the last ubatch failed or was aborted -> remove all positions of that ubatch from the memory module
2025-06-15 10:08:58 +03:00
llama_pos pos_min [ LLAMA_MAX_SEQ ];
for ( int s = 0 ; s < LLAMA_MAX_SEQ ; ++ s ) {
2025-06-05 09:06:29 +03:00
pos_min [ s ] = std :: numeric_limits < llama_pos >:: max ();
}
2025-03-13 12:35:44 +02:00
2025-05-31 10:24:04 +03:00
for ( uint32_t i = 0 ; i < ubatch . n_tokens ; ++ i ) {
const auto & seq_id = ubatch . seq_id [ i ][ 0 ];
2025-03-13 12:35:44 +02:00
2025-05-31 10:24:04 +03:00
pos_min [ seq_id ] = std :: min ( pos_min [ seq_id ], ubatch . pos [ i ]);
}
2025-03-13 12:35:44 +02:00
2025-06-15 10:08:58 +03:00
for ( int s = 0 ; s < LLAMA_MAX_SEQ ; ++ s ) {
2025-05-31 10:24:04 +03:00
if ( pos_min [ s ] == std :: numeric_limits < llama_pos >:: max ()) {
continue ;
}
2025-08-26 12:47:00 +03:00
LLAMA_LOG_WARN ( "%s: removing memory module entries for seq_id = %d, pos = [%d, +inf) \n " , __func__ , s , pos_min [ s ]);
2025-05-31 10:24:04 +03:00
2025-06-05 15:29:22 +03:00
memory -> seq_rm ( s , pos_min [ s ], - 1 );
2025-05-31 10:24:04 +03:00
}
switch ( status ) {
case GGML_STATUS_ABORTED : return 2 ;
case GGML_STATUS_ALLOC_FAILED : return - 2 ;
case GGML_STATUS_FAILED : return - 3 ;
case GGML_STATUS_SUCCESS : GGML_ABORT ( "should not happen" );
2025-03-13 12:35:44 +02:00
}
}
// plot the computation graph in dot format (for debugging purposes)
//if (n_past%100 == 0) {
// ggml_graph_dump_dot(gf, NULL, "llama.dot");
//}
2026-06-04 01:29:09 +08:00
auto * t_logits = res -> get_logits ();
auto * t_embd = cparams . embeddings ? res -> get_embd () : nullptr ;
auto * t_h_nextn = cparams . embeddings_nextn ? res -> get_h_nextn () : nullptr ;
2025-03-13 12:35:44 +02:00
if ( t_embd && res -> get_embd_pooled ()) {
t_embd = res -> get_embd_pooled ();
}
// extract logits
2026-02-11 05:38:13 +01:00
if ( logits . data && t_logits && n_outputs > 0 && needs_raw_logits ( ubatch , sampling . samplers )) {
2025-03-13 12:35:44 +02:00
ggml_backend_t backend_res = ggml_backend_sched_get_tensor_backend ( sched . get (), t_logits );
GGML_ASSERT ( backend_res != nullptr );
2026-02-11 05:38:13 +01:00
GGML_ASSERT ( logits . data != nullptr );
2025-03-13 12:35:44 +02:00
2026-02-11 05:38:13 +01:00
float * logits_out = logits . data + n_outputs_prev * n_vocab ;
2025-03-13 12:35:44 +02:00
if ( n_outputs ) {
GGML_ASSERT ( n_outputs_prev + n_outputs <= n_outputs_all );
2026-02-11 05:38:13 +01:00
GGML_ASSERT (( n_outputs_prev + n_outputs ) * n_vocab <= ( int64_t ) logits . size );
2025-03-13 12:35:44 +02:00
ggml_backend_tensor_get_async ( backend_res , t_logits , logits_out , 0 , n_outputs * n_vocab * sizeof ( float ));
}
}
// extract embeddings
2026-02-11 05:38:13 +01:00
if ( embd . data && t_embd && n_outputs > 0 ) {
2025-03-13 12:35:44 +02:00
ggml_backend_t backend_embd = ggml_backend_sched_get_tensor_backend ( sched . get (), t_embd );
GGML_ASSERT ( backend_embd != nullptr );
switch ( cparams . pooling_type ) {
case LLAMA_POOLING_TYPE_NONE :
{
// extract token embeddings
2026-02-11 05:38:13 +01:00
GGML_ASSERT ( embd . data != nullptr );
2026-01-25 15:48:56 +02:00
const uint32_t n_embd_out = hparams . n_embd_out ();
2026-02-11 05:38:13 +01:00
float * embd_out = embd . data + n_outputs_prev * n_embd_out ;
2025-03-13 12:35:44 +02:00
if ( n_outputs ) {
GGML_ASSERT ( n_outputs_prev + n_outputs <= n_outputs_all );
2026-02-11 05:38:13 +01:00
GGML_ASSERT (( n_outputs_prev + n_outputs ) * n_embd_out <= ( int64_t ) embd . size );
2026-01-05 19:52:56 +01:00
ggml_backend_tensor_get_async ( backend_embd , t_embd , embd_out , 0 , n_outputs * n_embd_out * sizeof ( float ));
2025-03-13 12:35:44 +02:00
}
} break ;
case LLAMA_POOLING_TYPE_MEAN :
case LLAMA_POOLING_TYPE_CLS :
case LLAMA_POOLING_TYPE_LAST :
{
// extract sequence embeddings (cleared before processing each batch)
auto & embd_seq_out = embd_seq ;
2026-03-21 18:35:00 +01:00
// use n_embd_out (not n_embd_inp) - the pooled embedding has the model's
// output dimension, which differs from input dimension for deepstack models (e.g. qwen3vl)
const uint32_t n_embd_out = hparams . n_embd_out ();
2025-06-20 10:14:14 +03:00
for ( uint32_t s = 0 ; s < ubatch . n_seqs_unq ; ++ s ) {
const llama_seq_id seq_id = ubatch . seq_id_unq [ s ];
const int32_t seq_idx = ubatch . seq_idx [ seq_id ];
2026-03-21 18:35:00 +01:00
embd_seq_out [ seq_id ]. resize ( n_embd_out );
ggml_backend_tensor_get_async ( backend_embd , t_embd , embd_seq_out [ seq_id ]. data (), ( n_embd_out * seq_idx ) * sizeof ( float ), n_embd_out * sizeof ( float ));
2025-03-13 12:35:44 +02:00
}
} break ;
case LLAMA_POOLING_TYPE_RANK :
{
2025-06-20 10:14:14 +03:00
// extract the rerank score - n_cls_out floats per sequence
2025-03-13 12:35:44 +02:00
auto & embd_seq_out = embd_seq ;
2025-06-20 10:14:14 +03:00
const uint32_t n_cls_out = hparams . n_cls_out ;
for ( uint32_t s = 0 ; s < ubatch . n_seqs_unq ; ++ s ) {
const llama_seq_id seq_id = ubatch . seq_id_unq [ s ];
const int32_t seq_idx = ubatch . seq_idx [ seq_id ];
embd_seq_out [ seq_id ]. resize ( n_cls_out );
ggml_backend_tensor_get_async ( backend_embd , t_embd , embd_seq_out [ seq_id ]. data (), ( n_cls_out * seq_idx ) * sizeof ( float ), n_cls_out * sizeof ( float ));
2025-03-13 12:35:44 +02:00
}
} break ;
case LLAMA_POOLING_TYPE_UNSPECIFIED :
{
GGML_ABORT ( "unknown pooling type" );
}
}
}
2026-06-12 09:21:06 +02:00
extract_layer_inputs ( res , n_tokens_prev , ubatch . n_tokens );
2026-06-04 01:29:09 +08:00
// extract nextn embeddings before
2026-05-16 20:06:23 +08:00
// only meaningful in LLAMA_POOLING_TYPE_NONE (per-token); other pooling modes are ignored.
2026-05-17 23:30:25 +08:00
{
2026-06-04 01:29:09 +08:00
const bool masked = cparams . embeddings_nextn_masked ;
2026-05-17 23:30:25 +08:00
const int64_t n_rows = masked ? n_outputs : ( int64_t ) ubatch . n_tokens ;
const int64_t offset = masked ? n_outputs_prev : n_tokens_prev ;
2026-05-16 20:06:23 +08:00
2026-06-04 01:29:09 +08:00
if ( embd_nextn . data && t_h_nextn && n_rows > 0 && cparams . pooling_type == LLAMA_POOLING_TYPE_NONE ) {
ggml_backend_t backend_h = ggml_backend_sched_get_tensor_backend ( sched . get (), t_h_nextn );
2026-05-17 23:30:25 +08:00
GGML_ASSERT ( backend_h != nullptr );
2026-05-16 20:06:23 +08:00
2026-06-07 20:50:54 +08:00
const uint32_t n_embd = hparams . n_embd_out ();
2026-06-04 01:29:09 +08:00
float * embd_nextn_out = embd_nextn . data + offset * n_embd ;
2026-05-17 23:30:25 +08:00
2026-06-04 01:29:09 +08:00
GGML_ASSERT (( offset + n_rows ) * n_embd <= ( int64_t ) embd_nextn . size );
ggml_backend_tensor_get_async ( backend_h , t_h_nextn , embd_nextn_out , 0 , n_rows * n_embd * sizeof ( float ));
2026-05-17 23:30:25 +08:00
}
2026-05-16 20:06:23 +08:00
}
2026-01-28 05:59:30 +01:00
// Copy backend sampling output if this ubatch produced any sampling tensors.
if ( has_samplers && ( ! res -> t_sampled . empty () || ! res -> t_sampled_probs . empty () || ! res -> t_sampled_logits . empty ())) {
2026-01-04 21:22:16 +01:00
const auto seq_to_output_row = build_seq_to_output_row ( ubatch , n_outputs_prev );
const auto stride = n_vocab ;
// async copy the sampling data from the backend to the host
2026-02-11 05:38:13 +01:00
copy_tensor_async_ints ( res -> t_sampled , sampling . sampled , seq_to_output_row , sched . get ());
2026-01-04 21:22:16 +01:00
copy_tensor_async_floats ( res -> t_sampled_logits , sampling . logits , stride , sampling . logits_count , seq_to_output_row , sched . get ());
copy_tensor_async_floats ( res -> t_sampled_probs , sampling . probs , stride , sampling . probs_count , seq_to_output_row , sched . get ());
copy_tensor_async_candidates ( res -> t_candidates , sampling . candidates , stride , sampling . candidates_count , seq_to_output_row , sched . get ());
}
2025-03-13 12:35:44 +02:00
n_outputs_prev += n_outputs ;
2026-05-17 23:30:25 +08:00
n_tokens_prev += ubatch . n_tokens ;
2025-06-21 08:03:46 +03:00
} while ( mctx -> next ());
2025-03-13 12:35:44 +02:00
2025-05-02 20:54:13 +03:00
// set to total number of outputs in the batch, for use in llama_get_logits_ith
n_outputs = n_outputs_all ;
2025-03-13 12:35:44 +02:00
// set output mappings
2025-06-13 13:47:55 +03:00
if ( n_outputs > 0 ) {
2025-03-13 12:35:44 +02:00
bool sorted_output = true ;
2025-06-20 10:14:14 +03:00
auto & out_ids = balloc -> get_out_ids ();
2025-05-02 17:48:36 +03:00
2025-06-13 13:47:55 +03:00
GGML_ASSERT ( out_ids . size () == ( size_t ) n_outputs );
2025-03-13 12:35:44 +02:00
2025-06-13 13:47:55 +03:00
for ( int64_t i = 0 ; i < n_outputs ; ++ i ) {
2025-05-02 17:48:36 +03:00
int64_t out_id = out_ids [ i ];
2025-03-13 12:35:44 +02:00
output_ids [ out_id ] = i ;
if ( out_id != i ) {
sorted_output = false ;
}
}
2025-05-02 17:48:36 +03:00
// make the outputs have the same order they had in the user-provided batch
// note: this is mostly relevant for recurrent models atm
2025-11-24 21:06:17 +01:00
if ( ! sorted_output && n_outputs > 1 ) {
2025-05-02 17:48:36 +03:00
GGML_ASSERT (( size_t ) n_outputs == out_ids . size ());
// TODO: is there something more efficient which also minimizes swaps?
// selection sort, to minimize swaps (from https://en.wikipedia.org/wiki/Selection_sort)
2025-06-13 13:47:55 +03:00
for ( uint32_t i = 0 ; i < n_outputs - 1 ; ++ i ) {
uint32_t j_min = i ;
for ( uint32_t j = i + 1 ; j < n_outputs ; ++ j ) {
2025-05-02 17:48:36 +03:00
if ( out_ids [ j ] < out_ids [ j_min ]) {
j_min = j ;
}
}
2025-06-13 13:47:55 +03:00
if ( j_min == i ) {
continue ;
}
2025-05-02 17:48:36 +03:00
std :: swap ( out_ids [ i ], out_ids [ j_min ]);
2025-07-24 16:31:48 +03:00
// remember the swaps and apply them lazily upon logits/embeddings access
output_swaps . push_back ({ i , j_min });
2025-05-02 17:48:36 +03:00
}
2025-06-13 13:47:55 +03:00
2025-05-02 17:48:36 +03:00
std :: fill ( output_ids . begin (), output_ids . end (), - 1 );
2025-06-13 13:47:55 +03:00
for ( uint32_t i = 0 ; i < n_outputs ; ++ i ) {
2025-05-02 17:48:36 +03:00
output_ids [ out_ids [ i ]] = i ;
}
2025-03-13 12:35:44 +02:00
}
}
// wait for the computation to finish (automatically done when obtaining the model output)
//synchronize();
return 0 ;
}
//
// output
//
2026-01-28 05:59:30 +01:00
uint32_t llama_context :: output_reserve ( int32_t n_outputs ) {
2025-03-13 12:35:44 +02:00
const auto & hparams = model . hparams ;
const auto & vocab = model . vocab ;
const int64_t n_outputs_max = std :: max < int64_t > ( n_outputs , n_seq_max ());
2025-01-03 10:18:53 +02:00
2026-01-05 19:52:56 +01:00
const auto n_batch = cparams . n_batch ;
const auto n_vocab = vocab . n_tokens ();
2026-06-12 09:21:06 +02:00
const auto n_embd = hparams . n_embd ;
2026-01-25 15:48:56 +02:00
const auto n_embd_out = hparams . n_embd_out ();
2025-01-03 10:18:53 +02:00
2026-06-04 01:29:09 +08:00
bool has_logits = true ;
bool has_embd = cparams . embeddings ;
bool has_embd_nextn = cparams . embeddings_nextn ;
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
// TODO: hacky enc-dec support
if ( model . arch == LLM_ARCH_T5 ) {
has_logits = true ;
has_embd = true ;
2025-01-03 10:18:53 +02:00
}
2026-01-04 21:22:16 +01:00
size_t backend_float_count = 0 ;
size_t backend_token_count = 0 ;
2026-06-12 09:21:06 +02:00
size_t embd_layer_inp_float_count = 0 ;
2026-01-04 21:22:16 +01:00
2026-06-04 01:29:09 +08:00
logits . size = has_logits ? n_vocab * n_outputs_max : 0 ;
embd . size = has_embd ? n_embd_out * n_outputs_max : 0 ;
2026-06-07 20:50:54 +08:00
embd_nextn . size = has_embd_nextn ? n_embd_out * n_outputs_max : 0 ;
2026-01-04 21:22:16 +01:00
2026-06-04 01:29:09 +08:00
if ( has_embd_nextn && ! cparams . embeddings_nextn_masked ) {
// unmasked: nextn row exists for every token in the batch, not just
2026-05-17 23:30:25 +08:00
// those flagged via batch.logits[i] -> size by token count instead.
2026-06-07 20:50:54 +08:00
embd_nextn . size = ( size_t ) n_embd_out * n_batch ;
2026-05-17 23:30:25 +08:00
}
2026-06-12 09:21:06 +02:00
for ( bool enabled : cparams . embeddings_layer_inp ) {
if ( enabled ) {
embd_layer_inp_float_count += ( size_t ) n_embd * n_batch ;
}
}
2026-01-28 05:59:30 +01:00
// Allocate backend sampling output buffers if there are backend samplers configured.
const bool has_sampling = ! sampling . samplers . empty ();
if ( has_sampling ) {
2026-02-11 05:38:13 +01:00
backend_float_count = 2 * n_vocab * n_outputs_max ; // logits + probs
backend_token_count = ( 1 + n_vocab ) * n_outputs_max ; // sampled + candidates
2026-01-04 21:22:16 +01:00
}
2025-03-13 12:35:44 +02:00
if ( output_ids . empty ()) {
// init, never resized afterwards
output_ids . resize ( n_batch );
}
const size_t prev_size = buf_output ? ggml_backend_buffer_get_size ( buf_output . get ()) : 0 ;
2026-01-04 21:22:16 +01:00
const size_t new_size =
2026-06-12 09:21:06 +02:00
( logits . size + embd . size + embd_nextn . size + embd_layer_inp_float_count + backend_float_count ) * sizeof ( float ) +
( backend_token_count ) * sizeof ( llama_token );
2025-01-03 10:18:53 +02:00
// alloc only when more than the current capacity is required
// TODO: also consider shrinking the buffer
2025-03-13 12:35:44 +02:00
if ( ! buf_output || prev_size < new_size ) {
if ( buf_output ) {
2025-01-03 10:18:53 +02:00
#ifndef NDEBUG
// This doesn't happen often, but may be annoying in some cases (like the HellaSwag benchmark)
2026-01-04 21:22:16 +01:00
LLAMA_LOG_DEBUG ( "%s: reallocating output buffer from size %.02f MiB to %.02f MiB \n " , __func__ , prev_size / 1024.0 / 1024.0 , new_size / 1024.0 / 1024.0 );
2025-01-03 10:18:53 +02:00
#endif
2025-12-13 09:19:51 -06:00
synchronize ();
2026-01-04 21:22:16 +01:00
// TODO: not needed?
2025-03-13 12:35:44 +02:00
buf_output = nullptr ;
2026-02-11 05:38:13 +01:00
logits . data = nullptr ;
embd . data = nullptr ;
2026-06-04 01:29:09 +08:00
embd_nextn . data = nullptr ;
2026-06-12 09:21:06 +02:00
for ( auto & layer_inp : embd_layer_inp ) {
layer_inp = { nullptr , 0 };
}
2025-01-03 10:18:53 +02:00
}
auto * buft = ggml_backend_cpu_buffer_type ();
// try to use the host buffer of the device where the output tensor is allocated for faster transfer to system memory
2025-03-13 12:35:44 +02:00
auto * output_dev = model . dev_output ();
2025-01-03 10:18:53 +02:00
auto * output_dev_host_buft = output_dev ? ggml_backend_dev_host_buffer_type ( output_dev ) : nullptr ;
if ( output_dev_host_buft ) {
buft = output_dev_host_buft ;
}
2025-03-13 12:35:44 +02:00
buf_output . reset ( ggml_backend_buft_alloc_buffer ( buft , new_size ));
if ( buf_output == nullptr ) {
2025-01-03 10:18:53 +02:00
LLAMA_LOG_ERROR ( "%s: failed to allocate output buffer of size %.2f MiB \n " , __func__ , new_size / ( 1024.0 * 1024.0 ));
return 0 ;
}
2026-03-20 17:31:34 +08:00
ggml_backend_buffer_clear ( buf_output . get (), 0 );
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
float * output_base = ( float * ) ggml_backend_buffer_get_base ( buf_output . get ());
2025-01-03 10:18:53 +02:00
2026-01-04 21:22:16 +01:00
size_t offset = 0 ;
uint8_t * base = ( uint8_t * ) output_base ;
2026-02-11 05:38:13 +01:00
logits = has_logits ? buffer_view < float > { output_base , logits . size } : buffer_view < float > { nullptr , 0 };
offset += logits . size * sizeof ( float );
2026-01-04 21:22:16 +01:00
2026-02-11 05:38:13 +01:00
embd = has_embd ? buffer_view < float > {( float * ) ( base + offset ), embd . size } : buffer_view < float > { nullptr , 0 };
offset += embd . size * sizeof ( float );
2026-01-04 21:22:16 +01:00
2026-06-04 01:29:09 +08:00
embd_nextn = has_embd_nextn ? buffer_view < float > {( float * ) ( base + offset ), embd_nextn . size } : buffer_view < float > { nullptr , 0 };
offset += embd_nextn . size * sizeof ( float );
2026-05-16 20:06:23 +08:00
2026-06-12 09:21:06 +02:00
for ( uint32_t il = 0 ; il < embd_layer_inp . size (); ++ il ) {
if ( cparams . embeddings_layer_inp [ il ]) {
embd_layer_inp [ il ] = buffer_view < float > {( float * ) ( base + offset ), ( size_t ) n_embd * n_batch };
offset += embd_layer_inp [ il ]. size * sizeof ( float );
} else {
embd_layer_inp [ il ] = buffer_view < float > { nullptr , 0 };
}
}
2026-01-04 21:22:16 +01:00
if ( has_sampling ) {
2026-02-11 05:38:13 +01:00
sampling . logits = {( float * ) ( base + offset ), ( size_t )( n_vocab * n_outputs_max )};
offset += sampling . logits . size * sizeof ( float );
2026-01-04 21:22:16 +01:00
2026-02-11 05:38:13 +01:00
sampling . probs = {( float * ) ( base + offset ), ( size_t )( n_vocab * n_outputs_max )};
offset += sampling . probs . size * sizeof ( float );
2026-01-04 21:22:16 +01:00
2026-02-11 05:38:13 +01:00
sampling . sampled = {( llama_token * ) ( base + offset ), ( size_t ) n_outputs_max };
offset += sampling . sampled . size * sizeof ( llama_token );
2026-01-04 21:22:16 +01:00
2026-02-11 05:38:13 +01:00
sampling . candidates = {( llama_token * ) ( base + offset ), ( size_t )( n_vocab * n_outputs_max )};
offset += sampling . candidates . size * sizeof ( llama_token );
2026-01-04 21:22:16 +01:00
// The count vectors keep track of the actual number of logits/probs/candidates
// copied from the backend for each output row.
sampling . logits_count . resize ( n_outputs_max );
sampling . probs_count . resize ( n_outputs_max );
sampling . candidates_count . resize ( n_outputs_max );
std :: fill ( sampling . logits_count . begin (), sampling . logits_count . end (), 0 );
std :: fill ( sampling . probs_count . begin (), sampling . probs_count . end (), 0 );
std :: fill ( sampling . candidates_count . begin (), sampling . candidates_count . end (), 0 );
2026-02-11 05:38:13 +01:00
std :: fill_n ( sampling . sampled . data , sampling . sampled . size , LLAMA_TOKEN_NULL );
2026-02-15 14:57:40 +02:00
} else {
sampling . logits = { nullptr , 0 };
sampling . probs = { nullptr , 0 };
sampling . sampled = { nullptr , 0 };
sampling . candidates = { nullptr , 0 };
sampling . logits_count . clear ();
sampling . probs_count . clear ();
sampling . candidates_count . clear ();
2026-01-04 21:22:16 +01:00
}
2025-01-03 10:18:53 +02:00
// set all ids as invalid (negative)
2025-03-13 12:35:44 +02:00
std :: fill ( output_ids . begin (), output_ids . end (), - 1 );
2025-01-03 10:18:53 +02:00
2025-06-13 13:47:55 +03:00
this -> n_outputs = 0 ;
2025-01-03 10:18:53 +02:00
2026-06-01 23:01:38 +08:00
GGML_ASSERT ( n_outputs_max <= cparams . n_outputs_max );
2025-01-03 10:18:53 +02:00
return n_outputs_max ;
}
2026-06-12 09:21:06 +02:00
void llama_context :: extract_layer_inputs ( const llm_graph_result * res , size_t token_offset , size_t n_tokens ) {
for ( uint32_t il = 0 ; il < cparams . embeddings_layer_inp . size (); ++ il ) {
if ( ! cparams . embeddings_layer_inp [ il ]) {
continue ;
}
if ( ! embd_layer_inp [ il ]. has_data ()) {
GGML_ABORT ( "output layer input buffer not allocated" );
}
ggml_tensor * t = res -> get_layer_inp (( int ) il );
if ( ! t ) {
GGML_ABORT ( "layer input tensor not found" );
}
const size_t nbytes = ggml_nbytes ( t );
const size_t nfloats = nbytes / sizeof ( float );
GGML_ASSERT ( n_tokens > 0 );
GGML_ASSERT ( nfloats % n_tokens == 0 );
const size_t row_floats = nfloats / n_tokens ;
const size_t dst_offset = token_offset * row_floats ;
GGML_ASSERT ( dst_offset + nfloats <= embd_layer_inp [ il ]. size );
ggml_backend_t backend = ggml_backend_sched_get_tensor_backend ( sched . get (), t );
GGML_ASSERT ( backend != nullptr );
ggml_backend_tensor_get_async ( backend , t , embd_layer_inp [ il ]. data + dst_offset , 0 , nbytes );
}
}
2025-07-24 16:31:48 +03:00
void llama_context :: output_reorder () {
2026-08-02 20:55:34 +08:00
const uint64_t n_vocab = model . vocab . n_tokens ();
const uint64_t n_embd = model . hparams . n_embd ;
const uint64_t n_embd_out = model . hparams . n_embd_out ();
2025-07-24 16:31:48 +03:00
2025-08-05 05:27:45 -04:00
for ( size_t s = 0 ; s < output_swaps . size (); ++ s ) {
const uint64_t i0 = output_swaps [ s ]. i0 ;
const uint64_t i1 = output_swaps [ s ]. i1 ;
2025-07-24 16:31:48 +03:00
2026-02-11 05:38:13 +01:00
if ( logits . size > 0 ) {
2025-08-05 05:27:45 -04:00
for ( uint64_t k = 0 ; k < n_vocab ; k ++ ) {
2026-02-11 05:38:13 +01:00
std :: swap ( logits . data [ i0 * n_vocab + k ], logits . data [ i1 * n_vocab + k ]);
2025-07-24 16:31:48 +03:00
}
}
2026-02-11 05:38:13 +01:00
if ( embd . size > 0 ) {
2026-08-02 20:55:34 +08:00
for ( uint64_t k = 0 ; k < n_embd_out ; k ++ ) {
std :: swap ( embd . data [ i0 * n_embd_out + k ], embd . data [ i1 * n_embd_out + k ]);
2025-07-24 16:31:48 +03:00
}
}
2026-01-04 21:22:16 +01:00
2026-06-04 01:29:09 +08:00
if ( embd_nextn . size > 0 ) {
2026-08-02 20:55:34 +08:00
for ( uint64_t k = 0 ; k < n_embd_out ; k ++ ) {
std :: swap ( embd_nextn . data [ i0 * n_embd_out + k ], embd_nextn . data [ i1 * n_embd_out + k ]);
2026-05-16 20:06:23 +08:00
}
}
2026-06-12 09:21:06 +02:00
if ( embd_layer_inp . size () > 0 ) {
for ( int lid = 0 ; lid < ( int ) embd_layer_inp . size (); ++ lid ) {
if ( embd_layer_inp [ lid ]. size > 0 ) {
for ( uint64_t k = 0 ; k < n_embd ; ++ k ) {
std :: swap ( embd_layer_inp [ lid ]. data [ i0 * n_embd + k ], embd_layer_inp [ lid ]. data [ i1 * n_embd + k ]);
}
}
}
}
2026-02-15 14:57:40 +02:00
if ( ! sampling . samplers . empty ()) {
assert ( sampling . logits . size > 0 );
assert ( sampling . probs . size > 0 );
assert ( sampling . candidates . size > 0 );
assert ( sampling . sampled . size > 0 );
assert ( sampling . logits_count . size () > 0 );
assert ( sampling . probs_count . size () > 0 );
assert ( sampling . candidates_count . size () > 0 );
2026-01-04 21:22:16 +01:00
for ( uint64_t k = 0 ; k < n_vocab ; ++ k ) {
2026-02-11 05:38:13 +01:00
std :: swap ( sampling . logits . data [ i0 * n_vocab + k ], sampling . logits . data [ i1 * n_vocab + k ]);
2026-01-04 21:22:16 +01:00
}
for ( uint64_t k = 0 ; k < n_vocab ; ++ k ) {
2026-02-11 05:38:13 +01:00
std :: swap ( sampling . probs . data [ i0 * n_vocab + k ], sampling . probs . data [ i1 * n_vocab + k ]);
2026-01-04 21:22:16 +01:00
}
for ( uint64_t k = 0 ; k < n_vocab ; ++ k ) {
2026-02-11 05:38:13 +01:00
std :: swap ( sampling . candidates . data [ i0 * n_vocab + k ], sampling . candidates . data [ i1 * n_vocab + k ]);
2026-01-04 21:22:16 +01:00
}
2026-02-15 14:57:40 +02:00
std :: swap ( sampling . sampled . data [ i0 ], sampling . sampled . data [ i1 ]);
std :: swap ( sampling . logits_count [ i0 ], sampling . logits_count [ i1 ]);
std :: swap ( sampling . probs_count [ i0 ], sampling . probs_count [ i1 ]);
2026-01-04 21:22:16 +01:00
std :: swap ( sampling . candidates_count [ i0 ], sampling . candidates_count [ i1 ]);
}
2025-07-24 16:31:48 +03:00
}
output_swaps . clear ();
}
2025-01-03 10:18:53 +02:00
//
2025-03-13 12:35:44 +02:00
// graph
2025-01-03 10:18:53 +02:00
//
2025-12-08 14:32:41 +01:00
uint32_t llama_context :: graph_max_nodes ( uint32_t n_tokens ) const {
2026-06-29 16:58:51 +08:00
if ( model . arch == LLM_ARCH_QWEN3NEXT ||
model . arch == LLM_ARCH_KIMI_LINEAR ||
model . arch == LLM_ARCH_QWEN35 ||
model . arch == LLM_ARCH_QWEN35MOE ||
2026-07-26 19:43:45 +02:00
model . arch == LLM_ARCH_DEEPSEEK4 ||
2026-08-02 20:55:34 +08:00
( model . arch == LLM_ARCH_DFLASH && model . hparams . dsv4_hc_mult > 0 ) ||
2026-07-27 23:04:18 +08:00
model . arch == LLM_ARCH_NANBEIGE ||
2026-07-26 19:43:45 +02:00
model . arch == LLM_ARCH_MINIMAX_M3 ) {
2025-12-08 14:32:41 +01:00
return std :: max < uint32_t > ( n_tokens * 40 , 32u * model . n_tensors ());
2025-11-28 12:02:56 +01:00
}
2025-12-30 15:53:12 +01:00
uint32_t res = std :: max < uint32_t > ( 1024u , 8u * model . n_tensors ());
2026-01-15 10:24:28 +01:00
for ( const auto & lora : model . loras ) {
res += lora -> get_n_nodes ();
}
2025-12-30 15:53:12 +01:00
return res ;
2025-01-03 10:18:53 +02:00
}
2025-07-17 19:08:33 +03:00
llm_graph_result * llama_context :: get_gf_res_reserve () const {
return static_cast < llm_graph_result *> ( gf_res_reserve . get ());
2025-01-03 10:18:53 +02:00
}
2025-12-15 09:24:59 +01:00
ggml_cgraph * llama_context :: graph_reserve (
uint32_t n_tokens , uint32_t n_seqs , uint32_t n_outputs , const llama_memory_context_i * mctx , bool split_only , size_t * sizes ) {
2025-05-31 10:24:04 +03:00
LLAMA_LOG_DEBUG ( "%s: reserving a graph for ubatch with n_tokens = %4u, n_seqs = %2u, n_outputs = %4u \n " , __func__ , n_tokens , n_seqs , n_outputs );
2025-09-04 15:40:44 +02:00
GGML_ASSERT ( n_outputs >= 1 );
2025-05-31 10:24:04 +03:00
if ( n_tokens % n_seqs != 0 ) {
2025-06-12 02:56:04 -04:00
n_tokens = (( n_tokens + ( n_seqs - 1 )) / n_seqs ) * n_seqs ; // round to next multiple of n_seqs
2025-05-31 10:24:04 +03:00
LLAMA_LOG_DEBUG ( "%s: making n_tokens a multiple of n_seqs - n_tokens = %u, n_seqs = %u, n_outputs = %u \n " , __func__ , n_tokens , n_seqs , n_outputs );
}
2025-07-17 19:08:33 +03:00
ggml_backend_sched_reset ( sched . get ());
2026-03-05 08:50:21 +01:00
// when the scheduler is reset, we cannot reuse the old graph, so we reset the previous graph result to prevent that
2025-07-17 19:08:33 +03:00
gf_res_prev -> reset ();
2025-05-31 10:24:04 +03:00
// store the n_outputs as it is, and restore it afterwards
// TODO: not sure if needed, might simplify in the future by removing this
const auto save_n_outputs = this -> n_outputs ;
this -> n_outputs = n_outputs ;
2025-06-20 10:14:14 +03:00
llama_batch_allocr balloc ( model . hparams . n_pos_per_embd ());
llama_ubatch ubatch = balloc . ubatch_reserve ( n_tokens / n_seqs , n_seqs );
2025-05-31 10:24:04 +03:00
2026-01-04 21:22:16 +01:00
// set one output token per sequence in order to activate all backend samplers
std :: vector < llama_seq_id > seq_ids ( n_seqs );
for ( uint32_t i = 0 ; i < n_seqs ; ++ i ) {
seq_ids [ i ] = i ;
ubatch . n_seq_id [ i ] = 1 ;
ubatch . seq_id [ i ] = & seq_ids [ i ];
ubatch . output [ i ] = true ;
}
2025-07-17 19:08:33 +03:00
auto * res = gf_res_reserve . get ();
2026-05-16 20:06:23 +08:00
const auto gparams = graph_params ( res , ubatch , mctx , ctx_type_to_graph_type ( cparams . ctx_type ));
2025-07-17 19:08:33 +03:00
res -> reset ();
auto * gf = model . build_graph ( gparams );
2025-05-31 10:24:04 +03:00
this -> n_outputs = save_n_outputs ;
// initialize scheduler with the specified graph
2025-08-31 06:49:03 -07:00
if ( split_only ) {
2025-12-15 09:24:59 +01:00
if ( sizes ) {
ggml_backend_sched_reserve_size ( sched . get (), gf , sizes );
} else {
ggml_backend_sched_split_graph ( sched . get (), gf );
}
2025-08-31 06:49:03 -07:00
} else if ( ! ggml_backend_sched_reserve ( sched . get (), gf )) {
2025-12-15 09:24:59 +01:00
GGML_ASSERT ( ! sizes );
2025-05-31 10:24:04 +03:00
LLAMA_LOG_ERROR ( "%s: failed to allocate compute buffers \n " , __func__ );
return nullptr ;
}
return gf ;
}
2025-07-17 19:08:33 +03:00
llm_graph_params llama_context :: graph_params (
2025-07-18 08:29:28 +03:00
llm_graph_result * res ,
2025-07-17 19:08:33 +03:00
const llama_ubatch & ubatch ,
const llama_memory_context_i * mctx ,
2026-01-04 21:22:16 +01:00
llm_graph_type gtype ) const {
2025-07-17 19:08:33 +03:00
return {
/*.arch =*/ model . arch ,
/*.hparams =*/ model . hparams ,
/*.cparams =*/ cparams ,
/*.ubatch =*/ ubatch ,
/*.gtype =*/ gtype ,
/*.sched =*/ sched . get (),
/*.backend_cpu =*/ backend_cpu ,
2026-02-16 09:21:11 +02:00
/*.cvec =*/ cvec . get (),
/*.loras =*/ loras . get (),
2025-07-17 19:08:33 +03:00
/*.mctx =*/ mctx ,
/*.cross =*/ & cross ,
2026-01-04 21:22:16 +01:00
/*.samplers =*/ sampling . samplers ,
2025-07-17 19:08:33 +03:00
/*.n_outputs =*/ n_outputs ,
/*.cb =*/ graph_get_cb (),
/*.res =*/ res ,
};
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
ggml_status llama_context :: graph_compute (
ggml_cgraph * gf ,
bool batched ) {
int n_threads = batched ? cparams . n_threads_batch : cparams . n_threads ;
ggml_threadpool_t tp = batched ? threadpool_batch : threadpool ;
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
if ( backend_cpu != nullptr ) {
auto * reg = ggml_backend_dev_backend_reg ( ggml_backend_get_device ( backend_cpu ));
auto * set_threadpool_fn = ( decltype ( ggml_backend_cpu_set_threadpool ) * ) ggml_backend_reg_get_proc_address ( reg , "ggml_backend_cpu_set_threadpool" );
2025-09-10 05:33:58 +02:00
if ( set_threadpool_fn ) {
set_threadpool_fn ( backend_cpu , tp );
}
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
// set the number of threads for all the backends
for ( const auto & set_n_threads_fn : set_n_threads_fns ) {
set_n_threads_fn . second ( set_n_threads_fn . first , n_threads );
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
auto status = ggml_backend_sched_graph_compute_async ( sched . get (), gf );
if ( status != GGML_STATUS_SUCCESS ) {
LLAMA_LOG_ERROR ( "%s: ggml_backend_sched_graph_compute_async failed with error %d \n " , __func__ , status );
}
// fprintf(stderr, "splits: %d\n", ggml_backend_sched_get_n_splits(sched));
return status ;
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
llm_graph_cb llama_context :: graph_get_cb () const {
return [ & ]( const llama_ubatch & ubatch , ggml_tensor * cur , const char * name , int il ) {
if ( il >= 0 ) {
ggml_format_name ( cur , "%s-%d" , name , il );
2025-01-03 10:18:53 +02:00
} else {
2025-03-13 12:35:44 +02:00
ggml_set_name ( cur , name );
2025-01-03 10:18:53 +02:00
}
2026-07-27 14:54:46 +03:00
// - norm may be automatically assigned to the backend of the previous layer, increasing data transfer between backends
// - force the last op of the layer on the specified backend to avoid running it on the backend of the next layer due to scheduling
2025-03-13 12:35:44 +02:00
// FIXME: fix in ggml_backend_sched
2026-06-06 06:06:47 +02:00
const bool full_offload = model . n_gpu_layers () > model . hparams . n_layer_all ;
2025-03-13 12:35:44 +02:00
if ( ubatch . n_tokens < 32 || full_offload ) {
2026-07-27 14:54:46 +03:00
if ( il != - 1 && ( strcmp ( name , "norm" ) == 0 || strcmp ( name , "l_last" ) == 0 )) {
2025-03-13 12:35:44 +02:00
const auto & dev_layer = model . dev_layer ( il );
for ( const auto & backend : backends ) {
if ( ggml_backend_get_device ( backend . get ()) == dev_layer ) {
if ( ggml_backend_supports_op ( backend . get (), cur )) {
ggml_backend_sched_set_tensor_backend ( sched . get (), cur , backend . get ());
2025-01-03 10:18:53 +02:00
}
}
}
}
}
2025-03-13 12:35:44 +02:00
};
}
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
//
// state save/load
//
2025-01-03 10:18:53 +02:00
2025-03-13 12:35:44 +02:00
class llama_io_write_dummy : public llama_io_write_i {
public :
2026-05-05 06:35:07 +03:00
llama_io_write_dummy ( bool skip_tensors ) : skip_tensors ( skip_tensors ) {}
2025-01-03 10:18:53 +02:00
void write ( const void * /* src */ , size_t size ) override {
size_written += size ;
}
2026-05-05 06:35:07 +03:00
void write_tensor ( ggml_tensor * /* tensor */ , size_t /* offset */ , size_t size ) override {
if ( skip_tensors ) {
return ;
}
2025-01-03 10:18:53 +02:00
size_written += size ;
}
2025-03-13 12:35:44 +02:00
size_t n_bytes () override {
2025-01-03 10:18:53 +02:00
return size_written ;
}
2025-03-13 12:35:44 +02:00
private :
2026-05-05 06:35:07 +03:00
const bool skip_tensors ;
2025-03-13 12:35:44 +02:00
size_t size_written = 0 ;
2025-01-03 10:18:53 +02:00
};
2026-05-05 06:35:07 +03:00
class llama_io_write_host : public llama_io_write_i {
2025-03-13 12:35:44 +02:00
public :
2026-05-05 06:35:07 +03:00
llama_io_write_host (
2025-03-13 12:35:44 +02:00
uint8_t * p , size_t len ) : ptr ( p ), buf_size ( len ) {}
2025-01-03 10:18:53 +02:00
2026-05-05 06:35:07 +03:00
~ llama_io_write_host () {
2026-05-02 18:03:25 +03:00
// TODO: add backend support to batch tensor_get? or some other way to speed this up
2026-05-05 06:35:07 +03:00
for ( const auto & winfo : winfos ) {
ggml_backend_tensor_get ( winfo . tensor , winfo . ptr , winfo . offset , winfo . size );
2026-05-02 18:03:25 +03:00
}
}
2025-01-03 10:18:53 +02:00
void write ( const void * src , size_t size ) override {
if ( size > buf_size ) {
throw std :: runtime_error ( "unexpectedly reached end of buffer" );
}
memcpy ( ptr , src , size );
ptr += size ;
size_written += size ;
buf_size -= size ;
}
2026-05-05 06:35:07 +03:00
void write_tensor ( ggml_tensor * tensor , size_t offset , size_t size ) override {
2025-01-03 10:18:53 +02:00
if ( size > buf_size ) {
throw std :: runtime_error ( "unexpectedly reached end of buffer" );
}
2026-05-02 18:03:25 +03:00
// save the write for later during destruction
winfos . push_back ({ tensor , ptr , size , offset });
2025-01-03 10:18:53 +02:00
ptr += size ;
size_written += size ;
buf_size -= size ;
}
2025-03-13 12:35:44 +02:00
size_t n_bytes () override {
2025-01-03 10:18:53 +02:00
return size_written ;
}
2025-03-13 12:35:44 +02:00
private :
uint8_t * ptr ;
size_t buf_size = 0 ;
size_t size_written = 0 ;
2026-05-02 18:03:25 +03:00
struct write_info {
2026-05-05 06:35:07 +03:00
ggml_tensor * tensor ;
2026-05-02 18:03:25 +03:00
uint8_t * ptr ;
size_t size ;
size_t offset ;
};
std :: vector < write_info > winfos ;
2025-01-03 10:18:53 +02:00
};
2026-05-05 06:35:07 +03:00
class llama_io_read_host : public llama_io_read_i {
2025-03-13 12:35:44 +02:00
public :
2026-05-05 06:35:07 +03:00
llama_io_read_host ( const uint8_t * p , size_t len ) : ptr ( p ), buf_size ( len ) {}
2025-01-03 10:18:53 +02:00
2026-05-05 06:35:07 +03:00
~ llama_io_read_host () {
2026-05-02 18:03:25 +03:00
// flush the reads
2026-05-05 06:35:07 +03:00
for ( const auto & rinfo : rinfos ) {
ggml_backend_tensor_set ( rinfo . tensor , rinfo . ptr , rinfo . offset , rinfo . size );
2026-05-02 18:03:25 +03:00
}
}
void read ( void * dst , size_t size ) override {
2025-01-03 10:18:53 +02:00
if ( size > buf_size ) {
throw std :: runtime_error ( "unexpectedly reached end of buffer" );
}
2026-05-02 18:03:25 +03:00
memcpy ( dst , ptr , size );
2025-01-03 10:18:53 +02:00
ptr += size ;
size_read += size ;
buf_size -= size ;
}
2026-05-02 18:03:25 +03:00
void read_tensor ( ggml_tensor * tensor , size_t offset , size_t size ) override {
if ( size > buf_size ) {
throw std :: runtime_error ( "unexpectedly reached end of buffer" );
}
// save for later during destruction
rinfos . push_back ({ tensor , ptr , size , offset });
ptr += size ;
size_read += size ;
buf_size -= size ;
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
size_t n_bytes () override {
2025-01-03 10:18:53 +02:00
return size_read ;
}
2025-03-13 12:35:44 +02:00
private :
const uint8_t * ptr ;
size_t buf_size = 0 ;
size_t size_read = 0 ;
2026-05-02 18:03:25 +03:00
struct read_info {
ggml_tensor * tensor ;
const uint8_t * ptr ;
size_t size ;
size_t offset ;
};
std :: vector < read_info > rinfos ;
2025-01-03 10:18:53 +02:00
};
2025-03-13 12:35:44 +02:00
class llama_io_write_file : public llama_io_write_i {
public :
llama_io_write_file ( llama_file * f ) : file ( f ) {}
2025-01-03 10:18:53 +02:00
void write ( const void * src , size_t size ) override {
file -> write_raw ( src , size );
size_written += size ;
}
2026-05-05 06:35:07 +03:00
void write_tensor ( ggml_tensor * tensor , size_t offset , size_t size ) override {
2025-01-03 10:18:53 +02:00
temp_buffer . resize ( size );
ggml_backend_tensor_get ( tensor , temp_buffer . data (), offset , size );
write ( temp_buffer . data (), temp_buffer . size ());
}
2025-03-13 12:35:44 +02:00
size_t n_bytes () override {
2025-01-03 10:18:53 +02:00
return size_written ;
}
2025-03-13 12:35:44 +02:00
private :
llama_file * file ;
size_t size_written = 0 ;
std :: vector < uint8_t > temp_buffer ;
2025-01-03 10:18:53 +02:00
};
2025-03-13 12:35:44 +02:00
class llama_io_read_file : public llama_io_read_i {
public :
llama_io_read_file ( llama_file * f ) : file ( f ) {}
2025-01-03 10:18:53 +02:00
2026-05-02 18:03:25 +03:00
void read ( void * dst , size_t size ) override {
2025-01-03 10:18:53 +02:00
file -> read_raw ( dst , size );
size_read += size ;
}
2026-05-02 18:03:25 +03:00
void read_tensor ( ggml_tensor * tensor , size_t offset , size_t size ) override {
2025-01-03 10:18:53 +02:00
temp_buffer . resize ( size );
2026-05-02 18:03:25 +03:00
read ( temp_buffer . data (), size );
ggml_backend_tensor_set ( tensor , temp_buffer . data (), offset , size );
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
size_t n_bytes () override {
2025-01-03 10:18:53 +02:00
return size_read ;
}
2025-03-13 12:35:44 +02:00
private :
llama_file * file ;
size_t size_read = 0 ;
std :: vector < uint8_t > temp_buffer ;
2025-01-03 10:18:53 +02:00
};
2026-05-05 06:35:07 +03:00
class llama_io_write_device : public llama_io_write_i {
public :
llama_io_write_device ( uint8_t * p , size_t len , llama_memory_buffers & mbufs ) : ptr ( p ), buf_size ( len ), mbufs ( mbufs ) {
}
~ llama_io_write_device () {
llama_memory_buffers mbufs_new ;
for ( const auto & winfo : winfos ) {
auto * buft = ggml_backend_buffer_get_type ( winfo . tensor -> buffer );
mbufs_new [ buft ]. n_tensors ++ ;
mbufs_new [ buft ]. total_size += winfo . size ;
}
for ( auto & [ buft , mbuf ] : mbufs_new ) {
ggml_init_params params = {
/*.mem_size =*/ 2 * mbuf . n_tensors * ggml_tensor_overhead (),
/*.mem_buffer =*/ NULL ,
/*.no_alloc =*/ true ,
};
mbuf . ctx . reset ( ggml_init ( params ));
mbuf . org . reserve ( mbuf . n_tensors );
mbuf . cpy . reserve ( mbuf . n_tensors );
}
for ( const auto & winfo : winfos ) {
auto * buft = ggml_backend_buffer_get_type ( winfo . tensor -> buffer );
const int64_t n = winfo . size / ggml_element_size ( winfo . tensor );
auto & mbuf = mbufs_new [ buft ];
mbuf . org . push_back ( ggml_view_1d ( mbuf . ctx . get (), winfo . tensor , n , winfo . offset ));
mbuf . cpy . push_back ( ggml_new_tensor_1d ( mbuf . ctx . get (), winfo . tensor -> type , n ));
}
for ( auto & [ buft , mbuf ] : mbufs_new ) {
auto & mbuf_cur = mbufs [ buft ];
2026-05-07 21:43:40 +03:00
bool need_alloc = false ;
need_alloc = need_alloc || ( ! mbuf_cur . buf );
need_alloc = need_alloc || ( mbuf_cur . org . size () != mbuf . org . size ());
need_alloc = need_alloc || ( mbuf_cur . total_size != mbuf . total_size );
if ( ! need_alloc ) {
for ( size_t i = 0 ; i < mbuf_cur . org . size (); ++ i ) {
auto * org0 = mbuf_cur . org [ i ];
auto * org1 = mbuf . org [ i ];
if ( ! ggml_are_same_shape ( org0 , org1 )) {
need_alloc = true ;
break ;
}
if ( org0 -> view_src != org1 -> view_src || org0 -> view_offs != org1 -> view_offs ) {
need_alloc = true ;
break ;
}
}
}
if ( need_alloc ) {
2026-05-11 19:09:43 +03:00
if ( ! mbuf_cur . buf || mbuf_cur . total_size != mbuf . total_size ) {
mbuf_cur = std :: move ( mbuf );
2026-05-05 06:35:07 +03:00
2026-05-11 19:09:43 +03:00
mbuf_cur . buf . reset ( ggml_backend_alloc_ctx_tensors_from_buft ( mbuf_cur . ctx . get (), buft ));
2026-05-05 06:35:07 +03:00
2026-05-11 19:09:43 +03:00
LLAMA_LOG_INFO ( "%s: allocated '%s' buffer %.3f MiB \n " , __func__ , ggml_backend_buft_name ( buft ), mbuf . total_size / 1024.0 / 1024.0 );
} else {
//LLAMA_LOG_INFO("%s: reallocating tensors in '%s' buffer %.3f MiB\n", __func__, ggml_backend_buft_name(buft), mbuf.total_size/1024.0/1024.0);
// save the old buffer and allocate the new tensors in it
auto buf = std :: move ( mbuf_cur . buf );
mbuf_cur = std :: move ( mbuf );
ggml_tallocr talloc = ggml_tallocr_new ( buf . get ());
for ( size_t i = 0 ; i < mbuf_cur . org . size (); ++ i ) {
ggml_backend_view_init ( mbuf_cur . org [ i ]);
ggml_tallocr_alloc ( & talloc , mbuf_cur . cpy [ i ]);
}
mbuf_cur . buf = std :: move ( buf );
}
2026-05-05 06:35:07 +03:00
}
for ( size_t i = 0 ; i < mbuf_cur . org . size (); ++ i ) {
ggml_backend_tensor_copy ( mbuf_cur . org [ i ], mbuf_cur . cpy [ i ]);
}
}
}
void write ( const void * src , size_t size ) override {
if ( size > buf_size ) {
throw std :: runtime_error ( "unexpectedly reached end of buffer" );
}
memcpy ( ptr , src , size );
ptr += size ;
size_written += size ;
buf_size -= size ;
}
void write_tensor ( ggml_tensor * tensor , size_t offset , size_t size ) override {
// save the write for later during destruction
winfos . push_back ({ tensor , ptr , size , offset });
}
size_t n_bytes () override {
return size_written ;
}
private :
uint8_t * ptr ;
size_t buf_size = 0 ;
size_t size_written = 0 ;
struct write_info {
ggml_tensor * tensor ;
uint8_t * ptr ;
size_t size ;
size_t offset ;
};
std :: vector < write_info > winfos ;
llama_memory_buffers & mbufs ;
};
class llama_io_read_device : public llama_io_read_i {
public :
llama_io_read_device ( const uint8_t * p , size_t len , const llama_memory_buffers & mbufs ) : ptr ( p ), buf_size ( len ), mbufs ( mbufs ) {
}
~ llama_io_read_device () {
llama_memory_buffers mbufs_new ;
for ( const auto & rinfo : rinfos ) {
auto * buft = ggml_backend_buffer_get_type ( rinfo . tensor -> buffer );
mbufs_new [ buft ]. n_tensors ++ ;
mbufs_new [ buft ]. total_size += rinfo . size ;
}
2026-05-07 21:43:40 +03:00
for ( auto & [ buft , mbuf ] : mbufs_new ) {
ggml_init_params params = {
/*.mem_size =*/ mbuf . n_tensors * ggml_tensor_overhead (),
/*.mem_buffer =*/ NULL ,
/*.no_alloc =*/ true ,
};
mbuf . ctx . reset ( ggml_init ( params ));
mbuf . org . reserve ( mbuf . n_tensors );
}
for ( const auto & rinfo : rinfos ) {
auto * buft = ggml_backend_buffer_get_type ( rinfo . tensor -> buffer );
const int64_t n = rinfo . size / ggml_element_size ( rinfo . tensor );
auto & mbuf = mbufs_new [ buft ];
mbuf . org . push_back ( ggml_view_1d ( mbuf . ctx . get (), rinfo . tensor , n , rinfo . offset ));
2026-05-11 19:09:43 +03:00
ggml_backend_view_init ( mbuf . org . back ());
2026-05-07 21:43:40 +03:00
}
2026-05-05 06:35:07 +03:00
for ( auto & [ buft , mbuf ] : mbufs_new ) {
const auto & mbuf_cur = mbufs . at ( buft );
if ( ! mbuf_cur . buf || mbuf_cur . n_tensors != mbuf . n_tensors || mbuf_cur . total_size != mbuf . total_size ) {
GGML_ABORT ( "%s: memory buffer mismatch \n " , __func__ );
}
for ( size_t i = 0 ; i < mbuf_cur . org . size (); ++ i ) {
2026-05-07 21:43:40 +03:00
ggml_backend_tensor_copy ( mbuf_cur . cpy [ i ], mbuf . org [ i ]);
2026-05-05 06:35:07 +03:00
}
}
2026-05-07 21:43:40 +03:00
GGML_ASSERT ( buf_size == 0 );
2026-05-05 06:35:07 +03:00
}
void read ( void * dst , size_t size ) override {
if ( size > buf_size ) {
throw std :: runtime_error ( "unexpectedly reached end of buffer" );
}
memcpy ( dst , ptr , size );
ptr += size ;
size_read += size ;
buf_size -= size ;
}
void read_tensor ( ggml_tensor * tensor , size_t offset , size_t size ) override {
// save for later during destruction
rinfos . push_back ({ tensor , ptr , size , offset });
}
size_t n_bytes () override {
return size_read ;
}
private :
const uint8_t * ptr ;
size_t buf_size = 0 ;
size_t size_read = 0 ;
struct read_info {
ggml_tensor * tensor ;
const uint8_t * ptr ;
size_t size ;
size_t offset ;
};
std :: vector < read_info > rinfos ;
const llama_memory_buffers & mbufs ;
};
2025-03-13 12:35:44 +02:00
size_t llama_context :: state_get_size () {
2026-05-05 06:35:07 +03:00
llama_io_write_dummy io ( false );
2025-01-03 10:18:53 +02:00
try {
2025-03-13 12:35:44 +02:00
return state_write_data ( io );
2025-01-03 10:18:53 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error getting state size: %s \n " , __func__ , err . what ());
return 0 ;
}
}
2025-03-13 12:35:44 +02:00
size_t llama_context :: state_get_data ( uint8_t * dst , size_t size ) {
2026-05-05 06:35:07 +03:00
llama_io_write_host io ( dst , size );
2025-03-13 12:35:44 +02:00
try {
return state_write_data ( io );
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error saving state: %s \n " , __func__ , err . what ());
return 0 ;
}
2025-01-03 10:18:53 +02:00
}
2025-03-13 12:35:44 +02:00
size_t llama_context :: state_set_data ( const uint8_t * src , size_t size ) {
2026-05-05 06:35:07 +03:00
llama_io_read_host io ( src , size );
2025-01-03 10:18:53 +02:00
try {
2025-03-13 12:35:44 +02:00
return state_read_data ( io );
2025-01-03 10:18:53 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error loading state: %s \n " , __func__ , err . what ());
return 0 ;
}
}
2026-05-05 06:35:07 +03:00
static constexpr uint32_t io_magic = 0xaf143cd8 ;
2025-08-14 14:59:50 +03:00
size_t llama_context :: state_seq_get_size ( llama_seq_id seq_id , llama_state_seq_flags flags ) {
2026-05-05 06:35:07 +03:00
llama_io_write_dummy io ( flags & LLAMA_STATE_SEQ_FLAGS_ON_DEVICE );
2025-03-13 12:35:44 +02:00
try {
2026-05-05 06:35:07 +03:00
io . write ( & io_magic , sizeof ( io_magic ));
io . write ( & seq_id , sizeof ( seq_id ));
2025-08-14 14:59:50 +03:00
return state_seq_write_data ( io , seq_id , flags );
2025-03-13 12:35:44 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error getting state size: %s \n " , __func__ , err . what ());
return 0 ;
}
}
2025-08-14 14:59:50 +03:00
size_t llama_context :: state_seq_get_data ( llama_seq_id seq_id , uint8_t * dst , size_t size , llama_state_seq_flags flags ) {
2026-05-05 06:35:07 +03:00
std :: unique_ptr < llama_io_write_i > io ;
if ( flags & LLAMA_STATE_SEQ_FLAGS_ON_DEVICE ) {
io = std :: make_unique < llama_io_write_device > ( dst , size , mem_storage [ seq_id ]);
} else {
io = std :: make_unique < llama_io_write_host > ( dst , size );
}
2025-03-13 12:35:44 +02:00
try {
2026-05-05 06:35:07 +03:00
io -> write ( & io_magic , sizeof ( io_magic ));
io -> write ( & seq_id , sizeof ( seq_id ));
return state_seq_write_data ( * io , seq_id , flags );
2025-03-13 12:35:44 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error saving state: %s \n " , __func__ , err . what ());
return 0 ;
}
}
2025-08-14 14:59:50 +03:00
size_t llama_context :: state_seq_set_data ( llama_seq_id seq_id , const uint8_t * src , size_t size , llama_state_seq_flags flags ) {
2026-05-05 06:35:07 +03:00
std :: unique_ptr < llama_io_read_i > io ;
if ( flags & LLAMA_STATE_SEQ_FLAGS_ON_DEVICE ) {
// create a temporary io to read the magic and the src seq_id
io = std :: make_unique < llama_io_read_host > ( src , size );
uint32_t magic_read ;
io -> read ( & magic_read , sizeof ( magic_read ));
if ( io_magic != magic_read ) {
throw std :: runtime_error ( "wrong sequence state magic" );
}
llama_seq_id seq_id_read ;
io -> read ( & seq_id_read , sizeof ( seq_id_read ));
GGML_ASSERT ( mem_storage . find ( seq_id_read ) != mem_storage . end ());
io = std :: make_unique < llama_io_read_device > ( src , size , mem_storage [ seq_id_read ]);
} else {
io = std :: make_unique < llama_io_read_host > ( src , size );
}
2025-03-13 12:35:44 +02:00
try {
2026-05-05 06:35:07 +03:00
uint32_t magic_read ;
io -> read ( & magic_read , sizeof ( magic_read ));
if ( io_magic != magic_read ) {
throw std :: runtime_error ( "wrong sequence state magic" );
}
llama_seq_id seq_id_read ;
io -> read ( & seq_id_read , sizeof ( seq_id_read ));
return state_seq_read_data ( * io , seq_id , flags );
2025-03-13 12:35:44 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error loading state: %s \n " , __func__ , err . what ());
return 0 ;
}
}
bool llama_context :: state_load_file ( const char * filepath , llama_token * tokens_out , size_t n_token_capacity , size_t * n_token_count_out ) {
llama_file file ( filepath , "rb" );
2025-01-03 10:18:53 +02:00
// sanity checks
{
const uint32_t magic = file . read_u32 ();
const uint32_t version = file . read_u32 ();
if ( magic != LLAMA_SESSION_MAGIC || version != LLAMA_SESSION_VERSION ) {
LLAMA_LOG_ERROR ( "%s: unknown (magic, version) for session file: %08x, %08x \n " , __func__ , magic , version );
return false ;
}
}
// load the prompt
{
const uint32_t n_token_count = file . read_u32 ();
if ( n_token_count > n_token_capacity ) {
LLAMA_LOG_ERROR ( "%s: token count in session file exceeded capacity! %u > %zu \n " , __func__ , n_token_count , n_token_capacity );
return false ;
}
file . read_raw ( tokens_out , sizeof ( llama_token ) * n_token_count );
* n_token_count_out = n_token_count ;
}
// restore the context state
{
const size_t n_state_size_cur = file . size () - file . tell ();
2025-03-13 12:35:44 +02:00
llama_io_read_file io ( & file );
const size_t n_read = state_read_data ( io );
2025-01-03 10:18:53 +02:00
if ( n_read != n_state_size_cur ) {
LLAMA_LOG_ERROR ( "%s: did not read all of the session file data! size %zu, got %zu \n " , __func__ , n_state_size_cur , n_read );
return false ;
}
}
2025-03-13 12:35:44 +02:00
2025-01-03 10:18:53 +02:00
return true ;
}
2025-03-13 12:35:44 +02:00
bool llama_context :: state_save_file ( const char * filepath , const llama_token * tokens , size_t n_token_count ) {
llama_file file ( filepath , "wb" );
2025-01-03 10:18:53 +02:00
file . write_u32 ( LLAMA_SESSION_MAGIC );
file . write_u32 ( LLAMA_SESSION_VERSION );
// save the prompt
file . write_u32 (( uint32_t ) n_token_count );
file . write_raw ( tokens , sizeof ( llama_token ) * n_token_count );
// save the context state using stream saving
2025-03-13 12:35:44 +02:00
llama_io_write_file io ( & file );
state_write_data ( io );
2025-01-03 10:18:53 +02:00
return true ;
}
2025-03-13 12:35:44 +02:00
size_t llama_context :: state_seq_load_file ( llama_seq_id seq_id , const char * filepath , llama_token * tokens_out , size_t n_token_capacity , size_t * n_token_count_out ) {
2025-01-03 10:18:53 +02:00
llama_file file ( filepath , "rb" );
// version checks
{
const uint32_t magic = file . read_u32 ();
const uint32_t version = file . read_u32 ();
if ( magic != LLAMA_STATE_SEQ_MAGIC || version != LLAMA_STATE_SEQ_VERSION ) {
LLAMA_LOG_ERROR ( "%s: unknown (magic, version) for sequence state file: %08x, %08x \n " , __func__ , magic , version );
return 0 ;
}
}
// load the prompt
{
const uint32_t n_token_count = file . read_u32 ();
if ( n_token_count > n_token_capacity ) {
LLAMA_LOG_ERROR ( "%s: token count in sequence state file exceeded capacity! %u > %zu \n " , __func__ , n_token_count , n_token_capacity );
return 0 ;
}
file . read_raw ( tokens_out , sizeof ( llama_token ) * n_token_count );
* n_token_count_out = n_token_count ;
}
// restore the context state
{
const size_t state_size = file . size () - file . tell ();
2025-03-13 12:35:44 +02:00
llama_io_read_file io ( & file );
2025-08-14 14:59:50 +03:00
const size_t nread = state_seq_read_data ( io , seq_id , 0 );
2025-01-03 10:18:53 +02:00
if ( ! nread ) {
LLAMA_LOG_ERROR ( "%s: failed to restore sequence state \n " , __func__ );
return 0 ;
}
GGML_ASSERT ( nread <= state_size );
GGML_ASSERT ( nread + sizeof ( uint32_t ) * 3 + sizeof ( llama_token ) * * n_token_count_out == file . tell ());
}
return file . tell ();
}
2025-03-13 12:35:44 +02:00
size_t llama_context :: state_seq_save_file ( llama_seq_id seq_id , const char * filepath , const llama_token * tokens , size_t n_token_count ) {
llama_file file ( filepath , "wb" );
file . write_u32 ( LLAMA_STATE_SEQ_MAGIC );
file . write_u32 ( LLAMA_STATE_SEQ_VERSION );
// save the prompt
file . write_u32 (( uint32_t ) n_token_count );
file . write_raw ( tokens , sizeof ( llama_token ) * n_token_count );
// save the context state using stream saving
llama_io_write_file io ( & file );
2025-08-14 14:59:50 +03:00
state_seq_write_data ( io , seq_id , 0 );
2025-03-13 12:35:44 +02:00
const size_t res = file . tell ();
GGML_ASSERT ( res == sizeof ( uint32_t ) * 3 + sizeof ( llama_token ) * n_token_count + io . n_bytes ());
return res ;
}
size_t llama_context :: state_write_data ( llama_io_write_i & io ) {
LLAMA_LOG_DEBUG ( "%s: writing state \n " , __func__ );
// write model info
{
LLAMA_LOG_DEBUG ( "%s: - writing model info \n " , __func__ );
const std :: string arch_str = llm_arch_name ( model . arch );
io . write_string ( arch_str );
// TODO: add more model-specific info which should prevent loading the session file if not identical
}
2025-06-05 15:29:22 +03:00
if ( memory != nullptr ) {
2025-08-26 12:47:00 +03:00
LLAMA_LOG_DEBUG ( "%s: - writing memory module \n " , __func__ );
2025-06-05 15:29:22 +03:00
memory -> state_write ( io );
2025-05-14 19:18:18 +03:00
}
2025-03-13 12:35:44 +02:00
return io . n_bytes ();
}
size_t llama_context :: state_read_data ( llama_io_read_i & io ) {
LLAMA_LOG_DEBUG ( "%s: reading state \n " , __func__ );
// read model info
{
LLAMA_LOG_DEBUG ( "%s: - reading model info \n " , __func__ );
const std :: string cur_arch_str = llm_arch_name ( model . arch );
std :: string arch_str ;
io . read_string ( arch_str );
if ( cur_arch_str != arch_str ) {
throw std :: runtime_error ( format ( "wrong model arch: '%s' instead of '%s'" , arch_str . c_str (), cur_arch_str . c_str ()));
}
// TODO: add more info which needs to be identical but which is not verified otherwise
}
2025-05-12 15:12:27 +03:00
if ( memory ) {
2025-08-26 12:47:00 +03:00
LLAMA_LOG_DEBUG ( "%s: - reading memory module \n " , __func__ );
2025-05-02 17:48:36 +03:00
2025-06-05 15:29:22 +03:00
memory -> state_read ( io );
2025-05-12 15:12:27 +03:00
}
2025-03-13 12:35:44 +02:00
return io . n_bytes ();
}
2025-08-14 14:59:50 +03:00
size_t llama_context :: state_seq_write_data ( llama_io_write_i & io , llama_seq_id seq_id , llama_state_seq_flags flags ) {
2025-03-13 12:35:44 +02:00
GGML_UNUSED ( seq_id );
2025-05-12 15:12:27 +03:00
if ( memory ) {
2025-08-14 14:59:50 +03:00
memory -> state_write ( io , seq_id , flags );
2025-05-12 15:12:27 +03:00
}
2025-03-13 12:35:44 +02:00
return io . n_bytes ();
}
2025-08-14 14:59:50 +03:00
size_t llama_context :: state_seq_read_data ( llama_io_read_i & io , llama_seq_id seq_id , llama_state_seq_flags flags ) {
2025-03-13 12:35:44 +02:00
GGML_UNUSED ( seq_id );
2025-05-12 15:12:27 +03:00
if ( memory ) {
2025-08-14 14:59:50 +03:00
memory -> state_read ( io , seq_id , flags );
2025-05-12 15:12:27 +03:00
}
2025-03-13 12:35:44 +02:00
return io . n_bytes ();
}
//
// perf
//
llama_perf_context_data llama_context :: perf_get_data () const {
llama_perf_context_data data = {};
data . t_start_ms = 1e-3 * t_start_us ;
data . t_load_ms = 1e-3 * t_load_us ;
data . t_p_eval_ms = 1e-3 * t_p_eval_us ;
data . t_eval_ms = 1e-3 * t_eval_us ;
data . n_p_eval = std :: max ( 1 , n_p_eval );
data . n_eval = std :: max ( 1 , n_eval );
2025-07-17 19:08:33 +03:00
data . n_reused = std :: max ( 0 , n_reused );
2025-03-13 12:35:44 +02:00
return data ;
}
void llama_context :: perf_reset () {
t_start_us = ggml_time_us ();
t_eval_us = n_eval = 0 ;
t_p_eval_us = n_p_eval = 0 ;
2025-07-17 19:08:33 +03:00
n_reused = 0 ;
2025-03-13 12:35:44 +02:00
}
2026-04-21 09:54:36 +03:00
llama_memory_breakdown llama_context :: memory_breakdown () const {
2025-09-24 16:53:48 +02:00
std :: map < ggml_backend_buffer_type_t , llama_memory_breakdown_data > ret ;
2025-12-15 09:24:59 +01:00
for ( const auto & [ buft , size ] : model . memory_breakdown ()) {
ret [ buft ]. model += size ;
2025-09-24 16:53:48 +02:00
}
2025-12-15 09:24:59 +01:00
if ( memory ) {
for ( const auto & [ buft , size ] : memory -> memory_breakdown ()) {
ret [ buft ]. context += size ;
}
2025-09-24 16:53:48 +02:00
}
2025-12-15 09:24:59 +01:00
if ( model . hparams . no_alloc ) {
for ( size_t i = 0 ; i < backends . size (); ++ i ) {
ggml_backend_t backend = backends [ i ]. get ();
ggml_backend_buffer_type_t buft = ggml_backend_sched_get_buffer_type ( sched . get (), backend );
ret [ buft ]. compute += backend_buf_exp_size [ i ];
}
} else {
for ( const auto & backend_ptr : backends ) {
ggml_backend_t backend = backend_ptr . get ();
ggml_backend_buffer_type_t buft = ggml_backend_sched_get_buffer_type ( sched . get (), backend );
ret [ buft ]. compute += ggml_backend_sched_get_buffer_size ( sched . get (), backend );
}
2025-09-24 16:53:48 +02:00
}
return ret ;
}
2025-05-12 14:44:49 +02:00
//
// training
//
static void llama_set_param ( struct ggml_tensor * tensor , llama_opt_param_filter param_filter , void * userdata ) {
if ( ! tensor || tensor -> type != GGML_TYPE_F32 ) {
return ;
}
if ( ! param_filter ( tensor , userdata )) {
return ;
}
if ( strcmp ( tensor -> name , "token_embd.weight" ) == 0 ) {
return ; // FIXME
}
if ( strcmp ( tensor -> name , "rope_freqs.weight" ) == 0 ) {
return ; // FIXME
}
ggml_set_param ( tensor );
}
void llama_context :: opt_init ( struct llama_model * model , struct llama_opt_params lopt_params ) {
GGML_ASSERT ( ! opt_ctx );
model -> hparams . n_ctx_train = lopt_params . n_ctx_train > 0 ? lopt_params . n_ctx_train : n_ctx ();
const uint32_t n_batch = std :: min ( this -> n_batch (), model -> hparams . n_ctx_train );
const uint32_t n_ubatch = std :: min ( this -> n_ubatch (), n_batch );
GGML_ASSERT ( model -> hparams . n_ctx_train % n_batch == 0 );
GGML_ASSERT ( n_batch % n_ubatch == 0 );
ggml_opt_params opt_params = ggml_opt_default_params ( sched . get (), GGML_OPT_LOSS_TYPE_CROSS_ENTROPY );
opt_params . opt_period = n_batch / n_ubatch ;
opt_params . get_opt_pars = lopt_params . get_opt_pars ;
opt_params . get_opt_pars_ud = lopt_params . get_opt_pars_ud ;
2025-08-14 03:03:57 -07:00
opt_params . optimizer = lopt_params . optimizer_type ;
2025-05-12 14:44:49 +02:00
opt_ctx = ggml_opt_init ( opt_params );
llama_opt_param_filter param_filter = lopt_params . param_filter ;
void * param_filter_ud = lopt_params . param_filter_ud ;
//llama_set_param(model->tok_embd, param_filter, param_filter_ud); // FIXME
llama_set_param ( model -> type_embd , param_filter , param_filter_ud );
llama_set_param ( model -> pos_embd , param_filter , param_filter_ud );
llama_set_param ( model -> tok_norm , param_filter , param_filter_ud );
llama_set_param ( model -> tok_norm_b , param_filter , param_filter_ud );
llama_set_param ( model -> output_norm , param_filter , param_filter_ud );
llama_set_param ( model -> output_norm_b , param_filter , param_filter_ud );
llama_set_param ( model -> output , param_filter , param_filter_ud );
llama_set_param ( model -> output_b , param_filter , param_filter_ud );
llama_set_param ( model -> output_norm_enc , param_filter , param_filter_ud );
llama_set_param ( model -> cls , param_filter , param_filter_ud );
llama_set_param ( model -> cls_b , param_filter , param_filter_ud );
llama_set_param ( model -> cls_out , param_filter , param_filter_ud );
llama_set_param ( model -> cls_out_b , param_filter , param_filter_ud );
2026-02-19 02:52:21 -05:00
llama_set_param ( model -> cls_norm , param_filter , param_filter_ud );
2025-05-12 14:44:49 +02:00
for ( struct llama_layer & layer : model -> layers ) {
for ( size_t i = 0 ; i < sizeof ( layer ) / sizeof ( struct ggml_tensor * ); ++ i ) {
llama_set_param ( reinterpret_cast < struct ggml_tensor **> ( & layer )[ i ], param_filter , param_filter_ud );
}
}
}
void llama_context :: opt_epoch_iter (
ggml_opt_dataset_t dataset ,
ggml_opt_result_t result ,
const std :: vector < llama_token > & tokens ,
const std :: vector < llama_token > & labels_sparse ,
llama_batch & batch ,
ggml_opt_epoch_callback callback ,
bool train ,
int64_t idata_in_loop ,
int64_t ndata_in_loop ,
int64_t t_loop_start ) {
GGML_ASSERT ( opt_ctx );
const uint32_t n_ctx = llama_model_n_ctx_train ( & model );
const uint32_t n_batch = std :: min ( this -> n_batch (), n_ctx );
const uint32_t n_ubatch = std :: min ( this -> n_ubatch (), n_batch );
2025-06-06 14:11:15 +03:00
memory -> clear ( true );
2025-05-12 14:44:49 +02:00
for ( uint32_t pos_ctx = 0 ; pos_ctx < n_ctx ; pos_ctx += n_batch ) {
batch . n_tokens = n_batch ;
for ( uint32_t pos_batch = 0 ; pos_batch < n_batch ; ++ pos_batch ) {
batch . token [ pos_batch ] = tokens [ pos_ctx + pos_batch ];
batch . pos [ pos_batch ] = pos_ctx + pos_batch ;
batch . n_seq_id [ pos_batch ] = 1 ;
batch . seq_id [ pos_batch ][ 0 ] = 0 ;
batch . logits [ pos_batch ] = true ;
}
2025-11-07 19:27:58 +01:00
if ( ! balloc -> init ( batch , model . vocab , nullptr , model . hparams . n_embd_inp (), cparams . kv_unified ? LLAMA_MAX_SEQ : cparams . n_seq_max , true )) {
2025-06-20 10:14:14 +03:00
LLAMA_LOG_ERROR ( "%s: failed to initialize batch \n " , __func__ );
return ;
}
const uint32_t n_tokens_all = balloc -> get_n_tokens ();
2025-05-12 14:44:49 +02:00
n_queued_tokens += n_tokens_all ;
embd_seq . clear ();
2025-06-13 13:47:55 +03:00
uint32_t n_outputs_all = n_tokens_all ;
2025-05-12 14:44:49 +02:00
2025-06-21 08:03:46 +03:00
auto mctx = memory -> init_batch ( * balloc , cparams . n_ubatch , true );
if ( ! mctx || mctx -> get_status () != LLAMA_MEMORY_STATUS_SUCCESS ) {
2025-05-31 10:24:04 +03:00
LLAMA_LOG_ERROR ( "%s: could not initialize batch \n " , __func__ );
break ;
}
2025-05-12 14:44:49 +02:00
// reserve output buffer
2026-01-28 05:59:30 +01:00
if ( output_reserve ( n_outputs_all ) < n_outputs_all ) {
2025-06-13 13:47:55 +03:00
LLAMA_LOG_ERROR ( "%s: could not reserve space for batch with %d outputs \n " , __func__ , n_outputs_all );
2025-05-12 14:44:49 +02:00
GGML_ABORT ( "TODO: handle this error" );
};
2025-05-31 10:24:04 +03:00
uint32_t pos_batch = 0 ;
do {
2025-06-21 08:03:46 +03:00
const auto & ubatch = mctx -> get_ubatch ();
2025-05-12 14:44:49 +02:00
n_outputs = ubatch . n_tokens ;
2025-06-21 08:03:46 +03:00
if ( ! mctx -> apply ()) {
LLAMA_LOG_ERROR ( "%s: failed to update the memory context \n " , __func__ );
2025-05-31 10:24:04 +03:00
break ;
2025-05-12 14:44:49 +02:00
}
2025-07-17 19:08:33 +03:00
auto * res = gf_res_prev . get ();
2026-05-16 20:06:23 +08:00
const auto gparams = graph_params ( res , ubatch , mctx . get (), ctx_type_to_graph_type ( cparams . ctx_type ));
2025-07-17 19:08:33 +03:00
res -> reset ();
auto * gf = model . build_graph ( gparams );
2025-05-12 14:44:49 +02:00
struct ggml_context * ctx_compute_opt ;
{
const size_t size_gf = ggml_graph_size ( gf );
const size_t size_meta = 4 * size_gf * ggml_tensor_overhead () + 2 * ggml_graph_overhead_custom ( size_gf , /*grads = */ true );
struct ggml_init_params params = {
/*.mem_size =*/ size_meta ,
/*.mem_buffer =*/ nullptr ,
/*.no_alloc =*/ true ,
};
ctx_compute_opt = ggml_init ( params );
}
2026-01-23 18:22:34 +02:00
ggml_opt_prepare_alloc ( opt_ctx , ctx_compute_opt , gf , res -> get_inp_tokens (), res -> get_logits ());
2025-05-12 14:44:49 +02:00
ggml_opt_alloc ( opt_ctx , train );
2025-05-31 10:24:04 +03:00
2025-05-12 14:44:49 +02:00
res -> set_inputs ( & ubatch );
{
struct ggml_tensor * labels = ggml_opt_labels ( opt_ctx );
GGML_ASSERT ( labels -> ne [ 1 ] == n_ubatch );
ggml_set_zero ( labels );
const float onef = 1.0f ;
for ( uint32_t pos_ubatch = 0 ; pos_ubatch < n_ubatch ; ++ pos_ubatch ) {
const uint32_t ilabel = pos_ctx + pos_batch + pos_ubatch ;
GGML_ASSERT ( labels_sparse [ ilabel ] < labels -> ne [ 0 ]);
ggml_backend_tensor_set ( labels , & onef , ( pos_ubatch * labels -> ne [ 0 ] + labels_sparse [ ilabel ]) * sizeof ( float ), sizeof ( float ));
}
}
ggml_opt_eval ( opt_ctx , result );
if ( callback ) {
callback ( train , opt_ctx , dataset , result , idata_in_loop + ( pos_ctx + pos_batch ) / n_ubatch + 1 , ndata_in_loop , t_loop_start );
}
ggml_free ( ctx_compute_opt );
2025-05-31 10:24:04 +03:00
pos_batch += ubatch . n_tokens ;
2025-06-21 08:03:46 +03:00
} while ( mctx -> next ());
2025-05-31 10:24:04 +03:00
}
2025-05-12 14:44:49 +02:00
}
void llama_context :: opt_epoch (
ggml_opt_dataset_t dataset ,
ggml_opt_result_t result_train ,
ggml_opt_result_t result_eval ,
int64_t idata_split ,
ggml_opt_epoch_callback callback_train ,
ggml_opt_epoch_callback callback_eval ) {
const uint32_t n_ctx = this -> n_ctx ();
const uint32_t n_batch = std :: min ( cparams . n_batch , n_ctx );
const uint32_t n_ubatch = std :: min ( cparams . n_ubatch , n_batch );
const int64_t ndata = ggml_opt_dataset_ndata ( dataset );
GGML_ASSERT ( idata_split >= 0 );
GGML_ASSERT ( idata_split <= ndata );
const uint32_t ubatch_per_ctx = n_ctx / n_ubatch ;
struct llama_batch batch = llama_batch_init ( n_batch , 0 , 1 );
std :: vector < llama_token > tokens ( n_ctx );
std :: vector < llama_token > labels_sparse ( n_ctx );
int64_t idata = 0 ;
int64_t t_loop_start = ggml_time_us ();
int64_t ndata_in_loop = idata_split * ubatch_per_ctx ;
for (; idata < idata_split ; ++ idata ) {
constexpr bool train = true ;
const int64_t idata_in_loop = idata * ubatch_per_ctx ;
ggml_opt_dataset_get_batch_host ( dataset , tokens . data (), n_ctx * sizeof ( llama_token ), labels_sparse . data (), idata );
opt_epoch_iter ( dataset , result_train , tokens , labels_sparse , batch ,
callback_train , train , idata_in_loop , ndata_in_loop , t_loop_start );
}
t_loop_start = ggml_time_us ();
ndata_in_loop = ( ndata - idata_split ) * ubatch_per_ctx ;
for (; idata < ndata ; ++ idata ) {
constexpr bool train = false ;
const int64_t idata_in_loop = ( idata - idata_split ) * ubatch_per_ctx ;
ggml_opt_dataset_get_batch_host ( dataset , tokens . data (), n_ctx * sizeof ( llama_token ), labels_sparse . data (), idata );
opt_epoch_iter ( dataset , result_eval , tokens , labels_sparse , batch ,
callback_eval , train , idata_in_loop , ndata_in_loop , t_loop_start );
}
llama_batch_free ( batch );
}
2025-03-13 12:35:44 +02:00
//
// interface implementation
//
llama_context_params llama_context_default_params () {
llama_context_params result = {
/*.n_ctx =*/ 512 ,
/*.n_batch =*/ 2048 ,
/*.n_ubatch =*/ 512 ,
/*.n_seq_max =*/ 1 ,
2026-05-16 20:06:23 +08:00
/*.n_rs_seq =*/ 0 ,
2026-06-01 23:01:38 +08:00
/*.n_outputs_max =*/ 0 ,
2025-03-13 12:35:44 +02:00
/*.n_threads =*/ GGML_DEFAULT_N_THREADS , // TODO: better default
/*.n_threads_batch =*/ GGML_DEFAULT_N_THREADS ,
2026-05-16 20:06:23 +08:00
/*.ctx_type =*/ LLAMA_CONTEXT_TYPE_DEFAULT ,
2025-03-13 12:35:44 +02:00
/*.rope_scaling_type =*/ LLAMA_ROPE_SCALING_TYPE_UNSPECIFIED ,
/*.pooling_type =*/ LLAMA_POOLING_TYPE_UNSPECIFIED ,
/*.attention_type =*/ LLAMA_ATTENTION_TYPE_UNSPECIFIED ,
2025-08-30 16:32:10 +02:00
/*.flash_attn_type =*/ LLAMA_FLASH_ATTN_TYPE_AUTO ,
2025-03-13 12:35:44 +02:00
/*.rope_freq_base =*/ 0.0f ,
/*.rope_freq_scale =*/ 0.0f ,
/*.yarn_ext_factor =*/ - 1.0f ,
2025-09-14 23:00:59 +02:00
/*.yarn_attn_factor =*/ - 1.0f ,
/*.yarn_beta_fast =*/ - 1.0f ,
/*.yarn_beta_slow =*/ - 1.0f ,
2025-03-13 12:35:44 +02:00
/*.yarn_orig_ctx =*/ 0 ,
/*.defrag_thold =*/ - 1.0f ,
/*.cb_eval =*/ nullptr ,
/*.cb_eval_user_data =*/ nullptr ,
/*.type_k =*/ GGML_TYPE_F16 ,
/*.type_v =*/ GGML_TYPE_F16 ,
2025-05-08 14:26:50 +03:00
/*.abort_callback =*/ nullptr ,
/*.abort_callback_data =*/ nullptr ,
2025-03-13 12:35:44 +02:00
/*.embeddings =*/ false ,
/*.offload_kqv =*/ true ,
/*.no_perf =*/ true ,
2025-05-11 20:18:39 +08:00
/*.op_offload =*/ true ,
2025-05-20 08:05:46 +03:00
/*.swa_full =*/ true ,
2025-07-16 16:35:42 +03:00
/*.kv_unified =*/ false ,
2026-01-04 21:22:16 +01:00
/*.sampler =*/ nullptr ,
/*.n_sampler =*/ 0 ,
2026-06-07 20:50:54 +08:00
/*.ctx_other =*/ nullptr ,
2025-03-13 12:35:44 +02:00
};
return result ;
}
llama_context * llama_init_from_model (
llama_model * model ,
llama_context_params params ) {
if ( ! model ) {
LLAMA_LOG_ERROR ( "%s: model cannot be NULL \n " , __func__ );
return nullptr ;
}
if ( params . n_batch == 0 && params . n_ubatch == 0 ) {
LLAMA_LOG_ERROR ( "%s: n_batch and n_ubatch cannot both be zero \n " , __func__ );
return nullptr ;
}
if ( params . n_ctx == 0 && model -> hparams . n_ctx_train == 0 ) {
LLAMA_LOG_ERROR ( "%s: n_ctx and model->hparams.n_ctx_train cannot both be zero \n " , __func__ );
return nullptr ;
}
2025-08-30 16:32:10 +02:00
if ( params . flash_attn_type != LLAMA_FLASH_ATTN_TYPE_DISABLED && model -> arch == LLM_ARCH_GROK ) {
2025-03-13 12:35:44 +02:00
LLAMA_LOG_WARN ( "%s: flash_attn is not compatible with Grok - forcing off \n " , __func__ );
2025-08-30 16:32:10 +02:00
params . flash_attn_type = LLAMA_FLASH_ATTN_TYPE_DISABLED ;
2025-03-13 12:35:44 +02:00
}
2026-04-09 16:42:19 +02:00
if ( model -> split_mode () == LLAMA_SPLIT_MODE_TENSOR ) {
if ( params . flash_attn_type == LLAMA_FLASH_ATTN_TYPE_AUTO ) {
LLAMA_LOG_INFO ( "%s: enabling flash_attn since it is required for SPLIT_MODE_TENSOR \n " , __func__ );
params . flash_attn_type = LLAMA_FLASH_ATTN_TYPE_ENABLED ;
}
if ( params . flash_attn_type != LLAMA_FLASH_ATTN_TYPE_ENABLED ) {
LLAMA_LOG_ERROR ( "%s: SPLIT_MODE_TENSOR requires flash_attn to be enabled \n " , __func__ );
return nullptr ;
}
}
2026-07-31 09:03:30 +02:00
if (( model -> hparams . is_mla () || model -> arch == LLM_ARCH_DEEPSEEK4 ) && params . type_k != params . type_v ) {
LLAMA_LOG_ERROR ( "%s: model does not support different K (%s) and V (%s) cache types \n " , __func__ , ggml_type_name ( params . type_k ), ggml_type_name ( params . type_v ));
return nullptr ;
}
if ( ggml_is_quantized ( params . type_v ) && params . flash_attn_type != LLAMA_FLASH_ATTN_TYPE_ENABLED ) {
if ( params . flash_attn_type == LLAMA_FLASH_ATTN_TYPE_AUTO ) {
LLAMA_LOG_INFO ( "%s: enabling flash_attn since it is required for quantized V cache \n " , __func__ );
params . flash_attn_type = LLAMA_FLASH_ATTN_TYPE_ENABLED ;
}
if ( params . flash_attn_type == LLAMA_FLASH_ATTN_TYPE_DISABLED ) {
LLAMA_LOG_ERROR ( "%s: quantized V cache requires flash_attn to be enabled \n " , __func__ );
return nullptr ;
}
}
2026-04-08 15:08:57 +02:00
if ( params . flash_attn_type != LLAMA_FLASH_ATTN_TYPE_DISABLED && ggml_is_quantized ( params . type_k )) {
2025-08-30 16:32:10 +02:00
const uint32_t blck_size = ggml_blck_size ( params . type_k );
2026-06-05 11:09:36 +03:00
for ( uint32_t il = 0 ; il < model -> hparams . n_layer (); ++ il ) {
2026-03-09 22:22:39 +01:00
if ( model -> hparams . n_embd_head_k ( il ) % blck_size != 0 ) {
LLAMA_LOG_ERROR ( "%s: K cache type %s with block size %u does not divide n_embd_head_k=%u \n " ,
__func__ , ggml_type_name ( params . type_k ), blck_size , model -> hparams . n_embd_head_k ( il ));
return nullptr ;
}
2025-08-30 16:32:10 +02:00
}
}
2026-04-08 15:08:57 +02:00
if ( params . flash_attn_type != LLAMA_FLASH_ATTN_TYPE_DISABLED && ggml_is_quantized ( params . type_v )) {
2025-08-30 16:32:10 +02:00
const uint32_t blck_size = ggml_blck_size ( params . type_v );
2026-06-05 11:09:36 +03:00
for ( uint32_t il = 0 ; il < model -> hparams . n_layer (); ++ il ) {
2026-03-09 22:22:39 +01:00
if ( model -> hparams . n_embd_head_v ( il ) % blck_size != 0 ) {
LLAMA_LOG_ERROR ( "%s: V cache type %s with block size %u does not divide n_embd_head_v=%u \n " ,
__func__ , ggml_type_name ( params . type_v ), blck_size , model -> hparams . n_embd_head_v ( il ));
return nullptr ;
}
2025-08-30 16:32:10 +02:00
}
}
2025-10-20 15:44:21 +08:00
if ( params . pooling_type != LLAMA_POOLING_TYPE_UNSPECIFIED &&
params . pooling_type != model -> hparams . pooling_type ) {
2025-10-09 08:39:18 +02:00
//user-specified pooling-type is different from the model default
LLAMA_LOG_WARN ( "%s: model default pooling_type is [%d], but [%d] was specified \n " , __func__ ,
model -> hparams . pooling_type , params . pooling_type );
}
2026-08-10 00:53:46 -07:00
// router_layer >= 0 means n_layer_nextn is repurposed for a router layer, not real MTP
2026-05-16 20:06:23 +08:00
if ( params . ctx_type == LLAMA_CONTEXT_TYPE_MTP &&
2026-08-10 00:53:46 -07:00
( model -> hparams . n_layer_nextn == 0 || model -> hparams . router_layer >= 0 )) {
2026-05-16 20:06:23 +08:00
LLAMA_LOG_WARN ( "%s: context type MTP requested but model doesn't contain MTP layers \n " , __func__ );
return nullptr ;
}
2025-01-03 10:18:53 +02:00
try {
2025-03-13 12:35:44 +02:00
auto * ctx = new llama_context ( * model , params );
return ctx ;
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: failed to initialize the context: %s \n " , __func__ , err . what ());
}
return nullptr ;
}
// deprecated
llama_context * llama_new_context_with_model (
llama_model * model ,
llama_context_params params ) {
return llama_init_from_model ( model , params );
}
void llama_free ( llama_context * ctx ) {
delete ctx ;
}
uint32_t llama_n_ctx ( const llama_context * ctx ) {
return ctx -> n_ctx ();
}
2025-11-02 18:14:04 +02:00
uint32_t llama_n_ctx_seq ( const llama_context * ctx ) {
return ctx -> n_ctx_seq ();
}
2025-03-13 12:35:44 +02:00
uint32_t llama_n_batch ( const llama_context * ctx ) {
return ctx -> n_batch ();
}
uint32_t llama_n_ubatch ( const llama_context * ctx ) {
return ctx -> n_ubatch ();
}
uint32_t llama_n_seq_max ( const llama_context * ctx ) {
return ctx -> n_seq_max ();
}
2026-05-16 20:06:23 +08:00
uint32_t llama_n_rs_seq ( const llama_context * ctx ) {
return ctx -> get_cparams (). n_rs_seq ;
}
2025-03-13 12:35:44 +02:00
const llama_model * llama_get_model ( const llama_context * ctx ) {
return & ctx -> get_model ();
}
enum llama_pooling_type llama_pooling_type ( const llama_context * ctx ) {
return ctx -> pooling_type ();
}
void llama_attach_threadpool (
llama_context * ctx ,
ggml_threadpool_t threadpool ,
ggml_threadpool_t threadpool_batch ) {
ctx -> attach_threadpool ( threadpool , threadpool_batch );
}
void llama_detach_threadpool ( llama_context * ctx ) {
ctx -> detach_threadpool ();
}
void llama_set_n_threads ( llama_context * ctx , int32_t n_threads , int32_t n_threads_batch ) {
ctx -> set_n_threads ( n_threads , n_threads_batch );
}
int32_t llama_n_threads ( llama_context * ctx ) {
return ctx -> n_threads ();
}
int32_t llama_n_threads_batch ( llama_context * ctx ) {
return ctx -> n_threads_batch ();
}
void llama_set_abort_callback ( llama_context * ctx , bool ( * abort_callback )( void * data ), void * abort_callback_data ) {
ctx -> set_abort_callback ( abort_callback , abort_callback_data );
}
void llama_set_embeddings ( llama_context * ctx , bool embeddings ) {
ctx -> set_embeddings ( embeddings );
}
void llama_set_causal_attn ( llama_context * ctx , bool causal_attn ) {
ctx -> set_causal_attn ( causal_attn );
}
2025-03-14 13:47:05 +01:00
void llama_set_warmup ( llama_context * ctx , bool warmup ) {
ctx -> set_warmup ( warmup );
}
2025-03-13 12:35:44 +02:00
void llama_synchronize ( llama_context * ctx ) {
ctx -> synchronize ();
}
float * llama_get_logits ( llama_context * ctx ) {
ctx -> synchronize ();
return ctx -> get_logits ();
}
float * llama_get_logits_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
2026-01-04 21:22:16 +01:00
float * res = nullptr ;
res = ctx -> get_sampled_logits_ith ( i );
if ( ! res ) {
res = ctx -> get_logits_ith ( i );
}
return res ;
2025-03-13 12:35:44 +02:00
}
float * llama_get_embeddings ( llama_context * ctx ) {
ctx -> synchronize ();
return ctx -> get_embeddings ();
}
float * llama_get_embeddings_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
return ctx -> get_embeddings_ith ( i );
}
float * llama_get_embeddings_seq ( llama_context * ctx , llama_seq_id seq_id ) {
ctx -> synchronize ();
return ctx -> get_embeddings_seq ( seq_id );
}
2026-06-04 01:29:09 +08:00
void llama_set_embeddings_nextn ( llama_context * ctx , bool value , bool masked ) {
ctx -> set_embeddings_nextn ( value , masked );
2026-05-16 20:06:23 +08:00
}
2026-06-12 09:21:06 +02:00
void llama_set_embeddings_layer_inp ( llama_context * ctx , uint32_t lid , bool value ) {
ctx -> set_embeddings_layer_inp ( lid , value );
}
2026-06-21 16:33:18 +08:00
void llama_set_nextn_layer_offset ( llama_context * ctx , int32_t offset ) {
ctx -> set_nextn_layer_offset ( offset );
}
2026-06-07 20:50:54 +08:00
llama_memory_t llama_get_memory ( const struct llama_context * ctx ) {
if ( ! ctx ) {
return nullptr ;
}
return ctx -> get_memory ();
}
2026-06-04 01:29:09 +08:00
float * llama_get_embeddings_nextn ( llama_context * ctx ) {
2026-05-16 20:06:23 +08:00
ctx -> synchronize ();
2026-06-04 01:29:09 +08:00
return ctx -> get_embeddings_nextn ();
2026-05-16 20:06:23 +08:00
}
2026-06-04 01:29:09 +08:00
float * llama_get_embeddings_nextn_ith ( llama_context * ctx , int32_t i ) {
2026-05-16 20:06:23 +08:00
ctx -> synchronize ();
2026-06-04 01:29:09 +08:00
return ctx -> get_embeddings_nextn_ith ( i );
2026-05-16 20:06:23 +08:00
}
2026-06-12 09:21:06 +02:00
float * llama_get_embeddings_layer_inp ( llama_context * ctx , uint32_t lid ) {
ctx -> synchronize ();
return ctx -> get_embeddings_layer_inp ( lid );
}
2026-01-04 21:22:16 +01:00
bool llama_set_sampler ( llama_context * ctx , llama_seq_id seq_id , llama_sampler * smpl ) {
return ctx -> set_sampler ( seq_id , smpl );
}
llama_token llama_get_sampled_token_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
return ctx -> get_sampled_token_ith ( i );
}
float * llama_get_sampled_probs_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
return ctx -> get_sampled_probs_ith ( i );
}
float * llama_get_sampled_logits_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
return ctx -> get_sampled_logits_ith ( i );
}
llama_token * llama_get_sampled_candidates_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
return const_cast < llama_token *> ( ctx -> get_sampled_candidates_ith ( i ));
}
uint32_t llama_get_sampled_candidates_count_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
return static_cast < uint32_t > ( ctx -> get_sampled_candidates_count ( i ));
}
uint32_t llama_get_sampled_logits_count_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
return static_cast < uint32_t > ( ctx -> get_sampled_logits_count ( i ));
}
uint32_t llama_get_sampled_probs_count_ith ( llama_context * ctx , int32_t i ) {
ctx -> synchronize ();
return static_cast < uint32_t > ( ctx -> get_sampled_probs_count ( i ));
}
2026-03-12 13:26:00 +01:00
struct ggml_cgraph * llama_graph_reserve (
struct llama_context * ctx ,
uint32_t n_tokens ,
uint32_t n_seqs ,
uint32_t n_outputs ) {
2026-06-07 20:50:54 +08:00
auto memory = ctx -> get_memory ();
2026-03-12 13:26:00 +01:00
llama_memory_context_ptr mctx ;
if ( memory ) {
mctx = memory -> init_full ();
}
return ctx -> graph_reserve ( n_tokens , n_seqs , n_outputs , mctx . get ());
}
2025-03-13 12:35:44 +02:00
// llama adapter API
2026-02-14 03:06:27 -05:00
int32_t llama_set_adapters_lora (
2025-03-13 12:35:44 +02:00
llama_context * ctx ,
2026-02-14 03:06:27 -05:00
llama_adapter_lora ** adapters ,
size_t n_adapters ,
float * scales ) {
if ( adapters == nullptr || scales == nullptr ) {
GGML_ASSERT ( n_adapters == 0 && "invalid llama_set_adapters_lora call" );
}
ctx -> set_adapters_lora ( adapters , n_adapters , scales );
2025-03-13 12:35:44 +02:00
return 0 ;
}
2026-02-14 03:06:27 -05:00
int32_t llama_set_adapter_cvec (
2025-03-13 12:35:44 +02:00
llama_context * ctx ,
2026-02-14 03:06:27 -05:00
const float * data ,
size_t len ,
int32_t n_embd ,
int32_t il_start ,
int32_t il_end ) {
bool res = ctx -> set_adapter_cvec ( data , len , n_embd , il_start , il_end );
2025-03-13 12:35:44 +02:00
return res ? 0 : - 1 ;
}
2025-06-05 15:29:22 +03:00
//
// memory
//
2025-06-06 14:11:15 +03:00
void llama_memory_clear ( llama_memory_t mem , bool data ) {
if ( ! mem ) {
return ;
}
mem -> clear ( data );
2025-06-05 15:29:22 +03:00
}
bool llama_memory_seq_rm (
llama_memory_t mem ,
llama_seq_id seq_id ,
llama_pos p0 ,
llama_pos p1 ) {
2025-06-06 14:11:15 +03:00
if ( ! mem ) {
return true ;
}
2025-06-05 15:29:22 +03:00
return mem -> seq_rm ( seq_id , p0 , p1 );
}
void llama_memory_seq_cp (
llama_memory_t mem ,
llama_seq_id seq_id_src ,
llama_seq_id seq_id_dst ,
llama_pos p0 ,
llama_pos p1 ) {
2025-06-06 14:11:15 +03:00
if ( ! mem ) {
return ;
}
2025-06-05 15:29:22 +03:00
mem -> seq_cp ( seq_id_src , seq_id_dst , p0 , p1 );
}
void llama_memory_seq_keep (
llama_memory_t mem ,
llama_seq_id seq_id ) {
2025-06-06 14:11:15 +03:00
if ( ! mem ) {
return ;
}
2025-06-05 15:29:22 +03:00
mem -> seq_keep ( seq_id );
}
void llama_memory_seq_add (
llama_memory_t mem ,
llama_seq_id seq_id ,
llama_pos p0 ,
llama_pos p1 ,
llama_pos delta ) {
2025-06-06 14:11:15 +03:00
if ( ! mem ) {
return ;
}
2025-06-05 15:29:22 +03:00
mem -> seq_add ( seq_id , p0 , p1 , delta );
}
void llama_memory_seq_div (
llama_memory_t mem ,
llama_seq_id seq_id ,
llama_pos p0 ,
llama_pos p1 ,
int d ) {
2025-06-06 14:11:15 +03:00
if ( ! mem ) {
return ;
}
2025-06-05 15:29:22 +03:00
mem -> seq_div ( seq_id , p0 , p1 , d );
}
llama_pos llama_memory_seq_pos_min (
llama_memory_t mem ,
llama_seq_id seq_id ) {
2025-06-06 14:11:15 +03:00
if ( ! mem ) {
return - 1 ;
}
2025-06-05 15:29:22 +03:00
return mem -> seq_pos_min ( seq_id );
}
llama_pos llama_memory_seq_pos_max (
llama_memory_t mem ,
llama_seq_id seq_id ) {
2025-06-06 14:11:15 +03:00
if ( ! mem ) {
return - 1 ;
}
2025-06-05 15:29:22 +03:00
return mem -> seq_pos_max ( seq_id );
}
bool llama_memory_can_shift ( llama_memory_t mem ) {
2025-06-06 14:11:15 +03:00
if ( ! mem ) {
return false ;
}
2025-06-05 15:29:22 +03:00
return mem -> get_can_shift ();
}
2025-03-13 12:35:44 +02:00
// llama state API
// deprecated
size_t llama_get_state_size ( llama_context * ctx ) {
return llama_state_get_size ( ctx );
}
// deprecated
size_t llama_copy_state_data ( llama_context * ctx , uint8_t * dst ) {
return llama_state_get_data ( ctx , dst , - 1 );
}
// deprecated
size_t llama_set_state_data ( llama_context * ctx , const uint8_t * src ) {
return llama_state_set_data ( ctx , src , - 1 );
}
// deprecated
bool llama_load_session_file ( llama_context * ctx , const char * path_session , llama_token * tokens_out , size_t n_token_capacity , size_t * n_token_count_out ) {
return llama_state_load_file ( ctx , path_session , tokens_out , n_token_capacity , n_token_count_out );
}
// deprecated
bool llama_save_session_file ( llama_context * ctx , const char * path_session , const llama_token * tokens , size_t n_token_count ) {
return llama_state_save_file ( ctx , path_session , tokens , n_token_count );
}
// Returns the *actual* size of the state.
// Intended to be used when saving to state to a buffer.
size_t llama_state_get_size ( llama_context * ctx ) {
return ctx -> state_get_size ();
}
size_t llama_state_get_data ( llama_context * ctx , uint8_t * dst , size_t size ) {
ctx -> synchronize ();
return ctx -> state_get_data ( dst , size );
}
// Sets the state reading from the specified source address
size_t llama_state_set_data ( llama_context * ctx , const uint8_t * src , size_t size ) {
ctx -> synchronize ();
return ctx -> state_set_data ( src , size );
}
bool llama_state_load_file ( llama_context * ctx , const char * path_session , llama_token * tokens_out , size_t n_token_capacity , size_t * n_token_count_out ) {
ctx -> synchronize ();
try {
return ctx -> state_load_file ( path_session , tokens_out , n_token_capacity , n_token_count_out );
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error loading session file: %s \n " , __func__ , err . what ());
return false ;
}
}
bool llama_state_save_file ( llama_context * ctx , const char * path_session , const llama_token * tokens , size_t n_token_count ) {
ctx -> synchronize ();
try {
return ctx -> state_save_file ( path_session , tokens , n_token_count );
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error saving session file: %s \n " , __func__ , err . what ());
return false ;
}
}
size_t llama_state_seq_get_size ( llama_context * ctx , llama_seq_id seq_id ) {
2025-08-14 14:59:50 +03:00
return llama_state_seq_get_size_ext ( ctx , seq_id , 0 );
2025-03-13 12:35:44 +02:00
}
size_t llama_state_seq_get_data ( llama_context * ctx , uint8_t * dst , size_t size , llama_seq_id seq_id ) {
2025-08-14 14:59:50 +03:00
return llama_state_seq_get_data_ext ( ctx , dst , size , seq_id , 0 );
2025-03-13 12:35:44 +02:00
}
size_t llama_state_seq_set_data ( llama_context * ctx , const uint8_t * src , size_t size , llama_seq_id seq_id ) {
2025-08-14 14:59:50 +03:00
return llama_state_seq_set_data_ext ( ctx , src , size , seq_id , 0 );
}
size_t llama_state_seq_get_size_ext ( llama_context * ctx , llama_seq_id seq_id , llama_state_seq_flags flags ) {
return ctx -> state_seq_get_size ( seq_id , flags );
}
size_t llama_state_seq_get_data_ext ( llama_context * ctx , uint8_t * dst , size_t size , llama_seq_id seq_id , llama_state_seq_flags flags ) {
2025-03-13 12:35:44 +02:00
ctx -> synchronize ();
2025-08-14 14:59:50 +03:00
return ctx -> state_seq_get_data ( seq_id , dst , size , flags );
}
size_t llama_state_seq_set_data_ext ( llama_context * ctx , const uint8_t * src , size_t size , llama_seq_id seq_id , llama_state_seq_flags flags ) {
ctx -> synchronize ();
return ctx -> state_seq_set_data ( seq_id , src , size , flags );
2025-03-13 12:35:44 +02:00
}
size_t llama_state_seq_save_file ( llama_context * ctx , const char * filepath , llama_seq_id seq_id , const llama_token * tokens , size_t n_token_count ) {
ctx -> synchronize ();
try {
return ctx -> state_seq_save_file ( seq_id , filepath , tokens , n_token_count );
2025-01-03 10:18:53 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error saving sequence state file: %s \n " , __func__ , err . what ());
return 0 ;
}
}
2025-03-13 12:35:44 +02:00
size_t llama_state_seq_load_file ( llama_context * ctx , const char * filepath , llama_seq_id dest_seq_id , llama_token * tokens_out , size_t n_token_capacity , size_t * n_token_count_out ) {
ctx -> synchronize ();
2025-01-03 10:18:53 +02:00
try {
2025-03-13 12:35:44 +02:00
return ctx -> state_seq_load_file ( dest_seq_id , filepath , tokens_out , n_token_capacity , n_token_count_out );
2025-01-03 10:18:53 +02:00
} catch ( const std :: exception & err ) {
LLAMA_LOG_ERROR ( "%s: error loading sequence state file: %s \n " , __func__ , err . what ());
return 0 ;
}
}
2025-03-13 12:35:44 +02:00
///
int32_t llama_encode (
llama_context * ctx ,
llama_batch batch ) {
const int ret = ctx -> encode ( batch );
if ( ret != 0 ) {
LLAMA_LOG_ERROR ( "%s: failed to encode, ret = %d \n " , __func__ , ret );
}
return ret ;
}
int32_t llama_decode (
llama_context * ctx ,
llama_batch batch ) {
2025-05-31 12:55:57 +03:00
const int ret = ctx -> decode ( batch );
if ( ret != 0 && ret != 1 ) {
2025-03-13 12:35:44 +02:00
LLAMA_LOG_ERROR ( "%s: failed to decode, ret = %d \n " , __func__ , ret );
}
return ret ;
}
//
// perf
//
llama_perf_context_data llama_perf_context ( const llama_context * ctx ) {
llama_perf_context_data data = {};
if ( ctx == nullptr ) {
return data ;
}
data = ctx -> perf_get_data ();
return data ;
}
void llama_perf_context_print ( const llama_context * ctx ) {
const auto data = llama_perf_context ( ctx );
const double t_end_ms = 1e-3 * ggml_time_us ();
LLAMA_LOG_INFO ( "%s: load time = %10.2f ms \n " , __func__ , data . t_load_ms );
LLAMA_LOG_INFO ( "%s: prompt eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second) \n " ,
__func__ , data . t_p_eval_ms , data . n_p_eval , data . t_p_eval_ms / data . n_p_eval , 1e3 / data . t_p_eval_ms * data . n_p_eval );
LLAMA_LOG_INFO ( "%s: eval time = %10.2f ms / %5d runs (%8.2f ms per token, %8.2f tokens per second) \n " ,
__func__ , data . t_eval_ms , data . n_eval , data . t_eval_ms / data . n_eval , 1e3 / data . t_eval_ms * data . n_eval );
LLAMA_LOG_INFO ( "%s: total time = %10.2f ms / %5d tokens \n " , __func__ , ( t_end_ms - data . t_start_ms ), ( data . n_p_eval + data . n_eval ));
2025-07-17 19:08:33 +03:00
LLAMA_LOG_INFO ( "%s: graphs reused = %10d \n " , __func__ , data . n_reused );
2025-03-13 12:35:44 +02:00
}
void llama_perf_context_reset ( llama_context * ctx ) {
ctx -> perf_reset ();
2025-01-03 10:18:53 +02:00
}
2025-05-12 14:44:49 +02:00
//
// training
//
bool llama_opt_param_filter_all ( const struct ggml_tensor * tensor , void * userdata ) {
GGML_UNUSED ( tensor );
GGML_UNUSED ( userdata );
return true ;
}
void llama_opt_init ( struct llama_context * ctx , struct llama_model * model , struct llama_opt_params lopt_params ) {
ctx -> opt_init ( model , lopt_params );
}
void llama_opt_epoch (
struct llama_context * ctx ,
ggml_opt_dataset_t dataset ,
ggml_opt_result_t result_train ,
ggml_opt_result_t result_eval ,
int64_t idata_split ,
ggml_opt_epoch_callback callback_train ,
ggml_opt_epoch_callback callback_eval ) {
ctx -> opt_epoch (
dataset ,
result_train ,
result_eval ,
idata_split ,
callback_train ,
callback_eval );
}
2026-04-21 09:54:36 +03:00
//
// ext
//
llama_memory_breakdown llama_get_memory_breakdown ( const struct llama_context * ctx ) {
return ctx -> memory_breakdown ();
}
2026-06-07 20:50:54 +08:00
llama_context * llama_get_ctx_other ( struct llama_context * ctx ) {
return ctx -> get_cparams (). ctx_other ;
}