178 lines
5.5 KiB
C++
178 lines
5.5 KiB
C++
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <memory>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "calrt_calbin.h"
|
|
#include "calrt_utils.h"
|
|
#include "calrt_vdevice.h"
|
|
|
|
namespace calrt {
|
|
|
|
typedef int32_t KvPos;
|
|
typedef int32_t KvSeqId;
|
|
|
|
struct KvCellAddr_s {
|
|
int64_t src_offset; // offset from base_addr
|
|
int64_t dst_offset; // offset from base_addr
|
|
};
|
|
|
|
enum class KvBatchMode_e : int32_t {
|
|
ONLY_VALID = 0,
|
|
ALL = 1, // 0:only mv valid batch ; 1:mv 16 batch ;recommond threhold 5 batch
|
|
AUTO
|
|
};
|
|
|
|
enum class KvUpdateAlgo_e : int32_t { V_CACHE_MOVE = 0, K_CACHE_ROPE = 1 };
|
|
|
|
enum class KvDataType_e : int32_t { BF16 = 0, S8 = 1, INVALID };
|
|
|
|
#define D0 16
|
|
// kv_cache memory layout:
|
|
// batch=1: K&V cache [layer,head,1, max_seq_len/d0,head_dim/d2,d0,d2]
|
|
// batch>1: k_cache [layer,head,batch, max_seq_len/d0,head_dim/d2,d0,d2]
|
|
// v_cavhe [layer,head,max_seq_len,batch/d0, head_dim/d2,d0,d2]
|
|
// k_cache and v_cache have completely continous memory layout
|
|
struct kv_update_config_p16_s {
|
|
int64_t base_addr; // k or v_cache base addr,to compute offset
|
|
int64_t k_head_stride; // k_cache: size of each head, reduce mul [max_batch,max_seq/d0,head_dim/d2,d0,d2]*byte_size
|
|
int64_t v_max_seq_len_stride; // v_cache: size of each max_seq_len,
|
|
// reduce mul [max_batch/d0,head_dim/d2,d0,d2]*byte_size
|
|
int64_t v_head_stride; // v_cache: size of each head,
|
|
// reduce mul [max_seq_len,max_batch/d0,head_dim/d2,d0,d2]*byte_size
|
|
KvUpdateAlgo_e kv_update_algo;
|
|
KvBatchMode_e data_mv_mode;
|
|
|
|
int32_t model_batch_num; // to specify one batch or mul batch memory of v_cache,1-onebatch;16-16batch
|
|
int32_t n_layer; // all layer
|
|
int32_t n_head; // all head
|
|
int32_t n_batch; // only valid batch
|
|
int32_t n_tokens_len; // valid n_seq_len
|
|
|
|
int32_t head_dim_div_d2; // assert head_dim_div_d2 % 2 == 0
|
|
|
|
KvDataType_e sin_cos_table_data_type; // support bf16 first; 0-bf16
|
|
int16_t *sin_table;
|
|
int16_t *cos_table;
|
|
|
|
KvDataType_e kv_data_type; // support bf16 first for k_cache, bf16 s8 for v_cache, 0-bf16,1-s8
|
|
KvCellAddr_s *addrs; // [n_batch]
|
|
|
|
kv_update_config_p16_s() = default;
|
|
~kv_update_config_p16_s() { free(addrs); }
|
|
|
|
kv_update_config_p16_s(const kv_update_config_p16_s &) = delete;
|
|
kv_update_config_p16_s &operator=(const kv_update_config_p16_s &) = delete;
|
|
kv_update_config_p16_s(kv_update_config_p16_s &&) = default;
|
|
kv_update_config_p16_s &operator=(kv_update_config_p16_s &&) = default;
|
|
};
|
|
|
|
struct KvHwBatch {
|
|
KvHwBatch(int32_t batch);
|
|
KvSeqId seq_id;
|
|
int32_t batch_id;
|
|
size_t seq_len;
|
|
};
|
|
|
|
struct KvSeqInfo {
|
|
KvSeqId seq_id;
|
|
size_t seq_len;
|
|
};
|
|
|
|
struct CalrtSeqShift {
|
|
KvSeqId seq_id;
|
|
KvPos dst_pos;
|
|
KvPos src_pos;
|
|
KvPos len;
|
|
};
|
|
|
|
class CALRT_API KvManager {
|
|
public:
|
|
KvManager(calrt::VirtualDevice* vdev, calrt::Calbin* calbin);
|
|
|
|
bool canShift();
|
|
|
|
// check if we have avaliable hardware resource
|
|
bool canAllocate(size_t seq_num);
|
|
void Allocate(KvSeqInfo &seq_info);
|
|
|
|
void Free(KvSeqId seq_id);
|
|
|
|
void RemoveTokensAtEnd(KvSeqId seq_id, size_t n_tokens);
|
|
|
|
bool isExist(KvSeqId seq_id);
|
|
// keep track of kv state
|
|
bool Apply(KvSeqInfo &seq_info);
|
|
|
|
// if batch_mode == ALL, only seqs[0] will be used
|
|
void DoShift(KvBatchMode_e batch_mode, std::vector<CalrtSeqShift> &seqs);
|
|
|
|
KvPos GetSeqLen(KvSeqId seq_id) const;
|
|
|
|
void Clear();
|
|
|
|
KvPos seqPosMin(KvSeqId seq_id) const;
|
|
KvPos seqPosMax(KvSeqId seq_id) const;
|
|
|
|
private:
|
|
VirtualDevice* vdev;
|
|
Calbin* calbin;
|
|
CalbinLLM_s llm_spec;
|
|
|
|
std::deque<std::unique_ptr<KvHwBatch>> free_hw_batch;
|
|
|
|
std::unordered_map<KvSeqId, std::unique_ptr<KvHwBatch>> seq_to_hw_batch;
|
|
|
|
std::vector<float> inv_freq;
|
|
std::unordered_map<int, std::vector<int16_t>> sin_cache; // bf16
|
|
std::unordered_map<int, std::vector<int16_t>> cos_cache;
|
|
|
|
template <float (*tri)(float)> int16_t *get_table(std::unordered_map<int, std::vector<int16_t>> &cache, int offset);
|
|
|
|
kv_update_config_p16_s build_kv_config(KvUpdateAlgo_e k_or_v, KvBatchMode_e batch_mode, int32_t max_batch,
|
|
int32_t valid_batch, std::vector<CalrtSeqShift> &seqs);
|
|
};
|
|
|
|
// std::unique_ptr<KvManager> kv_manager_init_from_spec(const CalbinLLM_s &calbin_llm);
|
|
|
|
static int16_t float_to_bfloat16(float f) {
|
|
uint32_t float_bits;
|
|
std::memcpy(&float_bits, &f, sizeof(float)); // Copy float bits to uint32_t
|
|
|
|
// Bfloat16 is essentially the upper 16 bits of the float
|
|
// with the lower 16 bits of the mantissa truncated.
|
|
int16_t bfloat16_bits = static_cast<int16_t>(float_bits >> 16);
|
|
|
|
return bfloat16_bits;
|
|
}
|
|
|
|
template <float (*tri)(float)>
|
|
int16_t *KvManager::get_table(std::unordered_map<int, std::vector<int16_t>> &cache, int offset) {
|
|
auto it = cache.find(offset);
|
|
|
|
if (it != cache.end()) {
|
|
return it->second.data();
|
|
}
|
|
|
|
std::vector<int16_t> table(inv_freq.size() * 2);
|
|
|
|
for (size_t i = 0; i < inv_freq.size(); ++i) {
|
|
float theta = static_cast<float>(-offset) * inv_freq[i]; // use negative pos because we use this for kv shift
|
|
float val = tri(theta);
|
|
|
|
int16_t bf16 = float_to_bfloat16(val);
|
|
|
|
table[i] = bf16;
|
|
table[inv_freq.size() + i] = bf16;
|
|
}
|
|
|
|
auto [ins_it, _] = cache.emplace(offset, std::move(table));
|
|
return ins_it->second.data();
|
|
}
|
|
|
|
} // namespace calrt
|