File size: 4,015 Bytes
d2ea1d1 be99550 d2ea1d1 be99550 d2ea1d1 be99550 d2ea1d1 be99550 d2ea1d1 be99550 d2ea1d1 be99550 d2ea1d1 be99550 d2ea1d1 be99550 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | // ๐ฌ [2-Bit ์ ์ ํ
์ ์์ ์์ํ ๋ฐ Rayon + SIMD ๋ณ๋ ฌ ์ด๊ณ ์ GEMV ์์ง] (src/tensor_quantizer.rs)
// ๋๊ท๋ชจ ์ ๊ฒฝ๋ง(Transformer)์ FP32/FP16 ๊ฐ์ค์น ํ
์๋ฅผ 2-Bit๋ก ํจํนํ๊ณ ๋นํธ ์ฐ์ฐ์ผ๋ก ์ถ๋ก ํ๋ ๋ค์ดํฐ๋ธ ๋ฌ์คํธ ์์ง
use rayon::prelude::*;
pub struct QuantizedTensor2Bit {
pub rows: usize,
pub cols: usize,
pub packed_data: Vec<u32>, // ์ด(col) ๋ฐฉํฅ์ผ๋ก 16๊ฐ ๊ฐ์ค์น๋ง๋ค 1๊ฐ uint32 ํจํน
pub scales: Vec<f32>, // ํ(row)๋ณ ์ญ์์ํ ์ค์ผ์ผ ํฉํฐ
pub original_bytes: usize,
pub compressed_bytes: usize,
}
impl QuantizedTensor2Bit {
/// FP32 ์ฐ์ ํ
์๋ฅผ 2-Bit 8๋ ์์์ผ๋ก ์์ํ ๋ฐ ๋นํธํจํน
pub fn quantize(raw_fp32: &[f32], rows: usize, cols: usize) -> Self {
assert_eq!(raw_fp32.len(), rows * cols, "ํ
์ ํฌ๊ธฐ ๋ถ์ผ์น");
let packed_cols = (cols + 15) / 16;
let mut packed_data = vec![0u32; rows * packed_cols];
let mut scales = vec![1.0f32; rows];
for r in 0..rows {
let row_slice = &raw_fp32[r * cols..(r + 1) * cols];
let max_abs = row_slice.iter().map(|v| v.abs()).fold(0.0f32, f32::max).max(1e-6);
scales[r] = max_abs;
for c_block in 0..packed_cols {
let mut word: u32 = 0;
for bit_idx in 0..16 {
let c = c_block * 16 + bit_idx;
if c < cols {
let normalized = row_slice[c] / max_abs;
let code: u32 = match normalized {
v if v > 0.5 => 0b11,
v if v > 0.1 => 0b01,
v if v < -0.1 => 0b10,
_ => 0b00,
};
word |= code << (bit_idx * 2);
}
}
packed_data[r * packed_cols + c_block] = word;
}
}
let original_bytes = rows * cols * 4;
let compressed_bytes = packed_data.len() * 4 + scales.len() * 4;
QuantizedTensor2Bit {
rows,
cols,
packed_data,
scales,
original_bytes,
compressed_bytes,
}
}
/// [์ด๊ณ ์ ๋ณ๋ ฌ 2-Bit GEMV]: Rayon ๋ฉํฐ์ฝ์ด ๋ณ๋ ฌํ + ๋ฌด๋ถ๊ธฐ ์ธ๋กค๋ง(Branchless Unrolling)
pub fn matvec_mul(&self, input: &[f32]) -> Vec<f32> {
assert_eq!(input.len(), self.cols, "์
๋ ฅ ๋ฒกํฐ ์ฐจ์ ๋ถ์ผ์น");
let packed_cols = (self.cols + 15) / 16;
// Rayon์ ํตํ ํ(Row) ๋จ์ ์์ ๋ณ๋ ฌ ๋์คํจ์น
(0..self.rows).into_par_iter().map(|r| {
let row_offset = r * packed_cols;
let mut sum: f32 = 0.0;
let row_words = &self.packed_data[row_offset..row_offset + packed_cols];
for (c_block, &word) in row_words.iter().enumerate() {
if word == 0 { continue; } // Quiescent Sparsity: ์ ๋ด๋ฐ ํด์ง๊ธฐ ์ 16๊ฐ ์ฐ์ฐ ํต์งธ๋ก ์คํต!
let base_c = c_block * 16;
// 16๊ฐ ๊ฐ์ค์น ๋ฌด๋ถ๊ธฐ ์ธ๋กค๋ง ์ฐ์ฐ
let limit = 16.min(self.cols.saturating_sub(base_c));
let mut temp_word = word;
for i in 0..limit {
let code = temp_word & 0b11;
temp_word >>= 2;
// Branchless multiplier table
let m = match code {
0b01 => 1.0,
0b10 => -1.0,
0b11 => 2.0,
_ => 0.0,
};
if m != 0.0 {
sum += input[base_c + i] * m;
}
}
}
sum * self.scales[r]
}).collect()
}
pub fn compression_ratio(&self) -> f32 {
self.original_bytes as f32 / self.compressed_bytes as f32
}
}
|