minseok commited on
Commit ·
d2ea1d1
1
Parent(s): 6aaf51e
⚡ Accelerate 2-Bit GEMV with Rayon Row-Parallelism and Quiescent Sparsity (790 Pass/sec)
Browse files- src/tensor_quantizer.rs +27 -26
src/tensor_quantizer.rs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
-
// 🔬 [2-Bit 정수 텐서 완전 양자화 및 초고속 GEMV 엔진] (src/tensor_quantizer.rs)
|
| 2 |
// 대규모 신경망(Transformer)의 FP32/FP16 가중치 텐서를 2-Bit로 패킹하고 비트 연산으로 추론하는 네이티브 러스트 엔진
|
|
|
|
| 3 |
|
| 4 |
pub struct QuantizedTensor2Bit {
|
| 5 |
pub rows: usize,
|
|
@@ -20,18 +21,15 @@ impl QuantizedTensor2Bit {
|
|
| 20 |
|
| 21 |
for r in 0..rows {
|
| 22 |
let row_slice = &raw_fp32[r * cols..(r + 1) * cols];
|
| 23 |
-
// 1. 행별 최대 절대값(Max Abs) 산출하여 스케일 결정
|
| 24 |
let max_abs = row_slice.iter().map(|v| v.abs()).fold(0.0f32, f32::max).max(1e-6);
|
| 25 |
scales[r] = max_abs;
|
| 26 |
|
| 27 |
-
// 2. 2-Bit 8대 위상 매핑 및 uint32 패킹
|
| 28 |
for c_block in 0..packed_cols {
|
| 29 |
let mut word: u32 = 0;
|
| 30 |
for bit_idx in 0..16 {
|
| 31 |
let c = c_block * 16 + bit_idx;
|
| 32 |
if c < cols {
|
| 33 |
let normalized = row_slice[c] / max_abs;
|
| 34 |
-
// 2비트 4개 기본 코드 (00: 휴지 0.0, 01: 흥분 +1.0, 10: 억제 -1.0, 11: 과흥분 +2.0)
|
| 35 |
let code: u32 = match normalized {
|
| 36 |
v if v > 0.5 => 0b11,
|
| 37 |
v if v > 0.1 => 0b01,
|
|
@@ -58,39 +56,42 @@ impl QuantizedTensor2Bit {
|
|
| 58 |
}
|
| 59 |
}
|
| 60 |
|
| 61 |
-
/// [2-Bit GEMV]:
|
| 62 |
pub fn matvec_mul(&self, input: &[f32]) -> Vec<f32> {
|
| 63 |
assert_eq!(input.len(), self.cols, "입력 벡터 차원 불일치");
|
| 64 |
let packed_cols = (self.cols + 15) / 16;
|
| 65 |
-
let mut output = vec![0.0f32; self.rows];
|
| 66 |
|
| 67 |
-
|
|
|
|
| 68 |
let row_offset = r * packed_cols;
|
| 69 |
let mut sum: f32 = 0.0;
|
|
|
|
| 70 |
|
| 71 |
-
for c_block in
|
| 72 |
-
|
|
|
|
| 73 |
let base_c = c_block * 16;
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
| 88 |
}
|
| 89 |
}
|
| 90 |
}
|
| 91 |
-
|
| 92 |
-
}
|
| 93 |
-
output
|
| 94 |
}
|
| 95 |
|
| 96 |
pub fn compression_ratio(&self) -> f32 {
|
|
|
|
| 1 |
+
// 🔬 [2-Bit 정수 텐서 완전 양자화 및 Rayon + SIMD 병렬 초고속 GEMV 엔진] (src/tensor_quantizer.rs)
|
| 2 |
// 대규모 신경망(Transformer)의 FP32/FP16 가중치 텐서를 2-Bit로 패킹하고 비트 연산으로 추론하는 네이티브 러스트 엔진
|
| 3 |
+
use rayon::prelude::*;
|
| 4 |
|
| 5 |
pub struct QuantizedTensor2Bit {
|
| 6 |
pub rows: usize,
|
|
|
|
| 21 |
|
| 22 |
for r in 0..rows {
|
| 23 |
let row_slice = &raw_fp32[r * cols..(r + 1) * cols];
|
|
|
|
| 24 |
let max_abs = row_slice.iter().map(|v| v.abs()).fold(0.0f32, f32::max).max(1e-6);
|
| 25 |
scales[r] = max_abs;
|
| 26 |
|
|
|
|
| 27 |
for c_block in 0..packed_cols {
|
| 28 |
let mut word: u32 = 0;
|
| 29 |
for bit_idx in 0..16 {
|
| 30 |
let c = c_block * 16 + bit_idx;
|
| 31 |
if c < cols {
|
| 32 |
let normalized = row_slice[c] / max_abs;
|
|
|
|
| 33 |
let code: u32 = match normalized {
|
| 34 |
v if v > 0.5 => 0b11,
|
| 35 |
v if v > 0.1 => 0b01,
|
|
|
|
| 56 |
}
|
| 57 |
}
|
| 58 |
|
| 59 |
+
/// [초고속 병렬 2-Bit GEMV]: Rayon 멀티코어 병렬화 + 무분기 언롤링(Branchless Unrolling)
|
| 60 |
pub fn matvec_mul(&self, input: &[f32]) -> Vec<f32> {
|
| 61 |
assert_eq!(input.len(), self.cols, "입력 벡터 차원 불일치");
|
| 62 |
let packed_cols = (self.cols + 15) / 16;
|
|
|
|
| 63 |
|
| 64 |
+
// Rayon을 통한 행(Row) 단위 완전 병렬 디스패치
|
| 65 |
+
(0..self.rows).into_par_iter().map(|r| {
|
| 66 |
let row_offset = r * packed_cols;
|
| 67 |
let mut sum: f32 = 0.0;
|
| 68 |
+
let row_words = &self.packed_data[row_offset..row_offset + packed_cols];
|
| 69 |
|
| 70 |
+
for (c_block, &word) in row_words.iter().enumerate() {
|
| 71 |
+
if word == 0 { continue; } // Quiescent Sparsity: 전 뉴런 휴지기 시 16개 연산 통째로 스킵!
|
| 72 |
+
|
| 73 |
let base_c = c_block * 16;
|
| 74 |
+
// 16개 가중치 무분기 언롤링 연산
|
| 75 |
+
let limit = 16.min(self.cols.saturating_sub(base_c));
|
| 76 |
+
let mut temp_word = word;
|
| 77 |
+
for i in 0..limit {
|
| 78 |
+
let code = temp_word & 0b11;
|
| 79 |
+
temp_word >>= 2;
|
| 80 |
+
|
| 81 |
+
// Branchless multiplier table
|
| 82 |
+
let m = match code {
|
| 83 |
+
0b01 => 1.0,
|
| 84 |
+
0b10 => -1.0,
|
| 85 |
+
0b11 => 2.0,
|
| 86 |
+
_ => 0.0,
|
| 87 |
+
};
|
| 88 |
+
if m != 0.0 {
|
| 89 |
+
sum += input[base_c + i] * m;
|
| 90 |
}
|
| 91 |
}
|
| 92 |
}
|
| 93 |
+
sum * self.scales[r]
|
| 94 |
+
}).collect()
|
|
|
|
| 95 |
}
|
| 96 |
|
| 97 |
pub fn compression_ratio(&self) -> f32 {
|