ember.cpp 0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
ember_kvq Namespace Reference

Enumerations

enum  Format : int { kF32 = 0 , kF16 = 1 , kQ8 = 2 , kQ4 = 3 }
 

Functions

void write_cuda (int f, void *Kd, void *Ks, void *Vd, void *Vs, const float *k, const float *v, int T, int row, int start_pos, cudaStream_t st, const int *dpos)
 
size_t partial_floats (int hq, int hd, int max_len)
 
void decode_cuda (int f, const float *q, const void *Kd, const void *Ks, const void *Vd, const void *Vs, float *out, int hq, int hkv, int hd, float scale, int max_len, int seq_len, float *part, cudaStream_t st, const int *dpos)
 
void dequant_rows_cuda (int f, const void *Kd, const void *Ks, const void *Vd, const void *Vs, float *K, float *V, int n, int row, cudaStream_t st)
 
Format parse_format (const char *s)
 
const char * format_name (int f)
 
size_t data_bytes (int f, size_t row)
 
size_t scale_bytes (int f, size_t row)
 
std::string shape_error (int f, int head_dim)
 
uint16_t f32_to_f16 (float x)
 
float f16_to_f32 (uint16_t h)
 
void roundtrip_block (int f, float *x)
 
void roundtrip_rows (int f, float *x, size_t rows, size_t row)
 

Variables

constexpr int kBlock = 32
 

Enumeration Type Documentation

◆ Format

Enumerator
kF32 
kF16 
kQ8 
kQ4 

Definition at line 33 of file kv_quant.h.

33: int { kF32 = 0, kF16 = 1, kQ8 = 2, kQ4 = 3 };

Function Documentation

◆ data_bytes()

size_t ember_kvq::data_bytes ( int  f,
size_t  row 
)
inline

Definition at line 48 of file kv_quant.h.

48 {
49 return f == kF16 ? row * 2 : f == kQ8 ? row : f == kQ4 ? row / 2 : row * 4;
50}
size_t j

References j, kF16, kQ4, and kQ8.

◆ decode_cuda()

void ember_kvq::decode_cuda ( int  f,
const float *  q,
const void *  Kd,
const void *  Ks,
const void *  Vd,
const void *  Vs,
float *  out,
int  hq,
int  hkv,
int  hd,
float  scale,
int  max_len,
int  seq_len,
float *  part,
cudaStream_t  st,
const int *  dpos 
)

Definition at line 288 of file kv_quant.cu.

290 {
291 if (!ember_tiled::tiled_shape_ok(hq, hd, max_len, 1 << 20, kMin, kMax) || hkv <= 0 || hq % hkv != 0)
292 throw std::runtime_error("kv_quant decode: shape not supported (heads " + std::to_string(hq) + "/" +
293 std::to_string(hkv) + ", head_dim " + std::to_string(hd) + ", max_len " +
294 std::to_string(max_len) + ")");
295 switch (f) {
296 case kF16: decode_launch<kF16>(q, Kd, Ks, Vd, Vs, out, hq, hkv, hd, scale, max_len, seq_len, part, st, dpos); break;
297 case kQ8: decode_launch<kQ8>(q, Kd, Ks, Vd, Vs, out, hq, hkv, hd, scale, max_len, seq_len, part, st, dpos); break;
298 case kQ4: decode_launch<kQ4>(q, Kd, Ks, Vd, Vs, out, hq, hkv, hd, scale, max_len, seq_len, part, st, dpos); break;
299 default: throw std::runtime_error("kv_quant decode: format " + std::to_string(f));
300 }
301}
node out
node scale
__host__ bool tiled_shape_ok(int heads, int head_dim, int max_len, int heads_max, int min_chunk, int max_chunks)

References j, kF16, kQ4, kQ8, out, scale, and ember_tiled::tiled_shape_ok().

◆ dequant_rows_cuda()

void ember_kvq::dequant_rows_cuda ( int  f,
const void *  Kd,
const void *  Ks,
const void *  Vd,
const void *  Vs,
float *  K,
float *  V,
int  n,
int  row,
cudaStream_t  st 
)

Definition at line 303 of file kv_quant.cu.

304 {
305 if (n <= 0) return;
306 switch (f) {
307 case kF16: dequant_launch<kF16>(Kd, Ks, Vd, Vs, K, V, n, row, st); break;
308 case kQ8: dequant_launch<kQ8>(Kd, Ks, Vd, Vs, K, V, n, row, st); break;
309 case kQ4: dequant_launch<kQ4>(Kd, Ks, Vd, Vs, K, V, n, row, st); break;
310 default: throw std::runtime_error("kv_quant dequant: format " + std::to_string(f));
311 }
312}
int n

References j, kF16, kQ4, kQ8, and n.

◆ f16_to_f32()

float ember_kvq::f16_to_f32 ( uint16_t  h)
inline

Definition at line 88 of file kv_quant.h.

88 {
89 const uint32_t sign = static_cast<uint32_t>(h & 0x8000u) << 16;
90 const uint32_t e = (h >> 10) & 0x1fu, m = h & 0x3ffu;
91 uint32_t u;
92 if (e == 0) {
93 if (m == 0) {
94 u = sign;
95 } else { // subnormal: m * 2^-24, exact in float
96 const float v = static_cast<float>(m) * 5.9604644775390625e-8f;
97 std::memcpy(&u, &v, 4);
98 u |= sign;
99 }
100 } else if (e == 31) {
101 u = sign | 0x7f800000u | (m << 13);
102 } else {
103 u = sign | ((e + 112u) << 23) | (m << 13);
104 }
105 float f;
106 std::memcpy(&f, &u, 4);
107 return f;
108}
std::vector< float > v

References j, and v.

Referenced by roundtrip_block(), and roundtrip_rows().

◆ f32_to_f16()

uint16_t ember_kvq::f32_to_f16 ( float  x)
inline

Definition at line 64 of file kv_quant.h.

64 {
65 uint32_t u;
66 std::memcpy(&u, &x, 4);
67 const uint32_t sign = (u >> 16) & 0x8000u;
68 const uint32_t ax = u & 0x7fffffffu;
69 if (ax >= 0x7f800000u) return static_cast<uint16_t>(sign | (ax > 0x7f800000u ? 0x7e00u : 0x7c00u));
70 if (ax >= 0x477ff000u) return static_cast<uint16_t>(sign | 0x7c00u); // rounds past 65504: infinity
71 if (ax < 0x38800000u) { // below 2^-14: a half subnormal (or zero)
72 if (ax < 0x33000000u) return static_cast<uint16_t>(sign); // below half the smallest subnormal: 0
73 const uint32_t e = ax >> 23;
74 const uint32_t m = (ax & 0x7fffffu) | 0x800000u;
75 const uint32_t shift = 126 - e; // r = m * 2^(e - 126) units of 2^-24, m with its implicit bit
76 const uint32_t base = m >> shift;
77 const uint32_t rem = m & ((1u << shift) - 1u);
78 const uint32_t half_ulp = 1u << (shift - 1);
79 uint32_t r = base;
80 if (rem > half_ulp || (rem == half_ulp && (base & 1u))) ++r;
81 return static_cast<uint16_t>(sign | r);
82 }
83 uint32_t r = ((ax >> 13) - (112u << 10)); // rebias the exponent, keep 10 mantissa bits
84 const uint32_t rem = ax & 0x1fffu;
85 if (rem > 0x1000u || (rem == 0x1000u && (r & 1u))) ++r; // a carry into the exponent is correct
86 return static_cast<uint16_t>(sign | r);
87}

References j.

Referenced by roundtrip_block(), and roundtrip_rows().

◆ format_name()

const char * ember_kvq::format_name ( int  f)
inline

Definition at line 45 of file kv_quant.h.

45{ return f == kF16 ? "f16" : f == kQ8 ? "q8" : f == kQ4 ? "q4" : "f32"; }

References j, kF16, kQ4, and kQ8.

Referenced by ember_ir::Interpreter::init(), and ember_ir::Interpreter::kv_format().

◆ parse_format()

Format ember_kvq::parse_format ( const char *  s)
inline

Definition at line 37 of file kv_quant.h.

37 {
38 const std::string v = s ? s : "";
39 if (v.empty() || v == "f32") return kF32;
40 if (v == "f16") return kF16;
41 if (v == "q8") return kQ8;
42 if (v == "q4") return kQ4;
43 throw std::runtime_error("EMBER_KV=" + v + ": expected f32, f16, q8 or q4");
44}

References j, kF16, kF32, kQ4, kQ8, and v.

Referenced by ember_ir::Interpreter::init().

◆ partial_floats()

size_t ember_kvq::partial_floats ( int  hq,
int  hd,
int  max_len 
)

Definition at line 284 of file kv_quant.cu.

284 {
285 return static_cast<size_t>(hq) * ember_tiled::tiled_grid_chunks(max_len, kMin, kMax) * (hd + 2);
286}
__host__ __device__ int tiled_grid_chunks(int max_len, int min_chunk, int max_chunks)

References j, and ember_tiled::tiled_grid_chunks().

◆ roundtrip_block()

void ember_kvq::roundtrip_block ( int  f,
float *  x 
)
inline

Definition at line 111 of file kv_quant.h.

111 {
112 if (f == kQ8) {
113 float amax = 0.0f;
114 for (int i = 0; i < kBlock; ++i) amax = std::fmax(amax, std::fabs(x[i]));
115 const float d = f16_to_f32(f32_to_f16(amax / 127.0f));
116 for (int i = 0; i < kBlock; ++i) {
117 float q = d != 0.0f ? std::nearbyint(x[i] / d) : 0.0f;
118 q = std::fmin(127.0f, std::fmax(-127.0f, q));
119 x[i] = q * d;
120 }
121 } else if (f == kQ4) {
122 float mn = x[0], mx = x[0];
123 for (int i = 1; i < kBlock; ++i) {
124 mn = std::fmin(mn, x[i]);
125 mx = std::fmax(mx, x[i]);
126 }
127 const float d = f16_to_f32(f32_to_f16((mx - mn) / 15.0f));
128 const float m = f16_to_f32(f32_to_f16(mn));
129 for (int i = 0; i < kBlock; ++i) {
130 float q = d != 0.0f ? std::nearbyint((x[i] - m) / d) : 0.0f;
131 q = std::fmin(15.0f, std::fmax(0.0f, q));
132 x[i] = std::fma(q, d, m);
133 }
134 }
135}
uint16_t f32_to_f16(float x)
Definition kv_quant.h:64
constexpr int kBlock
Definition kv_quant.h:34
float f16_to_f32(uint16_t h)
Definition kv_quant.h:88

References f16_to_f32(), f32_to_f16(), j, kBlock, kQ4, and kQ8.

Referenced by roundtrip_rows().

◆ roundtrip_rows()

void ember_kvq::roundtrip_rows ( int  f,
float *  x,
size_t  rows,
size_t  row 
)
inline

Definition at line 138 of file kv_quant.h.

138 {
139 if (f == kF32) return;
140 const size_t n = rows * row;
141 if (f == kF16) {
142 for (size_t i = 0; i < n; ++i) x[i] = f16_to_f32(f32_to_f16(x[i]));
143 return;
144 }
145 for (size_t b = 0; b + kBlock <= n; b += kBlock) roundtrip_block(f, x + b);
146}

References f16_to_f32(), f32_to_f16(), j, kBlock, kF16, kF32, n, and roundtrip_block().

Referenced by ember_ir::Interpreter::run_nodes().

◆ scale_bytes()

size_t ember_kvq::scale_bytes ( int  f,
size_t  row 
)
inline

Definition at line 51 of file kv_quant.h.

51 {
52 return f == kQ8 ? row / kBlock * 2 : f == kQ4 ? row / kBlock * 4 : 0;
53}

References j, kBlock, kQ4, and kQ8.

◆ shape_error()

std::string ember_kvq::shape_error ( int  f,
int  head_dim 
)
inline

Definition at line 55 of file kv_quant.h.

55 {
56 if (f == kF32) return "";
57 if (head_dim % 4 != 0) return "head_dim % 4 != 0";
58 if ((f == kQ8 || f == kQ4) && head_dim % kBlock != 0) return "head_dim % 32 != 0 (q8 and q4 blocks sit inside a head)";
59 return "";
60}

References j, kBlock, kF32, kQ4, and kQ8.

Referenced by ember_ir::Interpreter::init().

◆ write_cuda()

void ember_kvq::write_cuda ( int  f,
void *  Kd,
void *  Ks,
void *  Vd,
void *  Vs,
const float *  k,
const float *  v,
int  T,
int  row,
int  start_pos,
cudaStream_t  st,
const int *  dpos 
)

Definition at line 260 of file kv_quant.cu.

261 {
262 if (T <= 0) return;
263 if (f == kF16) {
264 const size_t total = 2 * static_cast<size_t>(T) * row;
265 write_f16_kernel<<<static_cast<unsigned>((total + 255) / 256), 256, 0, st>>>(
266 static_cast<__half*>(Kd), static_cast<__half*>(Vd), k, v, T, row, start_pos, dpos);
267 } else {
268 const size_t threads = 2 * static_cast<size_t>(T) * (row / kBlock) * 32;
269 const unsigned grid = static_cast<unsigned>((threads + 127) / 128);
270 auto* kd = static_cast<uint8_t*>(Kd);
271 auto* ks = static_cast<uint8_t*>(Ks);
272 auto* vd = static_cast<uint8_t*>(Vd);
273 auto* vs = static_cast<uint8_t*>(Vs);
274 if (f == kQ8)
275 write_block_kernel<kQ8><<<grid, 128, 0, st>>>(kd, ks, vd, vs, k, v, T, row, start_pos, dpos);
276 else if (f == kQ4)
277 write_block_kernel<kQ4><<<grid, 128, 0, st>>>(kd, ks, vd, vs, k, v, T, row, start_pos, dpos);
278 else
279 throw std::runtime_error("kv_quant write: format " + std::to_string(f));
280 }
281 check(cudaGetLastError(), "write");
282}

References j, k, kBlock, kF16, kQ4, kQ8, and v.

Variable Documentation

◆ kBlock

constexpr int ember_kvq::kBlock = 32
constexpr

Definition at line 34 of file kv_quant.h.

Referenced by roundtrip_block(), roundtrip_rows(), scale_bytes(), shape_error(), and write_cuda().