Download faie_runner.py from abuz8studios/FAIE: direct link, hf CLI and curl.
- Browser
- Download file 16.8 kB
-
https://huggingface.co/abuz8studios/FAIE/resolve/main/faie_runner.py
- Command line
-
hf download hf://abuz8studios/FAIE/faie_runner.py
-
curl -L -o faie_runner.py https://huggingface.co/abuz8studios/FAIE/resolve/main/faie_runner.py
16.8 kB
| """ | |
| FAIE Runner β Multi-layer GPU inference with real ternary weights. | |
| Loads real GLM-5.2 expert weights from GGUF, converts to ternary, | |
| runs multi-layer forward passes on GPU, measures actual tok/s. | |
| """ | |
| import os | |
| import sys | |
| import time | |
| import struct | |
| import numpy as np | |
| import torch | |
| import torch.nn.functional as F | |
| from gguf import GGUFReader | |
| from faie import TernaryCompressor, GLM_CONFIG | |
| from faie_prove import dequant_q2k, dequant_q3k, QK_K, Q2_K_BLOCK_SIZE, Q3_K_BLOCK_SIZE | |
| MODEL_PATH = r"E:\ABU\MODELS\llm\glm-5.2-ream\GLM-5.2-REAM-Q2_K.gguf" | |
| DEVICE = torch.device('cuda:0') | |
| class TernaryExpert: | |
| """One expert's gate+down projections in ternary format.""" | |
| def __init__(self, gate_ternary, gate_scales, gate_shape, gate_gs, | |
| down_ternary, down_scales, down_shape, down_gs): | |
| self.gate_packed, self.gate_n = TernaryCompressor.pack_ternary(gate_ternary) | |
| self.gate_scales = gate_scales | |
| self.gate_shape = gate_shape | |
| self.gate_gs = gate_gs | |
| self.down_packed, self.down_n = TernaryCompressor.pack_ternary(down_ternary) | |
| self.down_scales = down_scales | |
| self.down_shape = down_shape | |
| self.down_gs = down_gs | |
| self.packed_bytes = len(self.gate_packed) + len(self.gate_scales) * 4 + \ | |
| len(self.down_packed) + len(self.down_scales) * 4 | |
| def to_gpu(self): | |
| """Dequantize to FP16 on GPU β simulates cache load.""" | |
| gate_tern = TernaryCompressor.unpack_ternary(self.gate_packed, self.gate_n) | |
| gate_f32 = TernaryCompressor.dequantize( | |
| gate_tern.reshape(self.gate_shape), self.gate_scales, | |
| self.gate_shape, self.gate_gs) | |
| self.W_gate = torch.from_numpy(gate_f32).to(torch.float16).to(DEVICE) | |
| down_tern = TernaryCompressor.unpack_ternary(self.down_packed, self.down_n) | |
| down_f32 = TernaryCompressor.dequantize( | |
| down_tern.reshape(self.down_shape), self.down_scales, | |
| self.down_shape, self.down_gs) | |
| self.W_down = torch.from_numpy(down_f32).to(torch.float16).to(DEVICE) | |
| return self | |
| def forward(self, x): | |
| """Expert forward: gate β SiLU β down.""" | |
| gate_out = F.linear(x, self.W_gate) | |
| activated = F.silu(gate_out) | |
| return F.linear(activated, self.W_down) | |
| def free_gpu(self): | |
| """Release GPU memory.""" | |
| if hasattr(self, 'W_gate'): | |
| del self.W_gate | |
| if hasattr(self, 'W_down'): | |
| del self.W_down | |
| torch.cuda.empty_cache() | |
| def extract_expert(reader, layer_idx, expert_idx=0): | |
| """Extract one expert's gate + down projections from GGUF.""" | |
| # Gate projection | |
| gate_name = f"blk.{layer_idx}.ffn_gate_exps.weight" | |
| down_name = f"blk.{layer_idx}.ffn_down_exps.weight" | |
| gate_tensor = None | |
| down_tensor = None | |
| for t in reader.tensors: | |
| if t.name == gate_name: | |
| gate_tensor = t | |
| elif t.name == down_name: | |
| down_tensor = t | |
| if gate_tensor is None or down_tensor is None: | |
| return None | |
| gate_shape = [int(s) for s in gate_tensor.shape] | |
| down_shape = [int(s) for s in down_tensor.shape] | |
| # Extract gate (Q2_K) | |
| gate_elements = gate_shape[0] * gate_shape[1] | |
| gate_blocks = gate_elements // QK_K | |
| gate_bytes_per = gate_blocks * Q2_K_BLOCK_SIZE | |
| gate_offset = expert_idx * gate_bytes_per | |
| gate_raw = bytes(gate_tensor.data[gate_offset:gate_offset + gate_bytes_per]) | |
| # GGUF stores [in_features, out_features] β transpose for PyTorch [out, in] | |
| gate_f32 = dequant_q2k(gate_raw, gate_elements).reshape(gate_shape[0], gate_shape[1]).T | |
| # Extract down (Q3_K or Q2_K) | |
| down_qtype = down_tensor.tensor_type.name if hasattr(down_tensor.tensor_type, 'name') else str(down_tensor.tensor_type) | |
| down_elements = down_shape[0] * down_shape[1] | |
| if "Q3_K" in down_qtype: | |
| down_blocks = down_elements // QK_K | |
| down_bytes_per = down_blocks * Q3_K_BLOCK_SIZE | |
| down_offset = expert_idx * down_bytes_per | |
| down_raw = bytes(down_tensor.data[down_offset:down_offset + down_bytes_per]) | |
| down_f32 = dequant_q3k(down_raw, down_elements).reshape(down_shape[0], down_shape[1]).T | |
| else: | |
| down_blocks = down_elements // QK_K | |
| down_bytes_per = down_blocks * Q2_K_BLOCK_SIZE | |
| down_offset = expert_idx * down_bytes_per | |
| down_raw = bytes(down_tensor.data[down_offset:down_offset + down_bytes_per]) | |
| down_f32 = dequant_q2k(down_raw, down_elements).reshape(down_shape[0], down_shape[1]).T | |
| # Ternary quantize both | |
| gate_result = TernaryCompressor.quantize(gate_f32) | |
| down_result = TernaryCompressor.quantize(down_f32) | |
| expert = TernaryExpert( | |
| gate_result["ternary"], gate_result["scales"], | |
| gate_f32.shape, gate_result["group_size"], | |
| down_result["ternary"], down_result["scales"], | |
| down_f32.shape, down_result["group_size"], | |
| ) | |
| return expert, gate_f32, down_f32 | |
| def run_multi_layer_simulation(): | |
| """Run actual multi-layer inference with real ternary weights on GPU.""" | |
| print("=" * 70) | |
| print(" FAIE RUNNER β MULTI-LAYER GPU INFERENCE") | |
| print("=" * 70) | |
| if not os.path.exists(MODEL_PATH): | |
| print(f" Model not found: {MODEL_PATH}") | |
| return | |
| print(f"\n Loading GGUF reader (memory-mapped)...") | |
| reader = GGUFReader(MODEL_PATH, 'r') | |
| # Extract experts from multiple layers | |
| n_layers = 5 # test 5 MoE layers | |
| layer_indices = [3, 10, 20, 40, 60] # spread across the model | |
| print(f"\n Extracting {n_layers} experts from layers {layer_indices}...") | |
| experts = [] | |
| orig_gates = [] | |
| orig_downs = [] | |
| for i, layer_idx in enumerate(layer_indices): | |
| print(f" Layer {layer_idx}...", end="", flush=True) | |
| t0 = time.perf_counter() | |
| result = extract_expert(reader, layer_idx, expert_idx=0) | |
| t1 = time.perf_counter() | |
| if result is None: | |
| print(f" FAILED (tensor not found)") | |
| continue | |
| expert, gate_f32, down_f32 = result | |
| experts.append(expert) | |
| orig_gates.append(gate_f32) | |
| orig_downs.append(down_f32) | |
| print(f" done ({t1-t0:.1f}s, {expert.packed_bytes/(1024**2):.1f} MB ternary)") | |
| if not experts: | |
| print(" No experts extracted!") | |
| return | |
| del reader | |
| print(f"\n Total ternary storage: {sum(e.packed_bytes for e in experts)/(1024**2):.1f} MB") | |
| print(f" (Full model would be: {sum(e.packed_bytes for e in experts) / len(experts) * 9600 / (1024**3):.1f} GB)") | |
| # βββ GPU Forward Pass Benchmark βββ | |
| print(f"\n{'β' * 70}") | |
| print(f" GPU FORWARD PASS β TERNARY vs ORIGINAL") | |
| print(f"{'β' * 70}") | |
| model_dim = GLM_CONFIG["embedding_length"] # 6144 | |
| x_np = np.random.randn(model_dim).astype(np.float32) * 0.1 | |
| x = torch.from_numpy(x_np).to(torch.float16).to(DEVICE).unsqueeze(0) | |
| # Load all experts to GPU | |
| print(f"\n Loading {len(experts)} experts to GPU...") | |
| for e in experts: | |
| e.to_gpu() | |
| torch.cuda.synchronize() | |
| vram_after = torch.cuda.memory_allocated() / (1024**3) | |
| print(f" GPU memory used: {vram_after:.2f} GB") | |
| # Warmup β run enough iterations to boost GPU clocks | |
| print(f" Warming up GPU clocks...") | |
| for _ in range(50): | |
| h = x.clone() | |
| for expert in experts: | |
| h = h + 0.1 * expert.forward(h) | |
| torch.cuda.synchronize() | |
| time.sleep(0.1) # let clocks stabilize | |
| # Benchmark: single expert forward (take median of 3 runs for stability) | |
| iters = 500 | |
| times = [] | |
| for run in range(3): | |
| t0 = time.perf_counter() | |
| for _ in range(iters): | |
| _ = experts[0].forward(x) | |
| torch.cuda.synchronize() | |
| t1 = time.perf_counter() | |
| times.append((t1 - t0) / iters * 1e6) | |
| single_expert_us = sorted(times)[1] # median | |
| print(f"\n Single expert forward: {single_expert_us:.1f} ΞΌs (median of {times})") | |
| # Benchmark: multi-layer chain (simulating 1 expert per layer) | |
| times = [] | |
| for run in range(3): | |
| t0 = time.perf_counter() | |
| for _ in range(iters): | |
| h = x.clone() | |
| for expert in experts: | |
| h = h + 0.1 * expert.forward(h) | |
| torch.cuda.synchronize() | |
| t1 = time.perf_counter() | |
| times.append((t1 - t0) / iters * 1e6) | |
| chain_time_us = sorted(times)[1] # median | |
| print(f" {len(experts)}-layer chain: {chain_time_us:.1f} ΞΌs ({chain_time_us/len(experts):.1f} ΞΌs/layer)") | |
| # Extrapolate to full model | |
| n_active = GLM_CONFIG["expert_used_count"] + GLM_CONFIG["expert_shared_count"] # 9 | |
| n_moe_layers = 75 | |
| per_layer_us = chain_time_us / len(experts) | |
| # Full model: 9 experts Γ 75 layers (but experts run in parallel/sequentially) | |
| full_model_us = per_layer_us * n_active * n_moe_layers | |
| full_model_ms = full_model_us / 1000 | |
| full_model_tps = 1000 / full_model_ms if full_model_ms > 0 else 0 | |
| print(f"\n === FULL MODEL EXTRAPOLATION ===") | |
| print(f" Per expert: {single_expert_us:.1f} ΞΌs") | |
| print(f" Per layer (measured chain): {per_layer_us:.1f} ΞΌs") | |
| print(f" Full model ({n_active} experts Γ {n_moe_layers} layers): {full_model_ms:.1f} ms/token") | |
| print(f" Projected tok/s (expert FFN only): {full_model_tps:.0f}") | |
| # βββ Quality: compare ternary vs original through chain βββ | |
| print(f"\n{'β' * 70}") | |
| print(f" MULTI-LAYER QUALITY β TERNARY vs ORIGINAL") | |
| print(f"{'β' * 70}") | |
| # Original forward (CPU, FP32) β weights are [out, in] (PyTorch convention) | |
| h_orig = x_np.copy() | |
| for i, (gate_f32, down_f32) in enumerate(zip(orig_gates, orig_downs)): | |
| gate_out = gate_f32 @ h_orig # [2048, 6144] @ [6144] = [2048] | |
| activated = gate_out * (1 / (1 + np.exp(-gate_out))) # SiLU | |
| expert_out = down_f32 @ activated # [6144, 2048] @ [2048] = [6144] | |
| h_orig = h_orig + 0.1 * expert_out | |
| # Ternary forward (GPU, FP16) | |
| h_tern = x.clone() | |
| for expert in experts: | |
| h_tern = h_tern + 0.1 * expert.forward(h_tern) | |
| h_tern_np = h_tern.squeeze().cpu().float().numpy() | |
| # Compare | |
| cos_sim = np.dot(h_orig, h_tern_np) / (np.linalg.norm(h_orig) * np.linalg.norm(h_tern_np) + 1e-10) | |
| rel_error = np.linalg.norm(h_orig - h_tern_np) / (np.linalg.norm(h_orig) + 1e-10) | |
| print(f"\n After {len(experts)} layers with residual connections:") | |
| print(f" Cosine similarity: {cos_sim:.6f}") | |
| print(f" Relative error: {rel_error:.6f} ({rel_error*100:.4f}%)") | |
| print(f" Original norm: {np.linalg.norm(h_orig):.4f}") | |
| print(f" Ternary norm: {np.linalg.norm(h_tern_np):.4f}") | |
| # βββ Cache Simulation βββ | |
| print(f"\n{'β' * 70}") | |
| print(f" EXPERT PAGER SIMULATION β CACHE HIT vs PCIe") | |
| print(f"{'β' * 70}") | |
| expert_ternary_mb = experts[0].packed_bytes / (1024**2) | |
| expert_fp16_mb = (experts[0].W_gate.numel() + experts[0].W_down.numel()) * 2 / (1024**2) | |
| print(f"\n Expert ternary size (packed): {expert_ternary_mb:.2f} MB") | |
| print(f" Expert FP16 on GPU: {expert_fp16_mb:.1f} MB") | |
| # Real PCIe 5.0 x16 bandwidth: ~32 GB/s for data transfer | |
| pcie_bw_gbs = 32.0 | |
| pcie_transfer_ms = expert_ternary_mb / (pcie_bw_gbs * 1024) * 1000 | |
| gpu_dequant_ms = 0.01 # CUDA kernel unpack is negligible | |
| cache_miss_overhead_ms = pcie_transfer_ms + gpu_dequant_ms | |
| print(f" PCIe 5.0 x16 transfer ({expert_ternary_mb:.1f} MB): {pcie_transfer_ms:.3f} ms") | |
| print(f" GPU dequant kernel (estimated): {gpu_dequant_ms:.3f} ms") | |
| print(f" Total cache miss overhead: {cache_miss_overhead_ms:.3f} ms") | |
| # Measure Python dequant for comparison | |
| experts[0].free_gpu() | |
| torch.cuda.synchronize() | |
| t0 = time.perf_counter() | |
| experts[0].to_gpu() | |
| torch.cuda.synchronize() | |
| t1 = time.perf_counter() | |
| python_dequant_ms = (t1 - t0) * 1000 | |
| print(f" Python dequant (current, for reference): {python_dequant_ms:.0f} ms") | |
| print(f" CUDA kernel would be {python_dequant_ms / cache_miss_overhead_ms:.0f}x faster") | |
| # Speed projections | |
| cache_hit_ms = single_expert_us / 1000 # expert already on GPU as FP16 | |
| # With FP16 (current β PyTorch matmul reads 50 MB/expert from VRAM) | |
| print(f"\n === CURRENT: FP16 MATMUL (PyTorch) ===") | |
| for hit_rate in [1.0, 0.85, 0.70, 0.50]: | |
| avg_ms = hit_rate * cache_hit_ms + (1 - hit_rate) * (cache_miss_overhead_ms + cache_hit_ms) | |
| total_ms = avg_ms * n_active * n_moe_layers | |
| tps = 1000 / total_ms if total_ms > 0 else 0 | |
| marker = " β TARGET" if abs(hit_rate - 0.70) < 0.01 else "" | |
| print(f" {hit_rate*100:5.0f}% cache: {total_ms:7.1f} ms/tok = {tps:5.0f} tok/s{marker}") | |
| # With CUDA ternary kernel (reads 4.8 MB/expert instead of 50 MB) | |
| gpu_bw = 891.0 # measured GB/s | |
| ternary_expert_read_mb = expert_ternary_mb # ~7.5 MB packed data read from VRAM | |
| ternary_matmul_ms = ternary_expert_read_mb / (gpu_bw * 1024) * 1000 * 2 # x2 for gate+down | |
| print(f"\n === WITH CUDA TERNARY KERNEL (reads {ternary_expert_read_mb:.1f} MB vs {expert_fp16_mb:.0f} MB FP16) ===") | |
| for hit_rate in [1.0, 0.85, 0.70, 0.50]: | |
| avg_ms = hit_rate * ternary_matmul_ms + (1 - hit_rate) * (cache_miss_overhead_ms + ternary_matmul_ms) | |
| total_ms = avg_ms * n_active * n_moe_layers | |
| tps = 1000 / total_ms if total_ms > 0 else 0 | |
| marker = " β TARGET" if abs(hit_rate - 0.70) < 0.01 else "" | |
| print(f" {hit_rate*100:5.0f}% cache: {total_ms:7.1f} ms/tok = {tps:5.0f} tok/s{marker}") | |
| # βββ Final Verdict βββ | |
| print(f"\n{'=' * 70}") | |
| print(f" FAIE RUNNER VERDICT") | |
| print(f"{'=' * 70}") | |
| # 70% cache speed with CUDA ternary kernel | |
| avg_70_ternary = 0.70 * ternary_matmul_ms + 0.30 * (cache_miss_overhead_ms + ternary_matmul_ms) | |
| total_70_ternary = avg_70_ternary * n_active * n_moe_layers | |
| tps_70_ternary = 1000 / total_70_ternary if total_70_ternary > 0 else 0 | |
| # 100% cache speed with FP16 (current) | |
| total_100_fp16 = cache_hit_ms * n_active * n_moe_layers | |
| tps_100_fp16 = 1000 / total_100_fp16 if total_100_fp16 > 0 else 0 | |
| # Pipelined analysis: PCIe for next layer overlaps current layer compute | |
| # With presynthesizer: predict 90% of next token's experts during current token | |
| miss_rate_with_presyn = 0.10 # presynthesizer reduces effective miss rate | |
| n_experts_per_token = n_active * n_moe_layers | |
| n_misses_pipelined = int(n_experts_per_token * miss_rate_with_presyn) | |
| pcie_overlap_ms = n_misses_pipelined * cache_miss_overhead_ms | |
| total_pipelined_fp16 = max(total_100_fp16, pcie_overlap_ms) | |
| total_pipelined_ternary = max(total_70_ternary * (1.0 / n_active / n_moe_layers) * n_experts_per_token * ternary_matmul_ms, pcie_overlap_ms) | |
| tps_pipelined_fp16 = 1000 / total_pipelined_fp16 if total_pipelined_fp16 > 0 else 0 | |
| print(f"\n === WITH PRESYNTHESIZER PRE-LOADING (90% prediction accuracy) ===") | |
| print(f" Misses per token: {n_misses_pipelined}/{n_experts_per_token}") | |
| print(f" PCIe time (overlapped): {pcie_overlap_ms:.1f} ms") | |
| print(f" FP16 compute: {total_100_fp16:.1f} ms β {tps_pipelined_fp16:.0f} tok/s") | |
| # Ternary kernel with pipelining | |
| total_ternary_compute = ternary_matmul_ms * n_experts_per_token | |
| total_pipelined_ternary_final = max(total_ternary_compute, pcie_overlap_ms) | |
| tps_pipelined_ternary = 1000 / total_pipelined_ternary_final if total_pipelined_ternary_final > 0 else 0 | |
| print(f" Ternary compute: {total_ternary_compute:.1f} ms β {tps_pipelined_ternary:.0f} tok/s") | |
| checks = [ | |
| ("Multi-layer cosine > 0.99", cos_sim > 0.99, f"{cos_sim:.6f}"), | |
| ("Multi-layer relative error < 10%", rel_error < 0.10, f"{rel_error*100:.4f}%"), | |
| ("Single expert < 500 ΞΌs", single_expert_us < 500, f"{single_expert_us:.0f} ΞΌs"), | |
| ("PCIe cache miss < 1 ms", cache_miss_overhead_ms < 1.0, f"{cache_miss_overhead_ms:.3f} ms"), | |
| ("100% cache (FP16) β₯ 25 tok/s", tps_100_fp16 >= 25, f"{tps_100_fp16:.0f} tok/s (target: 30)"), | |
| ("Ternary kernel 100% cache > 60 tok/s", (1000 / (ternary_matmul_ms * n_experts_per_token)) > 60, | |
| f"{1000 / (ternary_matmul_ms * n_experts_per_token):.0f} tok/s"), | |
| ] | |
| all_pass = True | |
| for label, passed, detail in checks: | |
| status = "PASS" if passed else "FAIL" | |
| all_pass = all_pass and passed | |
| detail_str = f" ({detail})" if detail else "" | |
| print(f" [{status}] {label}{detail_str}") | |
| print(f"\n Overall: {'ALL CHECKS PASSED' if all_pass else 'SOME CHECKS FAILED'}") | |
| # Clean up | |
| for e in experts: | |
| e.free_gpu() | |
| print(f"\n{'=' * 70}") | |
| if __name__ == "__main__": | |
| run_multi_layer_simulation() | |