From 7c7a899fd5226285bfc106963e5c76141e68ba1e Mon Sep 17 00:00:00 2001 From: Variet-Worker Date: Mon, 6 Apr 2026 21:49:56 +0900 Subject: [PATCH] Update tuning scripts and add task creation to sync_vikunja.js --- .agent/.agent/scripts/translate_gsd.py | 86 - .../skills/ui-ux-pro-max/scripts/core.py | 253 --- .../ui-ux-pro-max/scripts/design_system.py | 1067 ----------- .../skills/ui-ux-pro-max/scripts/search.py | 114 -- .agent/scripts/sync_vikunja.js | 108 +- scripts/analysis_raw.txt | 58 + scripts/auto_tune_122b.py | 372 ++++ scripts/auto_tune_122b_r2.py | 257 +++ scripts/auto_tune_gemma4_256k.py | 339 ++++ scripts/auto_tune_gemma4_ncpumoe.py | 163 ++ scripts/auto_tune_qwen35b_256k.py | 335 ++++ scripts/boot_122b.txt | Bin 0 -> 26954 bytes scripts/boot_122b_38.txt | Bin 0 -> 25252 bytes scripts/boot_122b_42.txt | Bin 0 -> 28522 bytes scripts/boot_122b_44.txt | Bin 0 -> 38504 bytes scripts/boot_122b_auto.txt | Bin 0 -> 28162 bytes scripts/boot_122b_maxmem.txt | Bin 0 -> 28272 bytes scripts/boot_122b_row.txt | Bin 0 -> 28366 bytes scripts/boot_122b_row_dual.txt | Bin 0 -> 25252 bytes scripts/boot_122b_single.txt | Bin 0 -> 37600 bytes scripts/boot_122b_single2.txt | Bin 0 -> 30360 bytes scripts/boot_122b_ts85.txt | Bin 0 -> 28564 bytes scripts/boot_122b_tune.txt | Bin 0 -> 25234 bytes scripts/boot_122b_tuned.txt | Bin 0 -> 40074 bytes scripts/boot_122b_v2.txt | Bin 0 -> 61200 bytes scripts/boot_log.txt | Bin 0 -> 29276 bytes scripts/boot_log2.txt | Bin 0 -> 27986 bytes scripts/boot_log3.txt | Bin 0 -> 27936 bytes scripts/boot_log4.txt | Bin 0 -> 9184 bytes scripts/boot_log5.txt | Bin 0 -> 27868 bytes scripts/boot_qwen_iq4.txt | Bin 0 -> 29832 bytes scripts/check_help.bat | 3 + scripts/download_llama.py | 38 + scripts/download_models.py | 33 + scripts/download_true_llama.py | 56 + scripts/dual_gpu_benchmark.mjs | 531 ++++++ scripts/dual_gpu_benchmark.py | 644 +++++++ scripts/dual_gpu_benchmark_v2.mjs | 330 ++++ scripts/dual_gpu_results.json | 1654 +++++++++++++++++ scripts/dual_gpu_summary.txt | 31 + scripts/final_tune_122b.txt | Bin 0 -> 30310 bytes scripts/final_tune_122b_dual.txt | Bin 0 -> 48340 bytes scripts/find_max_dense.mjs | 101 + scripts/help_full.txt | 562 ++++++ scripts/help_gpu_flags.txt | 31 + scripts/hf_search.py | 28 + scripts/perf_test.py | 123 ++ scripts/perf_test_122b.py | 169 ++ scripts/q4km_latest.txt | 5 + scripts/quick_pptest.mjs | 31 + scripts/qwen_fullgpu_challenge.mjs | 345 ++++ scripts/qwen_fullgpu_results.json | 834 +++++++++ scripts/qwen_intermediate.csv | 12 + scripts/qwen_latest.txt | 24 + scripts/test_20ts.txt | Bin 0 -> 1384 bytes scripts/tune_122b_20ts.mjs | 64 + scripts/tune_exact.mjs | 72 + scripts/tune_models.mjs | 84 + scripts/tune_results_gemma4_256k.json | 591 ++++++ scripts/tune_results_gemma4_ncpumoe.json | 201 ++ scripts/tune_results_qwen35b_256k.json | 522 ++++++ 61 files changed, 8705 insertions(+), 1566 deletions(-) delete mode 100644 .agent/.agent/scripts/translate_gsd.py delete mode 100644 .agent/.agent/skills/ui-ux-pro-max/scripts/core.py delete mode 100644 .agent/.agent/skills/ui-ux-pro-max/scripts/design_system.py delete mode 100644 .agent/.agent/skills/ui-ux-pro-max/scripts/search.py create mode 100644 scripts/analysis_raw.txt create mode 100644 scripts/auto_tune_122b.py create mode 100644 scripts/auto_tune_122b_r2.py create mode 100644 scripts/auto_tune_gemma4_256k.py create mode 100644 scripts/auto_tune_gemma4_ncpumoe.py create mode 100644 scripts/auto_tune_qwen35b_256k.py create mode 100644 scripts/boot_122b.txt create mode 100644 scripts/boot_122b_38.txt create mode 100644 scripts/boot_122b_42.txt create mode 100644 scripts/boot_122b_44.txt create mode 100644 scripts/boot_122b_auto.txt create mode 100644 scripts/boot_122b_maxmem.txt create mode 100644 scripts/boot_122b_row.txt create mode 100644 scripts/boot_122b_row_dual.txt create mode 100644 scripts/boot_122b_single.txt create mode 100644 scripts/boot_122b_single2.txt create mode 100644 scripts/boot_122b_ts85.txt create mode 100644 scripts/boot_122b_tune.txt create mode 100644 scripts/boot_122b_tuned.txt create mode 100644 scripts/boot_122b_v2.txt create mode 100644 scripts/boot_log.txt create mode 100644 scripts/boot_log2.txt create mode 100644 scripts/boot_log3.txt create mode 100644 scripts/boot_log4.txt create mode 100644 scripts/boot_log5.txt create mode 100644 scripts/boot_qwen_iq4.txt create mode 100644 scripts/check_help.bat create mode 100644 scripts/download_llama.py create mode 100644 scripts/download_models.py create mode 100644 scripts/download_true_llama.py create mode 100644 scripts/dual_gpu_benchmark.mjs create mode 100644 scripts/dual_gpu_benchmark.py create mode 100644 scripts/dual_gpu_benchmark_v2.mjs create mode 100644 scripts/dual_gpu_results.json create mode 100644 scripts/dual_gpu_summary.txt create mode 100644 scripts/final_tune_122b.txt create mode 100644 scripts/final_tune_122b_dual.txt create mode 100644 scripts/find_max_dense.mjs create mode 100644 scripts/help_full.txt create mode 100644 scripts/help_gpu_flags.txt create mode 100644 scripts/hf_search.py create mode 100644 scripts/perf_test.py create mode 100644 scripts/perf_test_122b.py create mode 100644 scripts/q4km_latest.txt create mode 100644 scripts/quick_pptest.mjs create mode 100644 scripts/qwen_fullgpu_challenge.mjs create mode 100644 scripts/qwen_fullgpu_results.json create mode 100644 scripts/qwen_intermediate.csv create mode 100644 scripts/qwen_latest.txt create mode 100644 scripts/test_20ts.txt create mode 100644 scripts/tune_122b_20ts.mjs create mode 100644 scripts/tune_exact.mjs create mode 100644 scripts/tune_models.mjs create mode 100644 scripts/tune_results_gemma4_256k.json create mode 100644 scripts/tune_results_gemma4_ncpumoe.json create mode 100644 scripts/tune_results_qwen35b_256k.json diff --git a/.agent/.agent/scripts/translate_gsd.py b/.agent/.agent/scripts/translate_gsd.py deleted file mode 100644 index e5e5708..0000000 --- a/.agent/.agent/scripts/translate_gsd.py +++ /dev/null @@ -1,86 +0,0 @@ -import os -import glob -import re - -skill_dir = r"C:\Users\Certes\.gemini\antigravity\skills" - -translations = { - "Manage parallel workstreams — list, create, switch, status, progress, complete, and resume": "병렬 작업 스트림 관리 — 목록, 생성, 전환, 상태, 진행률, 완료 및 재개", - "Validate built features through conversational UAT": "대화형 UAT를 통해 구현된 기능 검증", - "Retroactively audit and fill Nyquist validation gaps for a completed phase": "완료된 단계에 대한 검증 누락 사후 감사 및 보완", - "Update GSD to latest version with changelog display": "GSD를 최신 버전으로 업데이트하고 변경 사항 표시", - "Retroactive 6-pillar visual audit of implemented frontend code": "구현된 프론트엔드 코드에 대한 6개 요소 시각적 사후 감사", - "Generate UI design contract (UI-SPEC.md) for frontend phases": "프론트엔드 단계를 위한 UI 디자인 명세서(UI-SPEC.md) 생성", - "Manage persistent context threads for cross-session work": "교차 세션 작업을 위한 영구 컨텍스트 스레드 관리", - "Display project statistics — phases, plans, requirements, git metrics, and timeline": "프로젝트 통계 표시 — 단계, 계획, 요구사항, Git 지표 및 타임라인", - "Create PR, run review, and prepare for merge after verification passes": "검증 통과 후 PR 생성, 리뷰 실행 및 병합 준비", - "Configure GSD workflow toggles and model profile": "GSD 워크플로우 옵션 및 모델 프로필 구성", - "Switch model profile for GSD agents (quality/balanced/budget/inherit)": "GSD 요원의 모델 프로필 전환 (고품질/균형/예산/상속)", - "Generate a session report with token usage estimates, work summary, and outcomes": "토큰 사용량, 작업 요약 및 결과를 포함한 세션 보고서 생성", - "Review and promote backlog items to active milestone": "백로그 항목을 검토하고 활성 마일스톤으로 승격", - "Request cross-AI peer review of phase plans from external AI CLIs": "외부 AI CLI에 단계 계획에 대한 교차 AI 동료 리뷰 요청", - "Resume work from previous session with full context restoration": "전체 컨텍스트 복원과 함께 이전 세션에서 작업 재개", - "Research how to implement a phase (standalone - usually use /gsd-plan-phase instead)": "단계를 구현하는 방법 리서치 (단독 실행 - 보통 /gsd-plan-phase 사용)", - "Remove a GSD workspace and clean up worktrees": "GSD 워크스페이스 제거 및 워크트리 정리", - "Remove a future phase from roadmap and renumber subsequent phases": "로드맵에서 향후 단계를 제거하고 이후 단계 번호 재지정", - "Reapply local modifications after a GSD update": "GSD 업데이트 후 로컬 수정 사항 재적용", - "Execute a quick task with GSD guarantees (atomic commits, state tracking) but skip optional agents": "GSD 보장(원자적 커밋, 상태 추적)을 사용하여 빠른 작업을 실행하되 선택적 요원 생략", - "Check project progress, show context, and route to next action (execute or plan)": "프로젝트 진행 상황 확인, 컨텍스트 표시 및 다음 작업(실행 또는 계획)으로 라우팅", - "Generate developer behavioral profile and create Claude-discoverable artifacts": "개발자 행동 프로필을 생성하고 AI가 인지할 수 있는 문서 작성", - "Create a clean PR branch by filtering out .planning/ commits — ready for code review": ".planning/ 커밋을 필터링하여 깔끔한 PR 브랜치 생성 — 코드 리뷰 준비", - "Capture a forward-looking idea with trigger conditions — surfaces automatically at the right milestone": "향후 아이디어를 트리거 조건과 함께 캡처 — 적절한 마일스톤에서 자동 표시", - "Create detailed phase plan (PLAN.md) with verification loop": "검증 루프를 포함한 상세 단계 계획(PLAN.md) 생성", - "Create phases to close all gaps identified by milestone audit": "마일스톤 감사에서 식별된 모든 격차를 해소하기 위한 단계 생성", - "Create context handoff when pausing work mid-phase": "작업 중단 시 컨텍스트 인수인계 파일 생성", - "Zero-friction idea capture. Append, list, or promote notes to todos.": "방해 없는 아이디어 캡처. 메모 추가, 나열 또는 할 일로 승격.", - "Automatically advance to the next logical step in the GSD workflow": "GSD 워크플로우의 다음 논리적 단계로 자동 진행", - "Create an isolated workspace with repo copies and independent .planning/": "외부 레포 사본 및 독립적인 .planning/을 갖춘 격리된 워크스페이스 생성", - "Initialize a new project with deep context gathering and PROJECT.md": "심층 컨텍스트 수집 및 PROJECT.md와 함께 새 프로젝트 초기화", - "Start a new milestone cycle — update PROJECT.md and route to requirements": "새로운 마일스톤 주기 시작 — PROJECT.md 업데이트 및 요구사항 재정의", - "Generate a comprehensive project summary from milestone artifacts for team onboarding and review": "팀 온보딩 및 리뷰를 위해 마일스톤 산출물에서 종합적인 프로젝트 요약 생성", - "Analyze codebase with parallel mapper agents to produce .planning/codebase/ documents": "병렬 매퍼 요원으로 코드베이스를 분석하여 .planning/codebase/ 문서 생성", - "Interactive command center for managing multiple phases from one terminal": "하나의 터미널에서 여러 단계를 관리하는 대화형 명령 센터", - "List active GSD workspaces and their status": "활성 GSD 워크스페이스 및 상태 나열", - "Surface the agent's assumptions about a phase approach before planning": "계획 전 단계적 접근 방식에 대한 요원의 가정을 미리 표시", - "Join the GSD Discord community": "GSD 디스코드 커뮤니티 참가", - "Insert urgent work as decimal phase (e.g., 72.1) between existing phases": "기존 단계 사이에 소수점 단계(예: 72.1)로 긴급 작업 삽입", - "Show available GSD commands and usage guide": "사용 가능한 GSD 명령어 및 사용 가이드 표시", - "Diagnose planning directory health and optionally repair issues": "계획 디렉토리 상태 진단 및 선택적으로 문제 복구", - "Post-mortem investigation for failed GSD workflows — analyzes git history, artifacts, and state to diagnose what went wrong": "실패한 GSD 워크플로우에 대한 사후 조사 — git 기록, 문서 및 상태 분석", - "Execute a trivial task inline — no subagents, no planning overhead": "인라인으로 사소한 작업 실행 — 서브 에이전트 및 계획 오버헤드 없음", - "Execute all plans in a phase with wave-based parallelization": "웨이브(Wave) 기반 병렬 처리를 사용하여 단계의 모든 계획 실행", - "Route freeform text to the right GSD command automatically": "자유 형식 텍스트를 적절한 GSD 명령으로 자동 라우팅", - "Systematic debugging with persistent state across context resets": "컨텍스트가 리셋되어도 상태를 유지하는 체계적인 디버깅", - "Gather phase context through adaptive questioning before planning. Use --auto to skip interactive questions (the agent picks recommended defaults).": "계획 전 심층 질문을 통해 단계 컨텍스트 수집. 대화형 건너뛰기(--auto) 가능.", - "Archive completed milestone and prepare for next version": "완료된 마일스톤 보관 및 다음 버전 준비", - "List pending todos and select one to work on": "보류 중인 할 일 목록 표시 및 작업할 항목 선택", - "Cross-phase audit of all outstanding UAT and verification items": "모든 미결 UAT 및 검증 항목에 대한 전체 단계 교차 감사", - "Audit milestone completion against original intent before archiving": "보관 전 원래 의도와 비교하여 마일스톤 달성 여부 감사", - "Capture idea or task as todo from current conversation context": "현재 대화 컨텍스트에서 아이디어 또는 작업을 할 일로 캡처", - "Generate tests for a completed phase based on UAT criteria and implementation": "UAT 기준 및 구현을 기반으로 완료된 단계에 대한 테스트 생성", - "Add phase to end of current milestone in roadmap": "로드맵의 현재 마일스톤 끝에 새 단계 추가", - "Add an idea to the backlog parking lot (999.x numbering)": "백로그 주차장(999.x 넘버링)에 아이디어 추가", - "Run all remaining phases autonomously — discuss→plan→execute per phase": "모든 남은 단계를 완전히 자율적으로 실행 (논의→계획→실행 루프)", - "Archive accumulated phase directories from completed milestones": "완료된 마일스톤에서 쌓인 단계 디렉토리 보관 및 정리" -} - -modified_count = 0 - -for filepath in glob.glob(os.path.join(skill_dir, "gsd-*", "SKILL.md")): - try: - with open(filepath, 'r', encoding='utf-8') as f: - content = f.read() - - new_content = content - for eng, kor in translations.items(): - pattern = re.compile(r"^description:\s*" + re.escape(eng) + r"\s*$", re.MULTILINE) - new_content = pattern.sub(f"description: {kor}", new_content) - - if new_content != content: - with open(filepath, 'w', encoding='utf-8') as f: - f.write(new_content) - modified_count += 1 - except Exception as e: - print(f"Error processing {filepath}: {e}") - -print(f"Successfully translated {modified_count} SKILL.md files.") diff --git a/.agent/.agent/skills/ui-ux-pro-max/scripts/core.py b/.agent/.agent/skills/ui-ux-pro-max/scripts/core.py deleted file mode 100644 index b7ba227..0000000 --- a/.agent/.agent/skills/ui-ux-pro-max/scripts/core.py +++ /dev/null @@ -1,253 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -UI/UX Pro Max Core - BM25 search engine for UI/UX style guides -""" - -import csv -import re -from pathlib import Path -from math import log -from collections import defaultdict - -# ============ CONFIGURATION ============ -DATA_DIR = Path(__file__).parent.parent / "data" -MAX_RESULTS = 3 - -CSV_CONFIG = { - "style": { - "file": "styles.csv", - "search_cols": ["Style Category", "Keywords", "Best For", "Type", "AI Prompt Keywords"], - "output_cols": ["Style Category", "Type", "Keywords", "Primary Colors", "Effects & Animation", "Best For", "Performance", "Accessibility", "Framework Compatibility", "Complexity", "AI Prompt Keywords", "CSS/Technical Keywords", "Implementation Checklist", "Design System Variables"] - }, - "color": { - "file": "colors.csv", - "search_cols": ["Product Type", "Notes"], - "output_cols": ["Product Type", "Primary (Hex)", "Secondary (Hex)", "CTA (Hex)", "Background (Hex)", "Text (Hex)", "Notes"] - }, - "chart": { - "file": "charts.csv", - "search_cols": ["Data Type", "Keywords", "Best Chart Type", "Accessibility Notes"], - "output_cols": ["Data Type", "Keywords", "Best Chart Type", "Secondary Options", "Color Guidance", "Accessibility Notes", "Library Recommendation", "Interactive Level"] - }, - "landing": { - "file": "landing.csv", - "search_cols": ["Pattern Name", "Keywords", "Conversion Optimization", "Section Order"], - "output_cols": ["Pattern Name", "Keywords", "Section Order", "Primary CTA Placement", "Color Strategy", "Conversion Optimization"] - }, - "product": { - "file": "products.csv", - "search_cols": ["Product Type", "Keywords", "Primary Style Recommendation", "Key Considerations"], - "output_cols": ["Product Type", "Keywords", "Primary Style Recommendation", "Secondary Styles", "Landing Page Pattern", "Dashboard Style (if applicable)", "Color Palette Focus"] - }, - "ux": { - "file": "ux-guidelines.csv", - "search_cols": ["Category", "Issue", "Description", "Platform"], - "output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"] - }, - "typography": { - "file": "typography.csv", - "search_cols": ["Font Pairing Name", "Category", "Mood/Style Keywords", "Best For", "Heading Font", "Body Font"], - "output_cols": ["Font Pairing Name", "Category", "Heading Font", "Body Font", "Mood/Style Keywords", "Best For", "Google Fonts URL", "CSS Import", "Tailwind Config", "Notes"] - }, - "icons": { - "file": "icons.csv", - "search_cols": ["Category", "Icon Name", "Keywords", "Best For"], - "output_cols": ["Category", "Icon Name", "Keywords", "Library", "Import Code", "Usage", "Best For", "Style"] - }, - "react": { - "file": "react-performance.csv", - "search_cols": ["Category", "Issue", "Keywords", "Description"], - "output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"] - }, - "web": { - "file": "web-interface.csv", - "search_cols": ["Category", "Issue", "Keywords", "Description"], - "output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"] - } -} - -STACK_CONFIG = { - "html-tailwind": {"file": "stacks/html-tailwind.csv"}, - "react": {"file": "stacks/react.csv"}, - "nextjs": {"file": "stacks/nextjs.csv"}, - "astro": {"file": "stacks/astro.csv"}, - "vue": {"file": "stacks/vue.csv"}, - "nuxtjs": {"file": "stacks/nuxtjs.csv"}, - "nuxt-ui": {"file": "stacks/nuxt-ui.csv"}, - "svelte": {"file": "stacks/svelte.csv"}, - "swiftui": {"file": "stacks/swiftui.csv"}, - "react-native": {"file": "stacks/react-native.csv"}, - "flutter": {"file": "stacks/flutter.csv"}, - "shadcn": {"file": "stacks/shadcn.csv"}, - "jetpack-compose": {"file": "stacks/jetpack-compose.csv"} -} - -# Common columns for all stacks -_STACK_COLS = { - "search_cols": ["Category", "Guideline", "Description", "Do", "Don't"], - "output_cols": ["Category", "Guideline", "Description", "Do", "Don't", "Code Good", "Code Bad", "Severity", "Docs URL"] -} - -AVAILABLE_STACKS = list(STACK_CONFIG.keys()) - - -# ============ BM25 IMPLEMENTATION ============ -class BM25: - """BM25 ranking algorithm for text search""" - - def __init__(self, k1=1.5, b=0.75): - self.k1 = k1 - self.b = b - self.corpus = [] - self.doc_lengths = [] - self.avgdl = 0 - self.idf = {} - self.doc_freqs = defaultdict(int) - self.N = 0 - - def tokenize(self, text): - """Lowercase, split, remove punctuation, filter short words""" - text = re.sub(r'[^\w\s]', ' ', str(text).lower()) - return [w for w in text.split() if len(w) > 2] - - def fit(self, documents): - """Build BM25 index from documents""" - self.corpus = [self.tokenize(doc) for doc in documents] - self.N = len(self.corpus) - if self.N == 0: - return - self.doc_lengths = [len(doc) for doc in self.corpus] - self.avgdl = sum(self.doc_lengths) / self.N - - for doc in self.corpus: - seen = set() - for word in doc: - if word not in seen: - self.doc_freqs[word] += 1 - seen.add(word) - - for word, freq in self.doc_freqs.items(): - self.idf[word] = log((self.N - freq + 0.5) / (freq + 0.5) + 1) - - def score(self, query): - """Score all documents against query""" - query_tokens = self.tokenize(query) - scores = [] - - for idx, doc in enumerate(self.corpus): - score = 0 - doc_len = self.doc_lengths[idx] - term_freqs = defaultdict(int) - for word in doc: - term_freqs[word] += 1 - - for token in query_tokens: - if token in self.idf: - tf = term_freqs[token] - idf = self.idf[token] - numerator = tf * (self.k1 + 1) - denominator = tf + self.k1 * (1 - self.b + self.b * doc_len / self.avgdl) - score += idf * numerator / denominator - - scores.append((idx, score)) - - return sorted(scores, key=lambda x: x[1], reverse=True) - - -# ============ SEARCH FUNCTIONS ============ -def _load_csv(filepath): - """Load CSV and return list of dicts""" - with open(filepath, 'r', encoding='utf-8') as f: - return list(csv.DictReader(f)) - - -def _search_csv(filepath, search_cols, output_cols, query, max_results): - """Core search function using BM25""" - if not filepath.exists(): - return [] - - data = _load_csv(filepath) - - # Build documents from search columns - documents = [" ".join(str(row.get(col, "")) for col in search_cols) for row in data] - - # BM25 search - bm25 = BM25() - bm25.fit(documents) - ranked = bm25.score(query) - - # Get top results with score > 0 - results = [] - for idx, score in ranked[:max_results]: - if score > 0: - row = data[idx] - results.append({col: row.get(col, "") for col in output_cols if col in row}) - - return results - - -def detect_domain(query): - """Auto-detect the most relevant domain from query""" - query_lower = query.lower() - - domain_keywords = { - "color": ["color", "palette", "hex", "#", "rgb"], - "chart": ["chart", "graph", "visualization", "trend", "bar", "pie", "scatter", "heatmap", "funnel"], - "landing": ["landing", "page", "cta", "conversion", "hero", "testimonial", "pricing", "section"], - "product": ["saas", "ecommerce", "e-commerce", "fintech", "healthcare", "gaming", "portfolio", "crypto", "dashboard"], - "style": ["style", "design", "ui", "minimalism", "glassmorphism", "neumorphism", "brutalism", "dark mode", "flat", "aurora", "prompt", "css", "implementation", "variable", "checklist", "tailwind"], - "ux": ["ux", "usability", "accessibility", "wcag", "touch", "scroll", "animation", "keyboard", "navigation", "mobile"], - "typography": ["font", "typography", "heading", "serif", "sans"], - "icons": ["icon", "icons", "lucide", "heroicons", "symbol", "glyph", "pictogram", "svg icon"], - "react": ["react", "next.js", "nextjs", "suspense", "memo", "usecallback", "useeffect", "rerender", "bundle", "waterfall", "barrel", "dynamic import", "rsc", "server component"], - "web": ["aria", "focus", "outline", "semantic", "virtualize", "autocomplete", "form", "input type", "preconnect"] - } - - scores = {domain: sum(1 for kw in keywords if kw in query_lower) for domain, keywords in domain_keywords.items()} - best = max(scores, key=scores.get) - return best if scores[best] > 0 else "style" - - -def search(query, domain=None, max_results=MAX_RESULTS): - """Main search function with auto-domain detection""" - if domain is None: - domain = detect_domain(query) - - config = CSV_CONFIG.get(domain, CSV_CONFIG["style"]) - filepath = DATA_DIR / config["file"] - - if not filepath.exists(): - return {"error": f"File not found: {filepath}", "domain": domain} - - results = _search_csv(filepath, config["search_cols"], config["output_cols"], query, max_results) - - return { - "domain": domain, - "query": query, - "file": config["file"], - "count": len(results), - "results": results - } - - -def search_stack(query, stack, max_results=MAX_RESULTS): - """Search stack-specific guidelines""" - if stack not in STACK_CONFIG: - return {"error": f"Unknown stack: {stack}. Available: {', '.join(AVAILABLE_STACKS)}"} - - filepath = DATA_DIR / STACK_CONFIG[stack]["file"] - - if not filepath.exists(): - return {"error": f"Stack file not found: {filepath}", "stack": stack} - - results = _search_csv(filepath, _STACK_COLS["search_cols"], _STACK_COLS["output_cols"], query, max_results) - - return { - "domain": "stack", - "stack": stack, - "query": query, - "file": STACK_CONFIG[stack]["file"], - "count": len(results), - "results": results - } diff --git a/.agent/.agent/skills/ui-ux-pro-max/scripts/design_system.py b/.agent/.agent/skills/ui-ux-pro-max/scripts/design_system.py deleted file mode 100644 index 209de20..0000000 --- a/.agent/.agent/skills/ui-ux-pro-max/scripts/design_system.py +++ /dev/null @@ -1,1067 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Design System Generator - Aggregates search results and applies reasoning -to generate comprehensive design system recommendations. - -Usage: - from design_system import generate_design_system - result = generate_design_system("SaaS dashboard", "My Project") - - # With persistence (Master + Overrides pattern) - result = generate_design_system("SaaS dashboard", "My Project", persist=True) - result = generate_design_system("SaaS dashboard", "My Project", persist=True, page="dashboard") -""" - -import csv -import json -import os -from datetime import datetime -from pathlib import Path -from core import search, DATA_DIR - - -# ============ CONFIGURATION ============ -REASONING_FILE = "ui-reasoning.csv" - -SEARCH_CONFIG = { - "product": {"max_results": 1}, - "style": {"max_results": 3}, - "color": {"max_results": 2}, - "landing": {"max_results": 2}, - "typography": {"max_results": 2} -} - - -# ============ DESIGN SYSTEM GENERATOR ============ -class DesignSystemGenerator: - """Generates design system recommendations from aggregated searches.""" - - def __init__(self): - self.reasoning_data = self._load_reasoning() - - def _load_reasoning(self) -> list: - """Load reasoning rules from CSV.""" - filepath = DATA_DIR / REASONING_FILE - if not filepath.exists(): - return [] - with open(filepath, 'r', encoding='utf-8') as f: - return list(csv.DictReader(f)) - - def _multi_domain_search(self, query: str, style_priority: list = None) -> dict: - """Execute searches across multiple domains.""" - results = {} - for domain, config in SEARCH_CONFIG.items(): - if domain == "style" and style_priority: - # For style, also search with priority keywords - priority_query = " ".join(style_priority[:2]) if style_priority else query - combined_query = f"{query} {priority_query}" - results[domain] = search(combined_query, domain, config["max_results"]) - else: - results[domain] = search(query, domain, config["max_results"]) - return results - - def _find_reasoning_rule(self, category: str) -> dict: - """Find matching reasoning rule for a category.""" - category_lower = category.lower() - - # Try exact match first - for rule in self.reasoning_data: - if rule.get("UI_Category", "").lower() == category_lower: - return rule - - # Try partial match - for rule in self.reasoning_data: - ui_cat = rule.get("UI_Category", "").lower() - if ui_cat in category_lower or category_lower in ui_cat: - return rule - - # Try keyword match - for rule in self.reasoning_data: - ui_cat = rule.get("UI_Category", "").lower() - keywords = ui_cat.replace("/", " ").replace("-", " ").split() - if any(kw in category_lower for kw in keywords): - return rule - - return {} - - def _apply_reasoning(self, category: str, search_results: dict) -> dict: - """Apply reasoning rules to search results.""" - rule = self._find_reasoning_rule(category) - - if not rule: - return { - "pattern": "Hero + Features + CTA", - "style_priority": ["Minimalism", "Flat Design"], - "color_mood": "Professional", - "typography_mood": "Clean", - "key_effects": "Subtle hover transitions", - "anti_patterns": "", - "decision_rules": {}, - "severity": "MEDIUM" - } - - # Parse decision rules JSON - decision_rules = {} - try: - decision_rules = json.loads(rule.get("Decision_Rules", "{}")) - except json.JSONDecodeError: - pass - - return { - "pattern": rule.get("Recommended_Pattern", ""), - "style_priority": [s.strip() for s in rule.get("Style_Priority", "").split("+")], - "color_mood": rule.get("Color_Mood", ""), - "typography_mood": rule.get("Typography_Mood", ""), - "key_effects": rule.get("Key_Effects", ""), - "anti_patterns": rule.get("Anti_Patterns", ""), - "decision_rules": decision_rules, - "severity": rule.get("Severity", "MEDIUM") - } - - def _select_best_match(self, results: list, priority_keywords: list) -> dict: - """Select best matching result based on priority keywords.""" - if not results: - return {} - - if not priority_keywords: - return results[0] - - # First: try exact style name match - for priority in priority_keywords: - priority_lower = priority.lower().strip() - for result in results: - style_name = result.get("Style Category", "").lower() - if priority_lower in style_name or style_name in priority_lower: - return result - - # Second: score by keyword match in all fields - scored = [] - for result in results: - result_str = str(result).lower() - score = 0 - for kw in priority_keywords: - kw_lower = kw.lower().strip() - # Higher score for style name match - if kw_lower in result.get("Style Category", "").lower(): - score += 10 - # Lower score for keyword field match - elif kw_lower in result.get("Keywords", "").lower(): - score += 3 - # Even lower for other field matches - elif kw_lower in result_str: - score += 1 - scored.append((score, result)) - - scored.sort(key=lambda x: x[0], reverse=True) - return scored[0][1] if scored and scored[0][0] > 0 else results[0] - - def _extract_results(self, search_result: dict) -> list: - """Extract results list from search result dict.""" - return search_result.get("results", []) - - def generate(self, query: str, project_name: str = None) -> dict: - """Generate complete design system recommendation.""" - # Step 1: First search product to get category - product_result = search(query, "product", 1) - product_results = product_result.get("results", []) - category = "General" - if product_results: - category = product_results[0].get("Product Type", "General") - - # Step 2: Get reasoning rules for this category - reasoning = self._apply_reasoning(category, {}) - style_priority = reasoning.get("style_priority", []) - - # Step 3: Multi-domain search with style priority hints - search_results = self._multi_domain_search(query, style_priority) - search_results["product"] = product_result # Reuse product search - - # Step 4: Select best matches from each domain using priority - style_results = self._extract_results(search_results.get("style", {})) - color_results = self._extract_results(search_results.get("color", {})) - typography_results = self._extract_results(search_results.get("typography", {})) - landing_results = self._extract_results(search_results.get("landing", {})) - - best_style = self._select_best_match(style_results, reasoning.get("style_priority", [])) - best_color = color_results[0] if color_results else {} - best_typography = typography_results[0] if typography_results else {} - best_landing = landing_results[0] if landing_results else {} - - # Step 5: Build final recommendation - # Combine effects from both reasoning and style search - style_effects = best_style.get("Effects & Animation", "") - reasoning_effects = reasoning.get("key_effects", "") - combined_effects = style_effects if style_effects else reasoning_effects - - return { - "project_name": project_name or query.upper(), - "category": category, - "pattern": { - "name": best_landing.get("Pattern Name", reasoning.get("pattern", "Hero + Features + CTA")), - "sections": best_landing.get("Section Order", "Hero > Features > CTA"), - "cta_placement": best_landing.get("Primary CTA Placement", "Above fold"), - "color_strategy": best_landing.get("Color Strategy", ""), - "conversion": best_landing.get("Conversion Optimization", "") - }, - "style": { - "name": best_style.get("Style Category", "Minimalism"), - "type": best_style.get("Type", "General"), - "effects": style_effects, - "keywords": best_style.get("Keywords", ""), - "best_for": best_style.get("Best For", ""), - "performance": best_style.get("Performance", ""), - "accessibility": best_style.get("Accessibility", "") - }, - "colors": { - "primary": best_color.get("Primary (Hex)", "#2563EB"), - "secondary": best_color.get("Secondary (Hex)", "#3B82F6"), - "cta": best_color.get("CTA (Hex)", "#F97316"), - "background": best_color.get("Background (Hex)", "#F8FAFC"), - "text": best_color.get("Text (Hex)", "#1E293B"), - "notes": best_color.get("Notes", "") - }, - "typography": { - "heading": best_typography.get("Heading Font", "Inter"), - "body": best_typography.get("Body Font", "Inter"), - "mood": best_typography.get("Mood/Style Keywords", reasoning.get("typography_mood", "")), - "best_for": best_typography.get("Best For", ""), - "google_fonts_url": best_typography.get("Google Fonts URL", ""), - "css_import": best_typography.get("CSS Import", "") - }, - "key_effects": combined_effects, - "anti_patterns": reasoning.get("anti_patterns", ""), - "decision_rules": reasoning.get("decision_rules", {}), - "severity": reasoning.get("severity", "MEDIUM") - } - - -# ============ OUTPUT FORMATTERS ============ -BOX_WIDTH = 90 # Wider box for more content - -def format_ascii_box(design_system: dict) -> str: - """Format design system as ASCII box with emojis (MCP-style).""" - project = design_system.get("project_name", "PROJECT") - pattern = design_system.get("pattern", {}) - style = design_system.get("style", {}) - colors = design_system.get("colors", {}) - typography = design_system.get("typography", {}) - effects = design_system.get("key_effects", "") - anti_patterns = design_system.get("anti_patterns", "") - - def wrap_text(text: str, prefix: str, width: int) -> list: - """Wrap long text into multiple lines.""" - if not text: - return [] - words = text.split() - lines = [] - current_line = prefix - for word in words: - if len(current_line) + len(word) + 1 <= width - 2: - current_line += (" " if current_line != prefix else "") + word - else: - if current_line != prefix: - lines.append(current_line) - current_line = prefix + word - if current_line != prefix: - lines.append(current_line) - return lines - - # Build sections from pattern - sections = pattern.get("sections", "").split(">") - sections = [s.strip() for s in sections if s.strip()] - - # Build output lines - lines = [] - w = BOX_WIDTH - 1 - - lines.append("+" + "-" * w + "+") - lines.append(f"| TARGET: {project} - RECOMMENDED DESIGN SYSTEM".ljust(BOX_WIDTH) + "|") - lines.append("+" + "-" * w + "+") - lines.append("|" + " " * BOX_WIDTH + "|") - - # Pattern section - lines.append(f"| PATTERN: {pattern.get('name', '')}".ljust(BOX_WIDTH) + "|") - if pattern.get('conversion'): - lines.append(f"| Conversion: {pattern.get('conversion', '')}".ljust(BOX_WIDTH) + "|") - if pattern.get('cta_placement'): - lines.append(f"| CTA: {pattern.get('cta_placement', '')}".ljust(BOX_WIDTH) + "|") - lines.append("| Sections:".ljust(BOX_WIDTH) + "|") - for i, section in enumerate(sections, 1): - lines.append(f"| {i}. {section}".ljust(BOX_WIDTH) + "|") - lines.append("|" + " " * BOX_WIDTH + "|") - - # Style section - lines.append(f"| STYLE: {style.get('name', '')}".ljust(BOX_WIDTH) + "|") - if style.get("keywords"): - for line in wrap_text(f"Keywords: {style.get('keywords', '')}", "| ", BOX_WIDTH): - lines.append(line.ljust(BOX_WIDTH) + "|") - if style.get("best_for"): - for line in wrap_text(f"Best For: {style.get('best_for', '')}", "| ", BOX_WIDTH): - lines.append(line.ljust(BOX_WIDTH) + "|") - if style.get("performance") or style.get("accessibility"): - perf_a11y = f"Performance: {style.get('performance', '')} | Accessibility: {style.get('accessibility', '')}" - lines.append(f"| {perf_a11y}".ljust(BOX_WIDTH) + "|") - lines.append("|" + " " * BOX_WIDTH + "|") - - # Colors section - lines.append("| COLORS:".ljust(BOX_WIDTH) + "|") - lines.append(f"| Primary: {colors.get('primary', '')}".ljust(BOX_WIDTH) + "|") - lines.append(f"| Secondary: {colors.get('secondary', '')}".ljust(BOX_WIDTH) + "|") - lines.append(f"| CTA: {colors.get('cta', '')}".ljust(BOX_WIDTH) + "|") - lines.append(f"| Background: {colors.get('background', '')}".ljust(BOX_WIDTH) + "|") - lines.append(f"| Text: {colors.get('text', '')}".ljust(BOX_WIDTH) + "|") - if colors.get("notes"): - for line in wrap_text(f"Notes: {colors.get('notes', '')}", "| ", BOX_WIDTH): - lines.append(line.ljust(BOX_WIDTH) + "|") - lines.append("|" + " " * BOX_WIDTH + "|") - - # Typography section - lines.append(f"| TYPOGRAPHY: {typography.get('heading', '')} / {typography.get('body', '')}".ljust(BOX_WIDTH) + "|") - if typography.get("mood"): - for line in wrap_text(f"Mood: {typography.get('mood', '')}", "| ", BOX_WIDTH): - lines.append(line.ljust(BOX_WIDTH) + "|") - if typography.get("best_for"): - for line in wrap_text(f"Best For: {typography.get('best_for', '')}", "| ", BOX_WIDTH): - lines.append(line.ljust(BOX_WIDTH) + "|") - if typography.get("google_fonts_url"): - lines.append(f"| Google Fonts: {typography.get('google_fonts_url', '')}".ljust(BOX_WIDTH) + "|") - if typography.get("css_import"): - lines.append(f"| CSS Import: {typography.get('css_import', '')[:70]}...".ljust(BOX_WIDTH) + "|") - lines.append("|" + " " * BOX_WIDTH + "|") - - # Key Effects section - if effects: - lines.append("| KEY EFFECTS:".ljust(BOX_WIDTH) + "|") - for line in wrap_text(effects, "| ", BOX_WIDTH): - lines.append(line.ljust(BOX_WIDTH) + "|") - lines.append("|" + " " * BOX_WIDTH + "|") - - # Anti-patterns section - if anti_patterns: - lines.append("| AVOID (Anti-patterns):".ljust(BOX_WIDTH) + "|") - for line in wrap_text(anti_patterns, "| ", BOX_WIDTH): - lines.append(line.ljust(BOX_WIDTH) + "|") - lines.append("|" + " " * BOX_WIDTH + "|") - - # Pre-Delivery Checklist section - lines.append("| PRE-DELIVERY CHECKLIST:".ljust(BOX_WIDTH) + "|") - checklist_items = [ - "[ ] No emojis as icons (use SVG: Heroicons/Lucide)", - "[ ] cursor-pointer on all clickable elements", - "[ ] Hover states with smooth transitions (150-300ms)", - "[ ] Light mode: text contrast 4.5:1 minimum", - "[ ] Focus states visible for keyboard nav", - "[ ] prefers-reduced-motion respected", - "[ ] Responsive: 375px, 768px, 1024px, 1440px" - ] - for item in checklist_items: - lines.append(f"| {item}".ljust(BOX_WIDTH) + "|") - lines.append("|" + " " * BOX_WIDTH + "|") - - lines.append("+" + "-" * w + "+") - - return "\n".join(lines) - - -def format_markdown(design_system: dict) -> str: - """Format design system as markdown.""" - project = design_system.get("project_name", "PROJECT") - pattern = design_system.get("pattern", {}) - style = design_system.get("style", {}) - colors = design_system.get("colors", {}) - typography = design_system.get("typography", {}) - effects = design_system.get("key_effects", "") - anti_patterns = design_system.get("anti_patterns", "") - - lines = [] - lines.append(f"## Design System: {project}") - lines.append("") - - # Pattern section - lines.append("### Pattern") - lines.append(f"- **Name:** {pattern.get('name', '')}") - if pattern.get('conversion'): - lines.append(f"- **Conversion Focus:** {pattern.get('conversion', '')}") - if pattern.get('cta_placement'): - lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}") - if pattern.get('color_strategy'): - lines.append(f"- **Color Strategy:** {pattern.get('color_strategy', '')}") - lines.append(f"- **Sections:** {pattern.get('sections', '')}") - lines.append("") - - # Style section - lines.append("### Style") - lines.append(f"- **Name:** {style.get('name', '')}") - if style.get('keywords'): - lines.append(f"- **Keywords:** {style.get('keywords', '')}") - if style.get('best_for'): - lines.append(f"- **Best For:** {style.get('best_for', '')}") - if style.get('performance') or style.get('accessibility'): - lines.append(f"- **Performance:** {style.get('performance', '')} | **Accessibility:** {style.get('accessibility', '')}") - lines.append("") - - # Colors section - lines.append("### Colors") - lines.append(f"| Role | Hex |") - lines.append(f"|------|-----|") - lines.append(f"| Primary | {colors.get('primary', '')} |") - lines.append(f"| Secondary | {colors.get('secondary', '')} |") - lines.append(f"| CTA | {colors.get('cta', '')} |") - lines.append(f"| Background | {colors.get('background', '')} |") - lines.append(f"| Text | {colors.get('text', '')} |") - if colors.get("notes"): - lines.append(f"\n*Notes: {colors.get('notes', '')}*") - lines.append("") - - # Typography section - lines.append("### Typography") - lines.append(f"- **Heading:** {typography.get('heading', '')}") - lines.append(f"- **Body:** {typography.get('body', '')}") - if typography.get("mood"): - lines.append(f"- **Mood:** {typography.get('mood', '')}") - if typography.get("best_for"): - lines.append(f"- **Best For:** {typography.get('best_for', '')}") - if typography.get("google_fonts_url"): - lines.append(f"- **Google Fonts:** {typography.get('google_fonts_url', '')}") - if typography.get("css_import"): - lines.append(f"- **CSS Import:**") - lines.append(f"```css") - lines.append(f"{typography.get('css_import', '')}") - lines.append(f"```") - lines.append("") - - # Key Effects section - if effects: - lines.append("### Key Effects") - lines.append(f"{effects}") - lines.append("") - - # Anti-patterns section - if anti_patterns: - lines.append("### Avoid (Anti-patterns)") - newline_bullet = '\n- ' - lines.append(f"- {anti_patterns.replace(' + ', newline_bullet)}") - lines.append("") - - # Pre-Delivery Checklist section - lines.append("### Pre-Delivery Checklist") - lines.append("- [ ] No emojis as icons (use SVG: Heroicons/Lucide)") - lines.append("- [ ] cursor-pointer on all clickable elements") - lines.append("- [ ] Hover states with smooth transitions (150-300ms)") - lines.append("- [ ] Light mode: text contrast 4.5:1 minimum") - lines.append("- [ ] Focus states visible for keyboard nav") - lines.append("- [ ] prefers-reduced-motion respected") - lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px") - lines.append("") - - return "\n".join(lines) - - -# ============ MAIN ENTRY POINT ============ -def generate_design_system(query: str, project_name: str = None, output_format: str = "ascii", - persist: bool = False, page: str = None, output_dir: str = None) -> str: - """ - Main entry point for design system generation. - - Args: - query: Search query (e.g., "SaaS dashboard", "e-commerce luxury") - project_name: Optional project name for output header - output_format: "ascii" (default) or "markdown" - persist: If True, save design system to design-system/ folder - page: Optional page name for page-specific override file - output_dir: Optional output directory (defaults to current working directory) - - Returns: - Formatted design system string - """ - generator = DesignSystemGenerator() - design_system = generator.generate(query, project_name) - - # Persist to files if requested - if persist: - persist_design_system(design_system, page, output_dir, query) - - if output_format == "markdown": - return format_markdown(design_system) - return format_ascii_box(design_system) - - -# ============ PERSISTENCE FUNCTIONS ============ -def persist_design_system(design_system: dict, page: str = None, output_dir: str = None, page_query: str = None) -> dict: - """ - Persist design system to design-system// folder using Master + Overrides pattern. - - Args: - design_system: The generated design system dictionary - page: Optional page name for page-specific override file - output_dir: Optional output directory (defaults to current working directory) - page_query: Optional query string for intelligent page override generation - - Returns: - dict with created file paths and status - """ - base_dir = Path(output_dir) if output_dir else Path.cwd() - - # Use project name for project-specific folder - project_name = design_system.get("project_name", "default") - project_slug = project_name.lower().replace(' ', '-') - - design_system_dir = base_dir / "design-system" / project_slug - pages_dir = design_system_dir / "pages" - - created_files = [] - - # Create directories - design_system_dir.mkdir(parents=True, exist_ok=True) - pages_dir.mkdir(parents=True, exist_ok=True) - - master_file = design_system_dir / "MASTER.md" - - # Generate and write MASTER.md - master_content = format_master_md(design_system) - with open(master_file, 'w', encoding='utf-8') as f: - f.write(master_content) - created_files.append(str(master_file)) - - # If page is specified, create page override file with intelligent content - if page: - page_file = pages_dir / f"{page.lower().replace(' ', '-')}.md" - page_content = format_page_override_md(design_system, page, page_query) - with open(page_file, 'w', encoding='utf-8') as f: - f.write(page_content) - created_files.append(str(page_file)) - - return { - "status": "success", - "design_system_dir": str(design_system_dir), - "created_files": created_files - } - - -def format_master_md(design_system: dict) -> str: - """Format design system as MASTER.md with hierarchical override logic.""" - project = design_system.get("project_name", "PROJECT") - pattern = design_system.get("pattern", {}) - style = design_system.get("style", {}) - colors = design_system.get("colors", {}) - typography = design_system.get("typography", {}) - effects = design_system.get("key_effects", "") - anti_patterns = design_system.get("anti_patterns", "") - - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - - lines = [] - - # Logic header - lines.append("# Design System Master File") - lines.append("") - lines.append("> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`.") - lines.append("> If that file exists, its rules **override** this Master file.") - lines.append("> If not, strictly follow the rules below.") - lines.append("") - lines.append("---") - lines.append("") - lines.append(f"**Project:** {project}") - lines.append(f"**Generated:** {timestamp}") - lines.append(f"**Category:** {design_system.get('category', 'General')}") - lines.append("") - lines.append("---") - lines.append("") - - # Global Rules section - lines.append("## Global Rules") - lines.append("") - - # Color Palette - lines.append("### Color Palette") - lines.append("") - lines.append("| Role | Hex | CSS Variable |") - lines.append("|------|-----|--------------|") - lines.append(f"| Primary | `{colors.get('primary', '#2563EB')}` | `--color-primary` |") - lines.append(f"| Secondary | `{colors.get('secondary', '#3B82F6')}` | `--color-secondary` |") - lines.append(f"| CTA/Accent | `{colors.get('cta', '#F97316')}` | `--color-cta` |") - lines.append(f"| Background | `{colors.get('background', '#F8FAFC')}` | `--color-background` |") - lines.append(f"| Text | `{colors.get('text', '#1E293B')}` | `--color-text` |") - lines.append("") - if colors.get("notes"): - lines.append(f"**Color Notes:** {colors.get('notes', '')}") - lines.append("") - - # Typography - lines.append("### Typography") - lines.append("") - lines.append(f"- **Heading Font:** {typography.get('heading', 'Inter')}") - lines.append(f"- **Body Font:** {typography.get('body', 'Inter')}") - if typography.get("mood"): - lines.append(f"- **Mood:** {typography.get('mood', '')}") - if typography.get("google_fonts_url"): - lines.append(f"- **Google Fonts:** [{typography.get('heading', '')} + {typography.get('body', '')}]({typography.get('google_fonts_url', '')})") - lines.append("") - if typography.get("css_import"): - lines.append("**CSS Import:**") - lines.append("```css") - lines.append(typography.get("css_import", "")) - lines.append("```") - lines.append("") - - # Spacing Variables - lines.append("### Spacing Variables") - lines.append("") - lines.append("| Token | Value | Usage |") - lines.append("|-------|-------|-------|") - lines.append("| `--space-xs` | `4px` / `0.25rem` | Tight gaps |") - lines.append("| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing |") - lines.append("| `--space-md` | `16px` / `1rem` | Standard padding |") - lines.append("| `--space-lg` | `24px` / `1.5rem` | Section padding |") - lines.append("| `--space-xl` | `32px` / `2rem` | Large gaps |") - lines.append("| `--space-2xl` | `48px` / `3rem` | Section margins |") - lines.append("| `--space-3xl` | `64px` / `4rem` | Hero padding |") - lines.append("") - - # Shadow Depths - lines.append("### Shadow Depths") - lines.append("") - lines.append("| Level | Value | Usage |") - lines.append("|-------|-------|-------|") - lines.append("| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift |") - lines.append("| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons |") - lines.append("| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns |") - lines.append("| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards |") - lines.append("") - - # Component Specs section - lines.append("---") - lines.append("") - lines.append("## Component Specs") - lines.append("") - - # Buttons - lines.append("### Buttons") - lines.append("") - lines.append("```css") - lines.append("/* Primary Button */") - lines.append(".btn-primary {") - lines.append(f" background: {colors.get('cta', '#F97316')};") - lines.append(" color: white;") - lines.append(" padding: 12px 24px;") - lines.append(" border-radius: 8px;") - lines.append(" font-weight: 600;") - lines.append(" transition: all 200ms ease;") - lines.append(" cursor: pointer;") - lines.append("}") - lines.append("") - lines.append(".btn-primary:hover {") - lines.append(" opacity: 0.9;") - lines.append(" transform: translateY(-1px);") - lines.append("}") - lines.append("") - lines.append("/* Secondary Button */") - lines.append(".btn-secondary {") - lines.append(f" background: transparent;") - lines.append(f" color: {colors.get('primary', '#2563EB')};") - lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};") - lines.append(" padding: 12px 24px;") - lines.append(" border-radius: 8px;") - lines.append(" font-weight: 600;") - lines.append(" transition: all 200ms ease;") - lines.append(" cursor: pointer;") - lines.append("}") - lines.append("```") - lines.append("") - - # Cards - lines.append("### Cards") - lines.append("") - lines.append("```css") - lines.append(".card {") - lines.append(f" background: {colors.get('background', '#FFFFFF')};") - lines.append(" border-radius: 12px;") - lines.append(" padding: 24px;") - lines.append(" box-shadow: var(--shadow-md);") - lines.append(" transition: all 200ms ease;") - lines.append(" cursor: pointer;") - lines.append("}") - lines.append("") - lines.append(".card:hover {") - lines.append(" box-shadow: var(--shadow-lg);") - lines.append(" transform: translateY(-2px);") - lines.append("}") - lines.append("```") - lines.append("") - - # Inputs - lines.append("### Inputs") - lines.append("") - lines.append("```css") - lines.append(".input {") - lines.append(" padding: 12px 16px;") - lines.append(" border: 1px solid #E2E8F0;") - lines.append(" border-radius: 8px;") - lines.append(" font-size: 16px;") - lines.append(" transition: border-color 200ms ease;") - lines.append("}") - lines.append("") - lines.append(".input:focus {") - lines.append(f" border-color: {colors.get('primary', '#2563EB')};") - lines.append(" outline: none;") - lines.append(f" box-shadow: 0 0 0 3px {colors.get('primary', '#2563EB')}20;") - lines.append("}") - lines.append("```") - lines.append("") - - # Modals - lines.append("### Modals") - lines.append("") - lines.append("```css") - lines.append(".modal-overlay {") - lines.append(" background: rgba(0, 0, 0, 0.5);") - lines.append(" backdrop-filter: blur(4px);") - lines.append("}") - lines.append("") - lines.append(".modal {") - lines.append(" background: white;") - lines.append(" border-radius: 16px;") - lines.append(" padding: 32px;") - lines.append(" box-shadow: var(--shadow-xl);") - lines.append(" max-width: 500px;") - lines.append(" width: 90%;") - lines.append("}") - lines.append("```") - lines.append("") - - # Style section - lines.append("---") - lines.append("") - lines.append("## Style Guidelines") - lines.append("") - lines.append(f"**Style:** {style.get('name', 'Minimalism')}") - lines.append("") - if style.get("keywords"): - lines.append(f"**Keywords:** {style.get('keywords', '')}") - lines.append("") - if style.get("best_for"): - lines.append(f"**Best For:** {style.get('best_for', '')}") - lines.append("") - if effects: - lines.append(f"**Key Effects:** {effects}") - lines.append("") - - # Layout Pattern - lines.append("### Page Pattern") - lines.append("") - lines.append(f"**Pattern Name:** {pattern.get('name', '')}") - lines.append("") - if pattern.get('conversion'): - lines.append(f"- **Conversion Strategy:** {pattern.get('conversion', '')}") - if pattern.get('cta_placement'): - lines.append(f"- **CTA Placement:** {pattern.get('cta_placement', '')}") - lines.append(f"- **Section Order:** {pattern.get('sections', '')}") - lines.append("") - - # Anti-Patterns section - lines.append("---") - lines.append("") - lines.append("## Anti-Patterns (Do NOT Use)") - lines.append("") - if anti_patterns: - anti_list = [a.strip() for a in anti_patterns.split("+")] - for anti in anti_list: - if anti: - lines.append(f"- ❌ {anti}") - lines.append("") - lines.append("### Additional Forbidden Patterns") - lines.append("") - lines.append("- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons)") - lines.append("- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer") - lines.append("- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout") - lines.append("- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio") - lines.append("- ❌ **Instant state changes** — Always use transitions (150-300ms)") - lines.append("- ❌ **Invisible focus states** — Focus states must be visible for a11y") - lines.append("") - - # Pre-Delivery Checklist - lines.append("---") - lines.append("") - lines.append("## Pre-Delivery Checklist") - lines.append("") - lines.append("Before delivering any UI code, verify:") - lines.append("") - lines.append("- [ ] No emojis used as icons (use SVG instead)") - lines.append("- [ ] All icons from consistent icon set (Heroicons/Lucide)") - lines.append("- [ ] `cursor-pointer` on all clickable elements") - lines.append("- [ ] Hover states with smooth transitions (150-300ms)") - lines.append("- [ ] Light mode: text contrast 4.5:1 minimum") - lines.append("- [ ] Focus states visible for keyboard navigation") - lines.append("- [ ] `prefers-reduced-motion` respected") - lines.append("- [ ] Responsive: 375px, 768px, 1024px, 1440px") - lines.append("- [ ] No content hidden behind fixed navbars") - lines.append("- [ ] No horizontal scroll on mobile") - lines.append("") - - return "\n".join(lines) - - -def format_page_override_md(design_system: dict, page_name: str, page_query: str = None) -> str: - """Format a page-specific override file with intelligent AI-generated content.""" - project = design_system.get("project_name", "PROJECT") - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - page_title = page_name.replace("-", " ").replace("_", " ").title() - - # Detect page type and generate intelligent overrides - page_overrides = _generate_intelligent_overrides(page_name, page_query, design_system) - - lines = [] - - lines.append(f"# {page_title} Page Overrides") - lines.append("") - lines.append(f"> **PROJECT:** {project}") - lines.append(f"> **Generated:** {timestamp}") - lines.append(f"> **Page Type:** {page_overrides.get('page_type', 'General')}") - lines.append("") - lines.append("> ⚠️ **IMPORTANT:** Rules in this file **override** the Master file (`design-system/MASTER.md`).") - lines.append("> Only deviations from the Master are documented here. For all other rules, refer to the Master.") - lines.append("") - lines.append("---") - lines.append("") - - # Page-specific rules with actual content - lines.append("## Page-Specific Rules") - lines.append("") - - # Layout Overrides - lines.append("### Layout Overrides") - lines.append("") - layout = page_overrides.get("layout", {}) - if layout: - for key, value in layout.items(): - lines.append(f"- **{key}:** {value}") - else: - lines.append("- No overrides — use Master layout") - lines.append("") - - # Spacing Overrides - lines.append("### Spacing Overrides") - lines.append("") - spacing = page_overrides.get("spacing", {}) - if spacing: - for key, value in spacing.items(): - lines.append(f"- **{key}:** {value}") - else: - lines.append("- No overrides — use Master spacing") - lines.append("") - - # Typography Overrides - lines.append("### Typography Overrides") - lines.append("") - typography = page_overrides.get("typography", {}) - if typography: - for key, value in typography.items(): - lines.append(f"- **{key}:** {value}") - else: - lines.append("- No overrides — use Master typography") - lines.append("") - - # Color Overrides - lines.append("### Color Overrides") - lines.append("") - colors = page_overrides.get("colors", {}) - if colors: - for key, value in colors.items(): - lines.append(f"- **{key}:** {value}") - else: - lines.append("- No overrides — use Master colors") - lines.append("") - - # Component Overrides - lines.append("### Component Overrides") - lines.append("") - components = page_overrides.get("components", []) - if components: - for comp in components: - lines.append(f"- {comp}") - else: - lines.append("- No overrides — use Master component specs") - lines.append("") - - # Page-Specific Components - lines.append("---") - lines.append("") - lines.append("## Page-Specific Components") - lines.append("") - unique_components = page_overrides.get("unique_components", []) - if unique_components: - for comp in unique_components: - lines.append(f"- {comp}") - else: - lines.append("- No unique components for this page") - lines.append("") - - # Recommendations - lines.append("---") - lines.append("") - lines.append("## Recommendations") - lines.append("") - recommendations = page_overrides.get("recommendations", []) - if recommendations: - for rec in recommendations: - lines.append(f"- {rec}") - lines.append("") - - return "\n".join(lines) - - -def _generate_intelligent_overrides(page_name: str, page_query: str, design_system: dict) -> dict: - """ - Generate intelligent overrides based on page type using layered search. - - Uses the existing search infrastructure to find relevant style, UX, and layout - data instead of hardcoded page types. - """ - from core import search - - page_lower = page_name.lower() - query_lower = (page_query or "").lower() - combined_context = f"{page_lower} {query_lower}" - - # Search across multiple domains for page-specific guidance - style_search = search(combined_context, "style", max_results=1) - ux_search = search(combined_context, "ux", max_results=3) - landing_search = search(combined_context, "landing", max_results=1) - - # Extract results from search response - style_results = style_search.get("results", []) - ux_results = ux_search.get("results", []) - landing_results = landing_search.get("results", []) - - # Detect page type from search results or context - page_type = _detect_page_type(combined_context, style_results) - - # Build overrides from search results - layout = {} - spacing = {} - typography = {} - colors = {} - components = [] - unique_components = [] - recommendations = [] - - # Extract style-based overrides - if style_results: - style = style_results[0] - style_name = style.get("Style Category", "") - keywords = style.get("Keywords", "") - best_for = style.get("Best For", "") - effects = style.get("Effects & Animation", "") - - # Infer layout from style keywords - if any(kw in keywords.lower() for kw in ["data", "dense", "dashboard", "grid"]): - layout["Max Width"] = "1400px or full-width" - layout["Grid"] = "12-column grid for data flexibility" - spacing["Content Density"] = "High — optimize for information display" - elif any(kw in keywords.lower() for kw in ["minimal", "simple", "clean", "single"]): - layout["Max Width"] = "800px (narrow, focused)" - layout["Layout"] = "Single column, centered" - spacing["Content Density"] = "Low — focus on clarity" - else: - layout["Max Width"] = "1200px (standard)" - layout["Layout"] = "Full-width sections, centered content" - - if effects: - recommendations.append(f"Effects: {effects}") - - # Extract UX guidelines as recommendations - for ux in ux_results: - category = ux.get("Category", "") - do_text = ux.get("Do", "") - dont_text = ux.get("Don't", "") - if do_text: - recommendations.append(f"{category}: {do_text}") - if dont_text: - components.append(f"Avoid: {dont_text}") - - # Extract landing pattern info for section structure - if landing_results: - landing = landing_results[0] - sections = landing.get("Section Order", "") - cta_placement = landing.get("Primary CTA Placement", "") - color_strategy = landing.get("Color Strategy", "") - - if sections: - layout["Sections"] = sections - if cta_placement: - recommendations.append(f"CTA Placement: {cta_placement}") - if color_strategy: - colors["Strategy"] = color_strategy - - # Add page-type specific defaults if no search results - if not layout: - layout["Max Width"] = "1200px" - layout["Layout"] = "Responsive grid" - - if not recommendations: - recommendations = [ - "Refer to MASTER.md for all design rules", - "Add specific overrides as needed for this page" - ] - - return { - "page_type": page_type, - "layout": layout, - "spacing": spacing, - "typography": typography, - "colors": colors, - "components": components, - "unique_components": unique_components, - "recommendations": recommendations - } - - -def _detect_page_type(context: str, style_results: list) -> str: - """Detect page type from context and search results.""" - context_lower = context.lower() - - # Check for common page type patterns - page_patterns = [ - (["dashboard", "admin", "analytics", "data", "metrics", "stats", "monitor", "overview"], "Dashboard / Data View"), - (["checkout", "payment", "cart", "purchase", "order", "billing"], "Checkout / Payment"), - (["settings", "profile", "account", "preferences", "config"], "Settings / Profile"), - (["landing", "marketing", "homepage", "hero", "home", "promo"], "Landing / Marketing"), - (["login", "signin", "signup", "register", "auth", "password"], "Authentication"), - (["pricing", "plans", "subscription", "tiers", "packages"], "Pricing / Plans"), - (["blog", "article", "post", "news", "content", "story"], "Blog / Article"), - (["product", "item", "detail", "pdp", "shop", "store"], "Product Detail"), - (["search", "results", "browse", "filter", "catalog", "list"], "Search Results"), - (["empty", "404", "error", "not found", "zero"], "Empty State"), - ] - - for keywords, page_type in page_patterns: - if any(kw in context_lower for kw in keywords): - return page_type - - # Fallback: try to infer from style results - if style_results: - style_name = style_results[0].get("Style Category", "").lower() - best_for = style_results[0].get("Best For", "").lower() - - if "dashboard" in best_for or "data" in best_for: - return "Dashboard / Data View" - elif "landing" in best_for or "marketing" in best_for: - return "Landing / Marketing" - - return "General" - - -# ============ CLI SUPPORT ============ -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description="Generate Design System") - parser.add_argument("query", help="Search query (e.g., 'SaaS dashboard')") - parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name") - parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format") - - args = parser.parse_args() - - result = generate_design_system(args.query, args.project_name, args.format) - print(result) diff --git a/.agent/.agent/skills/ui-ux-pro-max/scripts/search.py b/.agent/.agent/skills/ui-ux-pro-max/scripts/search.py deleted file mode 100644 index 575ea78..0000000 --- a/.agent/.agent/skills/ui-ux-pro-max/scripts/search.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -UI/UX Pro Max Search - BM25 search engine for UI/UX style guides -Usage: python search.py "" [--domain ] [--stack ] [--max-results 3] - python search.py "" --design-system [-p "Project Name"] - python search.py "" --design-system --persist [-p "Project Name"] [--page "dashboard"] - -Domains: style, prompt, color, chart, landing, product, ux, typography -Stacks: html-tailwind, react, nextjs - -Persistence (Master + Overrides pattern): - --persist Save design system to design-system/MASTER.md - --page Also create a page-specific override file in design-system/pages/ -""" - -import argparse -import sys -import io -from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, search, search_stack -from design_system import generate_design_system, persist_design_system - -# Force UTF-8 for stdout/stderr to handle emojis on Windows (cp1252 default) -if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8': - sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') -if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8': - sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') - - -def format_output(result): - """Format results for Claude consumption (token-optimized)""" - if "error" in result: - return f"Error: {result['error']}" - - output = [] - if result.get("stack"): - output.append(f"## UI Pro Max Stack Guidelines") - output.append(f"**Stack:** {result['stack']} | **Query:** {result['query']}") - else: - output.append(f"## UI Pro Max Search Results") - output.append(f"**Domain:** {result['domain']} | **Query:** {result['query']}") - output.append(f"**Source:** {result['file']} | **Found:** {result['count']} results\n") - - for i, row in enumerate(result['results'], 1): - output.append(f"### Result {i}") - for key, value in row.items(): - value_str = str(value) - if len(value_str) > 300: - value_str = value_str[:300] + "..." - output.append(f"- **{key}:** {value_str}") - output.append("") - - return "\n".join(output) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="UI Pro Max Search") - parser.add_argument("query", help="Search query") - parser.add_argument("--domain", "-d", choices=list(CSV_CONFIG.keys()), help="Search domain") - parser.add_argument("--stack", "-s", choices=AVAILABLE_STACKS, help="Stack-specific search (html-tailwind, react, nextjs)") - parser.add_argument("--max-results", "-n", type=int, default=MAX_RESULTS, help="Max results (default: 3)") - parser.add_argument("--json", action="store_true", help="Output as JSON") - # Design system generation - parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation") - parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output") - parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system") - # Persistence (Master + Overrides pattern) - parser.add_argument("--persist", action="store_true", help="Save design system to design-system/MASTER.md (creates hierarchical structure)") - parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/pages/") - parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory)") - - args = parser.parse_args() - - # Design system takes priority - if args.design_system: - result = generate_design_system( - args.query, - args.project_name, - args.format, - persist=args.persist, - page=args.page, - output_dir=args.output_dir - ) - print(result) - - # Print persistence confirmation - if args.persist: - project_slug = args.project_name.lower().replace(' ', '-') if args.project_name else "default" - print("\n" + "=" * 60) - print(f"✅ Design system persisted to design-system/{project_slug}/") - print(f" 📄 design-system/{project_slug}/MASTER.md (Global Source of Truth)") - if args.page: - page_filename = args.page.lower().replace(' ', '-') - print(f" 📄 design-system/{project_slug}/pages/{page_filename}.md (Page Overrides)") - print("") - print(f"📖 Usage: When building a page, check design-system/{project_slug}/pages/[page].md first.") - print(f" If exists, its rules override MASTER.md. Otherwise, use MASTER.md.") - print("=" * 60) - # Stack search - elif args.stack: - result = search_stack(args.query, args.stack, args.max_results) - if args.json: - import json - print(json.dumps(result, indent=2, ensure_ascii=False)) - else: - print(format_output(result)) - # Domain search - else: - result = search(args.query, args.domain, args.max_results) - if args.json: - import json - print(json.dumps(result, indent=2, ensure_ascii=False)) - else: - print(format_output(result)) diff --git a/.agent/scripts/sync_vikunja.js b/.agent/scripts/sync_vikunja.js index 77cf7bf..0cf668d 100644 --- a/.agent/scripts/sync_vikunja.js +++ b/.agent/scripts/sync_vikunja.js @@ -4,21 +4,29 @@ const path = require('path'); // 1. Get arguments const args = process.argv.slice(2); if (args.length < 2) { - console.error("Usage: node sync_vikunja.js "); + console.error("Usage:"); + console.error(" node sync_vikunja.js # Update existing task"); + console.error(" node sync_vikunja.js create \"\" \"<message>\" # Create new task"); process.exit(1); } -const taskId = args[0]; +const commandOrId = args[0]; const message = args[1]; // 2. Load configuration from .env.agent -const envPath = path.join(__dirname, '../config/.env.agent'); -if (!fs.existsSync(envPath)) { - console.error("Error: .agent/config/.env.agent file not found. Please create it from the template."); +const envPath = path.join(__dirname, '../../.env.agent'); +const fallbackEnvPath = path.join(__dirname, '../config/.env.agent'); + +let envContent = ''; +if (fs.existsSync(envPath)) { + envContent = fs.readFileSync(envPath, 'utf8'); +} else if (fs.existsSync(fallbackEnvPath)) { + envContent = fs.readFileSync(fallbackEnvPath, 'utf8'); +} else { + console.error("Error: .env.agent file not found."); process.exit(1); } -const envContent = fs.readFileSync(envPath, 'utf8'); const env = {}; envContent.split('\n').forEach(line => { const match = line.match(/^([^#=]+)="?(.*?)"?$/); @@ -29,6 +37,7 @@ envContent.split('\n').forEach(line => { const apiUrl = env.VIKUNJA_API_URL; const apiToken = env.VIKUNJA_API_TOKEN; +const projectId = env.VIKUNJA_PROJECT_ID || 14; if (!apiUrl || !apiToken || apiUrl.includes('[YOUR_')) { console.error("Error: VIKUNJA_API_URL or VIKUNJA_API_TOKEN is not configured correctly in .env.agent."); @@ -40,52 +49,59 @@ if (env.AGENT_OPERATING_MODE === "TEST") { process.exit(0); } -// 3. Helper to make API calls using native fetch (Node 18+) -async function markTaskDoneAndComment(taskId, message) { +const FETCH_OPTS = { + headers: { + 'Authorization': `Bearer ${apiToken}`, + 'Content-Type': 'application/json' + } +}; + +async function createTaskAndComment(title, message) { try { - console.log(`Connecting to Vikunja API for Task ${taskId}...`); - - // Update task status to done - const patchRes = await fetch(`${apiUrl}/tasks/${taskId}`, { - method: 'POST', // Vikunja uses POST to task endpoint for updates - headers: { - 'Authorization': `Bearer ${apiToken}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ done: true }) - }); - - if (!patchRes.ok) { - throw new Error(`Failed to mark task as done: ${patchRes.statusText}`); - } - - console.log(`✅ Task ${taskId} successfully marked as Done.`); - - // Add comment - const commentRes = await fetch(`${apiUrl}/tasks/${taskId}/comments`, { + console.log(`Creating new task in Project ${projectId}...`); + const createRes = await fetch(`${apiUrl}/projects/${projectId}/tasks`, { method: 'PUT', - headers: { - 'Authorization': `Bearer ${apiToken}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - text: `[Agent Automator] Phase completed.\nReason/Hash: ${message}` + ...FETCH_OPTS, + body: JSON.stringify({ + title: title, + description: message, + done: true }) }); - if (!commentRes.ok) { - console.error(`Warning: Task marked as done, but failed to attach comment: ${commentRes.statusText}`); - } else { - console.log("✅ Comment attached successfully."); - } - - } catch (error) { - console.error("❌ Failed to sync with Vikunja:"); - // Mask the token if it somehow leaks via error message - const secureErr = error.message.replace(new RegExp(apiToken, 'g'), "********"); - console.error(secureErr); + if (!createRes.ok) throw new Error(`Create failed: ${createRes.statusText}`); + const task = await createRes.json(); + console.log(`✅ Task created and marked Done! ID: #${task.id}`); + } catch (e) { + console.error("❌ Failed:", e.message); process.exit(1); } } -markTaskDoneAndComment(taskId, message); +async function markTaskDoneAndComment(taskId, message) { + try { + console.log(`Updating Task ${taskId}...`); + const patchRes = await fetch(`${apiUrl}/tasks/${taskId}`, { + method: 'POST', + ...FETCH_OPTS, + body: JSON.stringify({ done: true }) + }); + + if (!patchRes.ok) throw new Error(`Update failed: ${patchRes.statusText}`); + console.log(`✅ Task ${taskId} marked as Done.`); + + await fetch(`${apiUrl}/tasks/${taskId}/comments`, { + method: 'PUT', ...FETCH_OPTS, body: JSON.stringify({ text: `[Agent Automator] Phase completed.\nReason/Hash: ${message}` }) + }); + console.log("✅ Comment attached."); + } catch (e) { + console.error("❌ Failed:", e.message); + process.exit(1); + } +} + +if (commandOrId === "create") { + createTaskAndComment(message, args[2] || "Task fully completed."); +} else { + markTaskDoneAndComment(commandOrId, message); +} diff --git a/scripts/analysis_raw.txt b/scripts/analysis_raw.txt new file mode 100644 index 0000000..c9bc9f6 --- /dev/null +++ b/scripts/analysis_raw.txt @@ -0,0 +1,58 @@ +0|Gemma4-26B MXFP4_MOE|ngl=999 pure-GPU|63.21|63.78|G0:11770|G1:10411|t=6|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +1|Gemma4-26B MXFP4_MOE|compare: cpu-moe|12.92|14.21|G0:3096|G1:3497|t=6|ub=512 b=2048|kv=q4_0/q4_0|cpu-moe +2|Gemma4-26B MXFP4_MOE|t=2|64.1|64.27|G0:11728|G1:10411|t=2|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +3|Gemma4-26B MXFP4_MOE|t=4|64|64.39|G0:11728|G1:10411|t=4|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +4|Gemma4-26B MXFP4_MOE|t=8|63.75|63.9|G0:11728|G1:10411|t=8|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +5|Gemma4-26B MXFP4_MOE|t=10|64.01|64.14|G0:11728|G1:10411|t=10|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +6|Gemma4-26B MXFP4_MOE|t=12|63.86|63.98|G0:11728|G1:10411|t=12|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +7|Gemma4-26B MXFP4_MOE|ub=256 b=1024|63.8|64.12|G0:10504|G1:9619|t=2|ub=256 b=1024|kv=q4_0/q4_0|pure-GPU +8|Gemma4-26B MXFP4_MOE|ub=256 b=2048|63.88|64.04|G0:10504|G1:9619|t=2|ub=256 b=2048|kv=q4_0/q4_0|pure-GPU +9|Gemma4-26B MXFP4_MOE|ub=512 b=4096|63.91|64.18|G0:11728|G1:10411|t=2|ub=512 b=4096|kv=q4_0/q4_0|pure-GPU +10|Gemma4-26B MXFP4_MOE|ub=1024 b=2048|63.86|64.1|G0:10956|G1:9907|t=2|ub=1024 b=2048|kv=q4_0/q4_0|pure-GPU +11|Gemma4-26B MXFP4_MOE|ub=1024 b=4096|63.85|64.06|G0:10956|G1:9907|t=2|ub=1024 b=4096|kv=q4_0/q4_0|pure-GPU +12|Gemma4-26B MXFP4_MOE|kv=q8_0/q8_0|64.14|64.39|G0:10670|G1:10169|t=2|ub=512 b=2048|kv=q8_0/q8_0|pure-GPU +13|Gemma4-26B MXFP4_MOE|kv=q4_0/q8_0|37.52|37.86|G0:10394|G1:9753|t=2|ub=512 b=2048|kv=q4_0/q8_0|pure-GPU +14|Gemma4-26B MXFP4_MOE|kv=f16/f16|63.48|64.31|G0:11700|G1:11667|t=2|ub=512 b=2048|kv=f16/f16|pure-GPU +15|Gemma4-26B MXFP4_MOE|FINAL|64.05|64.29|G0:10667|G1:10169|t=2|ub=512 b=2048|kv=q8_0/q8_0|pure-GPU +16|Gemma4-26B Q4_K_M|ngl=999 pure-GPU|76.01|76.31|G0:11784|G1:10454|t=6|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +17|Gemma4-26B Q4_K_M|compare: cpu-moe|10.19|10.49|G0:2652|G1:2982|t=6|ub=512 b=2048|kv=q4_0/q4_0|cpu-moe +18|Gemma4-26B Q4_K_M|t=2|75.67|75.87|G0:11783|G1:10454|t=2|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +19|Gemma4-26B Q4_K_M|t=4|75.61|75.87|G0:11783|G1:10454|t=4|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +20|Gemma4-26B Q4_K_M|t=8|75.42|75.59|G0:11783|G1:10454|t=8|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +21|Gemma4-26B Q4_K_M|t=10|75.71|75.82|G0:11783|G1:10454|t=10|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +22|Gemma4-26B Q4_K_M|t=12|75.08|75.7|G0:11783|G1:10454|t=12|ub=512 b=2048|kv=q4_0/q4_0|pure-GPU +23|Gemma4-26B Q4_K_M|ub=256 b=1024|75.16|75.64|G0:10559|G1:9662|t=6|ub=256 b=1024|kv=q4_0/q4_0|pure-GPU +24|Gemma4-26B Q4_K_M|ub=256 b=2048|75.68|76.05|G0:10559|G1:9662|t=6|ub=256 b=2048|kv=q4_0/q4_0|pure-GPU +25|Gemma4-26B Q4_K_M|ub=512 b=4096|75.92|76.16|G0:11784|G1:10454|t=6|ub=512 b=4096|kv=q4_0/q4_0|pure-GPU +26|Gemma4-26B Q4_K_M|ub=1024 b=2048|75.7|75.9|G0:11012|G1:9950|t=6|ub=1024 b=2048|kv=q4_0/q4_0|pure-GPU +27|Gemma4-26B Q4_K_M|ub=1024 b=4096|75.77|75.99|G0:11011|G1:9950|t=6|ub=1024 b=4096|kv=q4_0/q4_0|pure-GPU +28|Gemma4-26B Q4_K_M|kv=q8_0/q8_0|76.3|76.69|G0:10725|G1:10212|t=6|ub=512 b=2048|kv=q8_0/q8_0|pure-GPU +29|Gemma4-26B Q4_K_M|kv=q4_0/q8_0|42.88|44.58|G0:10439|G1:9796|t=6|ub=512 b=2048|kv=q4_0/q8_0|pure-GPU +30|Gemma4-26B Q4_K_M|kv=f16/f16|76.36|76.78|G0:11761|G1:11710|t=6|ub=512 b=2048|kv=f16/f16|pure-GPU +31|Gemma4-26B Q4_K_M|FINAL|76.4|76.75|G0:11761|G1:11710|t=6|ub=512 b=2048|kv=f16/f16|pure-GPU +32|Qwen3.5-35B MXFP4_MOE|n-cpu-moe=5|51.43|52.07|G0:10365|G1:11152|t=6|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +33|Qwen3.5-35B MXFP4_MOE|t=2|43.8|46.4|G0:10365|G1:11152|t=2|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +34|Qwen3.5-35B MXFP4_MOE|t=4|49.21|52.78|G0:10353|G1:11152|t=4|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +35|Qwen3.5-35B MXFP4_MOE|t=8|46.43|50.49|G0:10397|G1:11152|t=8|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +36|Qwen3.5-35B MXFP4_MOE|t=10|46.12|50.06|G0:10351|G1:11152|t=10|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +37|Qwen3.5-35B MXFP4_MOE|t=12|45.23|47.1|G0:10337|G1:11152|t=12|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +38|Qwen3.5-35B MXFP4_MOE|ub=256 b=1024|48.9|52.3|G0:9834|G1:10906|t=6|ub=256 b=1024|kv=q4_0/q4_0|n-cpu-moe=5 +39|Qwen3.5-35B MXFP4_MOE|ub=256 b=2048|49.62|52.52|G0:9833|G1:10906|t=6|ub=256 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +40|Qwen3.5-35B MXFP4_MOE|ub=512 b=4096|48.78|52.14|G0:10337|G1:11152|t=6|ub=512 b=4096|kv=q4_0/q4_0|n-cpu-moe=5 +41|Qwen3.5-35B MXFP4_MOE|ub=1024 b=2048|49.95|52.53|G0:11124|G1:11644|t=6|ub=1024 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +42|Qwen3.5-35B MXFP4_MOE|ub=1024 b=4096|48.75|52.06|G0:11123|G1:11644|t=6|ub=1024 b=4096|kv=q4_0/q4_0|n-cpu-moe=5 +43|Qwen3.5-35B MXFP4_MOE|kv=q4_0/q8_0|42.81|44.14|G0:10681|G1:11472|t=6|ub=512 b=2048|kv=q4_0/q8_0|n-cpu-moe=5 +44|Qwen3.5-35B MXFP4_MOE|FINAL|46.66|47.09|G0:10476|G1:11152|t=6|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +45|Qwen3.5-35B Q4_K_M|n-cpu-moe=5|49.01|53.09|G0:10606|G1:11338|t=6|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +46|Qwen3.5-35B Q4_K_M|t=2|45.73|47.87|G0:10599|G1:11338|t=2|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +47|Qwen3.5-35B Q4_K_M|t=4|50.98|54.33|G0:10601|G1:11338|t=4|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +48|Qwen3.5-35B Q4_K_M|t=8|48.45|52.1|G0:10596|G1:11338|t=8|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +49|Qwen3.5-35B Q4_K_M|t=10|47.83|51.45|G0:10595|G1:11338|t=10|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +50|Qwen3.5-35B Q4_K_M|t=12|43.77|46.79|G0:10589|G1:11338|t=12|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +51|Qwen3.5-35B Q4_K_M|ub=256 b=1024|52.14|53.82|G0:10089|G1:11092|t=4|ub=256 b=1024|kv=q4_0/q4_0|n-cpu-moe=5 +52|Qwen3.5-35B Q4_K_M|ub=256 b=2048|50.23|53.66|G0:10091|G1:11092|t=4|ub=256 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +53|Qwen3.5-35B Q4_K_M|ub=512 b=2048|49.89|53.89|G0:10595|G1:11338|t=4|ub=512 b=2048|kv=q4_0/q4_0|n-cpu-moe=5 +54|Qwen3.5-35B Q4_K_M|ub=512 b=4096|50.4|54.19|G0:10564|G1:11338|t=4|ub=512 b=4096|kv=q4_0/q4_0|n-cpu-moe=5 +55|Qwen3.5-35B Q4_K_M|kv=q8_0/q8_0|51.84|53.53|G0:10726|G1:11732|t=4|ub=256 b=1024|kv=q8_0/q8_0|n-cpu-moe=5 +56|Qwen3.5-35B Q4_K_M|kv=q4_0/q8_0|43.22|45.99|G0:10410|G1:11412|t=4|ub=256 b=1024|kv=q4_0/q8_0|n-cpu-moe=5 +57|Qwen3.5-35B Q4_K_M|FINAL|52.05|54.48|G0:10062|G1:11092|t=4|ub=256 b=1024|kv=q4_0/q4_0|n-cpu-moe=5 \ No newline at end of file diff --git a/scripts/auto_tune_122b.py b/scripts/auto_tune_122b.py new file mode 100644 index 0000000..2652d53 --- /dev/null +++ b/scripts/auto_tune_122b.py @@ -0,0 +1,372 @@ +""" +Qwen3.5 122B-A10B 자동 정밀 튜닝 스크립트 +=========================================== +각 설정 조합으로 서버를 재시작하고 벤치마크를 자동 수행합니다. +서버 로그에서 순수 eval time (t/s)를 파싱하여 정확한 비교 테이블을 출력합니다. + +예상 소요 시간: 약 30-40분 (5개 설정 × ~6-7분/설정) +""" +import subprocess +import time +import json +import urllib.request +import os +import re +import sys +import datetime + +try: + sys.stdout.reconfigure(encoding='utf-8') +except AttributeError: + pass + +BASE_URL = "http://127.0.0.1:8000" +MODEL_PATH = r"models\Q4_K_M\Qwen3.5-122B-A10B-Q4_K_M-00001-of-00003.gguf" +SERVER_EXE = r"llama_bin_run\llama-server.exe" + +# ============================================================ +# 테스트할 설정 목록 +# ============================================================ +# 공통 파라미터 (변경하지 않는 것들) +COMMON_ARGS = [ + "--model", MODEL_PATH, + "-ngl", "999", + "--cpu-moe", + "-c", "2048", + "-np", "1", + "-fa", "on", + "--cache-type-k", "q4_0", + "--cache-type-v", "q4_0", + "-ub", "256", + "-b", "1024", + "--mlock", + "--port", "8000", + "--host", "0.0.0.0", + "--no-warmup", # 워밍업은 벤치마크 스크립트에서 직접 수행 +] + +# 변수 파라미터 조합 +CONFIGS = [ + { + "name": "A) --no-mmap -t 8", + "desc": "서버 권장: mmap 비활성화 (baseline 대비)", + "extra": ["--no-mmap", "-t", "8", "--prio", "2"], + }, + { + "name": "B) --no-mmap -t 6", + "desc": "스레드 감소 (캐시 경합 회피)", + "extra": ["--no-mmap", "-t", "6", "--prio", "2"], + }, + { + "name": "C) --no-mmap -t 10", + "desc": "스레드 증가 (RAM 대역폭 포화)", + "extra": ["--no-mmap", "-t", "10", "--prio", "2"], + }, + { + "name": "D) --no-mmap -t 12", + "desc": "더 많은 스레드", + "extra": ["--no-mmap", "-t", "12", "--prio", "2"], + }, + { + "name": "E) --no-mmap -t 10 --prio 3 --poll 100", + "desc": "최적 스레드 + 리얼타임 우선순위 + 폴링", + "extra": ["--no-mmap", "-t", "10", "--prio", "3", "--poll", "100"], + }, +] + +# ============================================================ +# 유틸리티 함수 +# ============================================================ + +def kill_server(): + """llama-server 프로세스 강제 종료""" + os.system("taskkill /F /IM llama-server.exe >nul 2>&1") + time.sleep(3) + +def start_server(config, log_path): + """서버 시작, 로그를 파일로 리다이렉트""" + cmd = [SERVER_EXE] + COMMON_ARGS + config["extra"] + log_file = open(log_path, "w", encoding="utf-8") + proc = subprocess.Popen( + cmd, + stdout=log_file, + stderr=subprocess.STDOUT, + cwd=os.getcwd() + ) + return proc, log_file + +def wait_for_server(timeout=600): + """서버가 준비될 때까지 대기""" + start = time.time() + while time.time() - start < timeout: + try: + req = urllib.request.Request(f"{BASE_URL}/health") + with urllib.request.urlopen(req, timeout=5) as resp: + data = json.loads(resp.read()) + if data.get("status") == "ok": + return True + except: + pass + time.sleep(5) + return False + +def run_single_benchmark(prompt, max_tokens=200): + """단일 벤치마크 실행""" + payload = json.dumps({ + "model": "local-model", + "messages": [{"role": "user", "content": prompt}], + "max_tokens": max_tokens, + "temperature": 0.0 + }).encode("utf-8") + + req = urllib.request.Request( + f"{BASE_URL}/v1/chat/completions", + data=payload, + headers={"Content-Type": "application/json"} + ) + + start = time.time() + with urllib.request.urlopen(req, timeout=600) as resp: + result = json.loads(resp.read()) + elapsed = time.time() - start + + usage = result.get("usage", {}) + completion_tokens = usage.get("completion_tokens", 0) + return completion_tokens, elapsed + +def parse_eval_times(log_path): + """서버 로그에서 순수 eval time 파싱""" + try: + with open(log_path, "r", encoding="utf-8", errors="ignore") as f: + content = f.read() + except: + return [] + + # "eval time = XXXXX.XX ms / NNN tokens (XXX.XX ms per token, XX.XX tokens per second)" + pattern = r'^\s+eval time\s*=\s*([\d.]+)\s*ms\s*/\s*(\d+)\s*tokens\s*\(\s*([\d.]+)\s*ms per token,\s*([\d.]+)\s*tokens per second\)' + matches = re.findall(pattern, content, re.MULTILINE) + + results = [] + for m in matches: + results.append({ + "total_ms": float(m[0]), + "tokens": int(m[1]), + "ms_per_token": float(m[2]), + "tps": float(m[3]) + }) + return results + +def parse_prompt_eval_times(log_path): + """서버 로그에서 prompt eval time 파싱""" + try: + with open(log_path, "r", encoding="utf-8", errors="ignore") as f: + content = f.read() + except: + return [] + + pattern = r'prompt eval time\s*=\s*([\d.]+)\s*ms\s*/\s*(\d+)\s*tokens\s*\(\s*([\d.]+)\s*ms per token,\s*([\d.]+)\s*tokens per second\)' + matches = re.findall(pattern, content, re.MULTILINE) + + results = [] + for m in matches: + results.append({ + "total_ms": float(m[0]), + "tokens": int(m[1]), + "ms_per_token": float(m[2]), + "tps": float(m[3]) + }) + return results + +def parse_vram_usage(log_path): + """서버 로그에서 CUDA0 모델 버퍼 크기 파싱""" + try: + with open(log_path, "r", encoding="utf-8", errors="ignore") as f: + content = f.read() + except: + return "N/A" + + match = re.search(r'CUDA0 model buffer size\s*=\s*([\d.]+)\s*MiB', content) + if match: + return f"{float(match.group(1)):.0f} MiB" + return "N/A" + +# ============================================================ +# 메인 튜닝 루프 +# ============================================================ + +def main(): + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + + print("=" * 70) + print(" Qwen3.5 122B-A10B 자동 정밀 튜닝") + print(f" 시작 시간: {datetime.datetime.now().strftime('%H:%M:%S')}") + print(f" 테스트 설정: {len(CONFIGS)}개") + print(f" 예상 소요: ~{len(CONFIGS) * 7}분") + print("=" * 70) + print() + print(" 기존 Baseline: mmap on, -t 8, --prio 2 → 10.06 t/s (eval)") + print() + + # 결과 저장 + all_results = [] + + for idx, config in enumerate(CONFIGS): + config_start = time.time() + log_path = os.path.join(os.getcwd(), f"tune_log_{idx}.txt") + + print(f"\n{'='*70}") + print(f" [{idx+1}/{len(CONFIGS)}] {config['name']}") + print(f" {config['desc']}") + print(f" 시작: {datetime.datetime.now().strftime('%H:%M:%S')}") + print(f"{'='*70}") + + # 1. 기존 서버 종료 + print(" [1/4] 서버 종료 중...") + kill_server() + + # 2. 새 서버 시작 + print(f" [2/4] 서버 시작 중... (모델 로딩 ~3-5분)") + proc, log_file = start_server(config, log_path) + + # 3. 서버 준비 대기 + if not wait_for_server(timeout=600): + print(" ❌ 서버 시작 실패! 다음 설정으로 넘어갑니다.") + kill_server() + log_file.close() + all_results.append({ + "config": config["name"], + "status": "FAILED", + "eval_tps": [], + "prompt_tps": [], + "vram": "N/A" + }) + continue + + load_time = time.time() - config_start + print(f" [3/4] 서버 준비 완료! (로딩 {load_time:.0f}초)") + + # 4. 벤치마크 실행 (워밍업 1회 + 본 테스트 3회) + print(" [4/4] 벤치마크 실행 중...") + + # 워밍업 + try: + run_single_benchmark("Say hello.", max_tokens=20) + print(" 워밍업 완료") + except Exception as e: + print(f" 워밍업 실패: {e}") + + # 본 테스트 3회 + prompts = [ + "Write a detailed explanation of how neural networks learn through backpropagation and gradient descent.", + "Explain the complete process of photosynthesis including light and dark reactions in detail.", + "Describe the differences between SQL and NoSQL databases with examples and performance characteristics.", + ] + + for i, prompt in enumerate(prompts): + try: + tokens, elapsed = run_single_benchmark(prompt, max_tokens=200) + approx_tps = tokens / elapsed if elapsed > 0 else 0 + print(f" Run {i+1}/3: {tokens} tokens, {elapsed:.1f}s, ~{approx_tps:.2f} t/s (approx)") + except Exception as e: + print(f" Run {i+1}/3: ERROR - {e}") + + # 서버 종료 전에 로그 플러시를 위해 잠시 대기 + time.sleep(2) + + # 서버 종료 + kill_server() + log_file.close() + time.sleep(2) + + # 로그 파싱 + eval_times = parse_eval_times(log_path) + prompt_times = parse_prompt_eval_times(log_path) + vram = parse_vram_usage(log_path) + + # 워밍업 제외 (첫 번째 결과) + if len(eval_times) > 1: + bench_evals = eval_times[1:] # 워밍업 제외 + else: + bench_evals = eval_times + + if len(prompt_times) > 1: + bench_prompts = prompt_times[1:] + else: + bench_prompts = prompt_times + + eval_speeds = [e["tps"] for e in bench_evals] + prompt_speeds = [p["tps"] for p in bench_prompts] + + result = { + "config": config["name"], + "status": "OK", + "eval_tps": eval_speeds, + "prompt_tps": prompt_speeds, + "vram": vram, + } + all_results.append(result) + + config_elapsed = time.time() - config_start + print(f"\n 완료! 소요: {config_elapsed:.0f}초") + + if eval_speeds: + avg_eval = sum(eval_speeds) / len(eval_speeds) + max_eval = max(eval_speeds) + print(f" 📊 Eval TPS: avg={avg_eval:.2f}, max={max_eval:.2f}") + + # ============================================================ + # 최종 결과 비교 테이블 + # ============================================================ + print("\n") + print("=" * 80) + print(" 🏆 최종 결과 비교 테이블") + print("=" * 80) + print() + + # 기존 baseline 추가 + print(f" {'설정':<45} {'Eval t/s':>10} {'최대':>8} {'Prompt t/s':>12} {'VRAM':>12}") + print(f" {'-'*45} {'-'*10} {'-'*8} {'-'*12} {'-'*12}") + + # Baseline (이전 결과) + print(f" {'[기준] mmap on, -t 8, --prio 2':<45} {'10.02':>10} {'10.06':>8} {'29.52':>12} {'5392 MiB':>12}") + + best_avg = 0 + best_config = "" + + for r in all_results: + if r["status"] != "OK" or not r["eval_tps"]: + print(f" {r['config']:<45} {'FAILED':>10} {'':>8} {'':>12} {r['vram']:>12}") + continue + + avg_e = sum(r["eval_tps"]) / len(r["eval_tps"]) + max_e = max(r["eval_tps"]) + avg_p = sum(r["prompt_tps"]) / len(r["prompt_tps"]) if r["prompt_tps"] else 0 + + if avg_e > best_avg: + best_avg = avg_e + best_config = r["config"] + + marker = " ⭐" if avg_e > 10.06 else "" + print(f" {r['config']:<45} {avg_e:>10.2f} {max_e:>8.2f} {avg_p:>12.2f} {r['vram']:>12}{marker}") + + print() + if best_avg > 0: + improvement = ((best_avg - 10.02) / 10.02) * 100 + print(f" 🏆 최고 성능: {best_config}") + print(f" → {best_avg:.2f} t/s (기준 10.02 t/s 대비 {improvement:+.1f}%)") + + print() + print(f" 완료 시간: {datetime.datetime.now().strftime('%H:%M:%S')}") + print("=" * 80) + + # 결과를 파일로도 저장 + result_path = os.path.join(os.getcwd(), f"tune_results_{timestamp}.txt") + with open(result_path, "w", encoding="utf-8") as f: + f.write("Qwen3.5 122B-A10B Fine Tuning Results\n") + f.write(f"Date: {timestamp}\n\n") + for r in all_results: + f.write(f"{r['config']}: {r['eval_tps']} (VRAM: {r['vram']})\n") + print(f" 결과 저장: {result_path}") + +if __name__ == "__main__": + main() diff --git a/scripts/auto_tune_122b_r2.py b/scripts/auto_tune_122b_r2.py new file mode 100644 index 0000000..f603ed7 --- /dev/null +++ b/scripts/auto_tune_122b_r2.py @@ -0,0 +1,257 @@ +""" +Qwen3.5 122B-A10B 정밀 튜닝 2라운드 +==================================== +1라운드 결과: mmap on이 더 빠르고, 스레드 수가 적을수록 빠름 +→ mmap on 상태에서 스레드 수 4~8 범위를 정밀 탐색 +""" +import subprocess +import time +import json +import urllib.request +import os +import re +import sys +import datetime + +try: + sys.stdout.reconfigure(encoding='utf-8') +except AttributeError: + pass + +BASE_URL = "http://127.0.0.1:8000" +MODEL_PATH = r"models\Q4_K_M\Qwen3.5-122B-A10B-Q4_K_M-00001-of-00003.gguf" +SERVER_EXE = r"llama_bin_run\llama-server.exe" + +COMMON_ARGS = [ + "--model", MODEL_PATH, + "-ngl", "999", + "--cpu-moe", + "-c", "2048", + "-np", "1", + "-fa", "on", + "--cache-type-k", "q4_0", + "--cache-type-v", "q4_0", + "-ub", "256", + "-b", "1024", + "--mlock", + "--port", "8000", + "--host", "0.0.0.0", + "--no-warmup", +] + +CONFIGS = [ + { + "name": "F) mmap on, -t 4", + "desc": "최소 스레드 (4개, 물리코어 절반)", + "extra": ["-t", "4", "--prio", "2"], + }, + { + "name": "G) mmap on, -t 5", + "desc": "스레드 5개", + "extra": ["-t", "5", "--prio", "2"], + }, + { + "name": "H) mmap on, -t 6", + "desc": "스레드 6개 (--no-mmap에서 최고였음)", + "extra": ["-t", "6", "--prio", "2"], + }, + { + "name": "I) mmap on, -t 7", + "desc": "스레드 7개", + "extra": ["-t", "7", "--prio", "2"], + }, + { + "name": "J) mmap on, -t 6, --prio 3", + "desc": "최적 스레드 + 리얼타임 우선순위", + "extra": ["-t", "6", "--prio", "3"], + }, +] + +def kill_server(): + os.system("taskkill /F /IM llama-server.exe >nul 2>&1") + time.sleep(3) + +def start_server(config, log_path): + cmd = [SERVER_EXE] + COMMON_ARGS + config["extra"] + log_file = open(log_path, "w", encoding="utf-8") + proc = subprocess.Popen(cmd, stdout=log_file, stderr=subprocess.STDOUT, cwd=os.getcwd()) + return proc, log_file + +def wait_for_server(timeout=600): + start = time.time() + while time.time() - start < timeout: + try: + req = urllib.request.Request(f"{BASE_URL}/health") + with urllib.request.urlopen(req, timeout=5) as resp: + data = json.loads(resp.read()) + if data.get("status") == "ok": + return True + except: + pass + time.sleep(5) + return False + +def run_single_benchmark(prompt, max_tokens=200): + payload = json.dumps({ + "model": "local-model", + "messages": [{"role": "user", "content": prompt}], + "max_tokens": max_tokens, + "temperature": 0.0 + }).encode("utf-8") + req = urllib.request.Request( + f"{BASE_URL}/v1/chat/completions", + data=payload, + headers={"Content-Type": "application/json"} + ) + start = time.time() + with urllib.request.urlopen(req, timeout=600) as resp: + result = json.loads(resp.read()) + elapsed = time.time() - start + usage = result.get("usage", {}) + return usage.get("completion_tokens", 0), elapsed + +def parse_eval_times(log_path): + try: + with open(log_path, "r", encoding="utf-8", errors="ignore") as f: + content = f.read() + except: + return [] + pattern = r'^\s+eval time\s*=\s*([\d.]+)\s*ms\s*/\s*(\d+)\s*tokens\s*\(\s*([\d.]+)\s*ms per token,\s*([\d.]+)\s*tokens per second\)' + matches = re.findall(pattern, content, re.MULTILINE) + return [{"tps": float(m[3]), "tokens": int(m[1])} for m in matches] + +def parse_prompt_eval_times(log_path): + try: + with open(log_path, "r", encoding="utf-8", errors="ignore") as f: + content = f.read() + except: + return [] + pattern = r'prompt eval time\s*=\s*([\d.]+)\s*ms\s*/\s*(\d+)\s*tokens\s*\(\s*([\d.]+)\s*ms per token,\s*([\d.]+)\s*tokens per second\)' + matches = re.findall(pattern, content, re.MULTILINE) + return [{"tps": float(m[3])} for m in matches] + +def main(): + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + + print("=" * 70) + print(" Qwen3.5 122B-A10B 정밀 튜닝 - 2라운드") + print(f" 시작: {datetime.datetime.now().strftime('%H:%M:%S')}") + print(f" 테스트: {len(CONFIGS)}개 설정 (mmap on + 스레드 4~7 정밀 탐색)") + print("=" * 70) + print() + + all_results = [] + + for idx, config in enumerate(CONFIGS): + config_start = time.time() + log_path = os.path.join(os.getcwd(), f"tune_r2_log_{idx}.txt") + + print(f"\n{'='*70}") + print(f" [{idx+1}/{len(CONFIGS)}] {config['name']}") + print(f" {config['desc']}") + print(f" 시작: {datetime.datetime.now().strftime('%H:%M:%S')}") + print(f"{'='*70}") + + kill_server() + print(f" [1/3] 서버 시작 중...") + proc, log_file = start_server(config, log_path) + + if not wait_for_server(timeout=600): + print(" ❌ 서버 시작 실패!") + kill_server() + log_file.close() + all_results.append({"config": config["name"], "status": "FAILED", "eval_tps": []}) + continue + + load_time = time.time() - config_start + print(f" [2/3] 서버 준비 완료! ({load_time:.0f}초)") + + # 워밍업 + 벤치마크 + try: + run_single_benchmark("Say hello.", max_tokens=20) + except: + pass + + print(" [3/3] 벤치마크 3회...") + prompts = [ + "Write a detailed explanation of how neural networks learn through backpropagation.", + "Explain the complete process of photosynthesis including light and dark reactions.", + "Describe the differences between SQL and NoSQL databases with examples.", + ] + for i, prompt in enumerate(prompts): + try: + tokens, elapsed = run_single_benchmark(prompt, max_tokens=200) + print(f" Run {i+1}: {tokens}tok, {elapsed:.1f}s, ~{tokens/elapsed:.2f} t/s") + except Exception as e: + print(f" Run {i+1}: ERROR - {e}") + + time.sleep(2) + kill_server() + log_file.close() + time.sleep(2) + + eval_times = parse_eval_times(log_path) + prompt_times = parse_prompt_eval_times(log_path) + bench_evals = eval_times[1:] if len(eval_times) > 1 else eval_times + bench_prompts = prompt_times[1:] if len(prompt_times) > 1 else prompt_times + + eval_speeds = [e["tps"] for e in bench_evals] + prompt_speeds = [p["tps"] for p in bench_prompts] + + all_results.append({ + "config": config["name"], + "status": "OK", + "eval_tps": eval_speeds, + "prompt_tps": prompt_speeds, + }) + + if eval_speeds: + print(f" 📊 Eval: avg={sum(eval_speeds)/len(eval_speeds):.2f}, max={max(eval_speeds):.2f}") + + # 최종 결과 + print("\n") + print("=" * 85) + print(" 🏆 전체 튜닝 결과 (1라운드 + 2라운드 통합)") + print("=" * 85) + print() + print(f" {'설정':<48} {'Avg t/s':>8} {'Max t/s':>8} {'Prompt':>8}") + print(f" {'-'*48} {'-'*8} {'-'*8} {'-'*8}") + + # 1라운드 결과 (하드코딩) + r1 = [ + ("[기준] mmap on, -t 8, --prio 2", 10.02, 10.06, 29.52), + ("A) --no-mmap -t 8", 9.66, 9.70, 28.26), + ("B) --no-mmap -t 6", 10.02, 10.18, 26.73), + ("C) --no-mmap -t 10", 9.42, 9.46, 27.31), + ("D) --no-mmap -t 12", 9.04, 9.11, 27.92), + ("E) --no-mmap -t 10 --prio 3 --poll 100", 9.41, 9.45, 28.37), + ] + for name, avg, mx, pp in r1: + marker = " ⭐" if avg >= 10.0 else "" + print(f" {name:<48} {avg:>8.2f} {mx:>8.2f} {pp:>8.2f}{marker}") + + print(f" {'--- 2라운드 ---':<48}") + + best_avg = 10.06 # 기존 최고 + best_config = "[기준] mmap on, -t 8" + + for r in all_results: + if r["status"] != "OK" or not r["eval_tps"]: + print(f" {r['config']:<48} {'FAIL':>8}") + continue + avg_e = sum(r["eval_tps"]) / len(r["eval_tps"]) + max_e = max(r["eval_tps"]) + avg_p = sum(r["prompt_tps"]) / len(r["prompt_tps"]) if r["prompt_tps"] else 0 + if max_e > best_avg: + best_avg = max_e + best_config = r["config"] + marker = " ⭐" if avg_e >= 10.0 else "" + print(f" {r['config']:<48} {avg_e:>8.2f} {max_e:>8.2f} {avg_p:>8.2f}{marker}") + + print() + print(f" 🏆 최고 성능: {best_config} → {best_avg:.2f} t/s") + print(f" 완료: {datetime.datetime.now().strftime('%H:%M:%S')}") + print("=" * 85) + +if __name__ == "__main__": + main() diff --git a/scripts/auto_tune_gemma4_256k.py b/scripts/auto_tune_gemma4_256k.py new file mode 100644 index 0000000..80d20fa --- /dev/null +++ b/scripts/auto_tune_gemma4_256k.py @@ -0,0 +1,339 @@ +""" +Gemma4 26B-A4B Comprehensive Auto-Tuner | 256K Context | RTX 3060 12GB +Phase 1: -ngl sweep (GPU layers) +Phase 2: -t / -tb sweep (CPU threads) +Phase 3: -ub / -b sweep (batch sizes) +Phase 4: --cache-type-k/v sweep (KV cache precision) +Phase 5: --no-mmap, --poll, --prio sweep (misc) +Each phase fixes the best from previous phases. +""" +import subprocess +import time +import json +import urllib.request +import sys +import os +import itertools + +try: + sys.stdout.reconfigure(encoding='utf-8') +except AttributeError: + pass + +BASE_URL = "http://127.0.0.1:8000" +LLAMA_SERVER = r"llama_bin_run\llama-server.exe" +MODEL = r"models\gemma-4-26B-A4B-it-Q4_K_M.gguf" +CONTEXT = 262144 +BENCHMARK_RUNS = 3 +BENCHMARK_TOKENS = 200 + +# ─── Baseline (from previous tuning at -c 4096) ─── +BEST = { + "ngl": 22, + "t": 8, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": True, + "mmap": True, + "prio": 2, + "poll": 50, +} + +ALL_RESULTS = [] + + +def kill_server(): + subprocess.run(["taskkill", "/F", "/IM", "llama-server.exe"], + capture_output=True) + time.sleep(4) + + +def build_cmd(cfg): + cmd = [LLAMA_SERVER, "--model", MODEL, + "-ngl", str(cfg["ngl"]), + "-c", str(CONTEXT), + "-np", "1", + "-fa", cfg["fa"], + "--cache-type-k", cfg["ctk"], + "--cache-type-v", cfg["ctv"], + "-ub", str(cfg["ub"]), + "-b", str(cfg["b"]), + "-t", str(cfg["t"]), + "-tb", str(cfg["tb"]), + "--prio", str(cfg["prio"]), + "--poll", str(cfg["poll"]), + "--port", "8000", + "--host", "0.0.0.0"] + if cfg["mlock"]: + cmd.append("--mlock") + if not cfg["mmap"]: + cmd.append("--no-mmap") + return cmd + + +def start_server(cfg): + cmd = build_cmd(cfg) + proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + cwd=os.getcwd(), text=True, encoding='utf-8', errors='replace' + ) + return proc + + +def wait_for_server(timeout=180): + start = time.time() + while time.time() - start < timeout: + try: + req = urllib.request.Request(f"{BASE_URL}/health") + with urllib.request.urlopen(req, timeout=3) as resp: + data = json.loads(resp.read()) + if data.get("status") == "ok": + return True + except: + pass + time.sleep(2) + return False + + +def run_benchmark(max_tokens=BENCHMARK_TOKENS): + payload = json.dumps({ + "model": "local-model", + "messages": [{"role": "user", "content": "Count from 1 to 50, writing each number on a new line."}], + "max_tokens": max_tokens, + "temperature": 0.0 + }).encode("utf-8") + + req = urllib.request.Request( + f"{BASE_URL}/v1/chat/completions", + data=payload, + headers={"Content-Type": "application/json"} + ) + + start = time.time() + with urllib.request.urlopen(req, timeout=300) as resp: + result = json.loads(resp.read()) + elapsed = time.time() - start + + usage = result.get("usage", {}) + ct = usage.get("completion_tokens", 0) + return ct / elapsed if elapsed > 0 else 0 + + +def get_vram(): + try: + r = subprocess.run( + ["nvidia-smi", "--query-gpu=memory.used,memory.total", + "--format=csv,noheader,nounits"], + capture_output=True, text=True, timeout=5 + ) + parts = r.stdout.strip().split(",") + return int(parts[0].strip()), int(parts[1].strip()) + except: + return 0, 0 + + +def test_config(cfg, label=""): + kill_server() + desc = label or str(cfg) + print(f" [{desc}] Starting server...") + proc = start_server(cfg) + + if not wait_for_server(): + print(f" [{desc}] FAILED to start") + proc.kill() + return None + + vram_used, vram_total = get_vram() + print(f" [{desc}] VRAM: {vram_used}/{vram_total} MiB | ", end="", flush=True) + + # Warmup + try: + run_benchmark(max_tokens=20) + except: + pass + + # Benchmark + speeds = [] + for i in range(BENCHMARK_RUNS): + try: + tps = run_benchmark() + speeds.append(tps) + except Exception as e: + print(f"ERR({e}) ", end="", flush=True) + + proc.kill() + + if not speeds: + print("ALL FAILED") + return None + + avg = sum(speeds) / len(speeds) + best = max(speeds) + print(f"AVG: {avg:.2f} t/s | BEST: {best:.2f} t/s") + + result = {**cfg, "avg_tps": avg, "best_tps": best, + "vram_used": vram_used, "vram_total": vram_total, "label": label} + ALL_RESULTS.append(result) + return result + + +def phase_sweep(phase_name, param_name, values, base_cfg): + print(f"\n{'='*70}") + print(f" PHASE: {phase_name}") + print(f" Sweeping: {param_name} = {values}") + print(f"{'='*70}") + + best_result = None + for val in values: + cfg = {**base_cfg} + if isinstance(param_name, list): + for p, v in zip(param_name, val): + cfg[p] = v + label = " | ".join(f"{p}={v}" for p, v in zip(param_name, val)) + else: + cfg[param_name] = val + label = f"{param_name}={val}" + + r = test_config(cfg, label) + if r and (best_result is None or r["avg_tps"] > best_result["avg_tps"]): + best_result = r + + if best_result: + print(f"\n ★ Phase winner: {best_result['label']} → {best_result['avg_tps']:.2f} t/s") + return best_result + + +def main(): + print("=" * 70) + print(" Gemma4 26B-A4B COMPREHENSIVE Auto-Tuner") + print(" 256K Context | RTX 3060 12GB") + print("=" * 70) + print() + + cfg = dict(BEST) + + # ─── Phase 1: -ngl (already done, quick verify top 3) ─── + r = phase_sweep("GPU Layers (-ngl)", "ngl", [22, 21, 20], cfg) + if r: + cfg["ngl"] = r["ngl"] + + # ─── Phase 2: CPU threads (-t, -tb) ─── + thread_combos = [ + (2, 2), (4, 4), (4, 8), (6, 6), (6, 8), + (8, 8), (8, 12), (10, 10), (12, 12), (16, 16) + ] + r = phase_sweep("CPU Threads (-t, -tb)", ["t", "tb"], thread_combos, cfg) + if r: + cfg["t"] = r["t"] + cfg["tb"] = r["tb"] + + # ─── Phase 3: Batch sizes (-ub, -b) ─── + batch_combos = [ + (128, 512), (256, 1024), (256, 2048), + (512, 1024), (512, 2048), (512, 4096), + (1024, 2048), (1024, 4096) + ] + r = phase_sweep("Batch Sizes (-ub, -b)", ["ub", "b"], batch_combos, cfg) + if r: + cfg["ub"] = r["ub"] + cfg["b"] = r["b"] + + # ─── Phase 4: KV cache precision ─── + kv_combos = [ + ("q4_0", "q4_0"), + ("q8_0", "q8_0"), + ("q4_0", "q8_0"), + ("f16", "f16"), + ] + r = phase_sweep("KV Cache Type (-ctk, -ctv)", ["ctk", "ctv"], kv_combos, cfg) + if r: + cfg["ctk"] = r["ctk"] + cfg["ctv"] = r["ctv"] + + # ─── Phase 5: Misc (mmap, poll, prio) ─── + misc_combos = [ + (True, 50, 2), # baseline + (False, 50, 2), # no-mmap + (True, 0, 2), # no polling + (True, 100, 2), # max polling + (True, 50, 3), # realtime priority + (False, 0, 3), # no-mmap + no-poll + realtime + ] + r = phase_sweep("Misc (mmap, poll, prio)", ["mmap", "poll", "prio"], misc_combos, cfg) + if r: + cfg["mmap"] = r["mmap"] + cfg["poll"] = r["poll"] + cfg["prio"] = r["prio"] + + # ─── Final Report ─── + print() + print("=" * 70) + print(" FINAL OPTIMAL CONFIGURATION") + print("=" * 70) + print(f" ngl: {cfg['ngl']}") + print(f" threads: -t {cfg['t']} -tb {cfg['tb']}") + print(f" batch: -ub {cfg['ub']} -b {cfg['b']}") + print(f" kv cache: -ctk {cfg['ctk']} -ctv {cfg['ctv']}") + print(f" flash: -fa {cfg['fa']}") + print(f" mlock: {'yes' if cfg['mlock'] else 'no'}") + print(f" mmap: {'yes' if cfg['mmap'] else 'no (--no-mmap)'}") + print(f" prio: {cfg['prio']}") + print(f" poll: {cfg['poll']}") + print() + + # Final verification run + print(" Running final verification (5 runs)...") + kill_server() + proc = start_server(cfg) + wait_for_server() + try: + run_benchmark(max_tokens=20) + except: + pass + final_speeds = [] + for i in range(5): + try: + tps = run_benchmark() + final_speeds.append(tps) + print(f" Run {i+1}: {tps:.2f} t/s") + except: + pass + proc.kill() + + if final_speeds: + avg = sum(final_speeds) / len(final_speeds) + best = max(final_speeds) + print(f"\n ★ FINAL: AVG {avg:.2f} t/s | BEST {best:.2f} t/s") + + print() + cmd_parts = [ + f"llama-server --model {MODEL}", + f"-ngl {cfg['ngl']} -c {CONTEXT}", + f"-t {cfg['t']} -tb {cfg['tb']}", + f"-ub {cfg['ub']} -b {cfg['b']}", + f"-fa {cfg['fa']}", + f"--cache-type-k {cfg['ctk']} --cache-type-v {cfg['ctv']}", + f"--prio {cfg['prio']} --poll {cfg['poll']}", + ] + if cfg["mlock"]: + cmd_parts.append("--mlock") + if not cfg["mmap"]: + cmd_parts.append("--no-mmap") + cmd_parts.append("--port 8000 --host 0.0.0.0") + + print(" Recommended command:") + print(f" {' '.join(cmd_parts)}") + print("=" * 70) + + # Dump all results to JSON + with open("scripts/tune_results_gemma4_256k.json", "w") as f: + json.dump(ALL_RESULTS, f, indent=2, default=str) + print(f"\n Full results saved: scripts/tune_results_gemma4_256k.json") + + +if __name__ == "__main__": + main() diff --git a/scripts/auto_tune_gemma4_ncpumoe.py b/scripts/auto_tune_gemma4_ncpumoe.py new file mode 100644 index 0000000..a38b027 --- /dev/null +++ b/scripts/auto_tune_gemma4_ncpumoe.py @@ -0,0 +1,163 @@ +""" +Gemma4 26B --n-cpu-moe sweep + secondary param tuning at 256K context. +Gemma4 has 30 layers. Sweep n-cpu-moe from 0 to 30. +""" +import subprocess, time, json, urllib.request, sys, os + +try: + sys.stdout.reconfigure(encoding='utf-8') +except: + pass + +BASE_URL = "http://127.0.0.1:8000" +SERVER = r"llama_bin_run\llama-server.exe" +MODEL = r"models\gemma-4-26B-A4B-it-Q4_K_M.gguf" +CTX = 262144 +RUNS = 3 + + +def kill(): + subprocess.run(["taskkill", "/F", "/IM", "llama-server.exe"], capture_output=True) + time.sleep(4) + + +def start(ncpumoe, t=4, ub=512, b=2048, ctk="q4_0", ctv="q4_0", prio=3, nommap=False): + cmd = [SERVER, "--model", MODEL, "-ngl", "999", + "-c", str(CTX), "-np", "1", "-fa", "on", + "--cache-type-k", ctk, "--cache-type-v", ctv, + "-ub", str(ub), "-b", str(b), "-t", str(t), "-tb", str(t), + "--prio", str(prio), "--poll", "50", + "--mlock", "--port", "8000", "--host", "0.0.0.0"] + if ncpumoe > 0: + cmd.extend(["--n-cpu-moe", str(ncpumoe)]) + if nommap: + cmd.append("--no-mmap") + return subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + cwd=os.getcwd(), text=True, encoding='utf-8', errors='replace') + + +def wait_ready(timeout=240): + t0 = time.time() + while time.time() - t0 < timeout: + try: + with urllib.request.urlopen(urllib.request.Request(f"{BASE_URL}/health"), timeout=3) as r: + if json.loads(r.read()).get("status") == "ok": + return True + except: + pass + time.sleep(2) + return False + + +def bench(n=200): + p = json.dumps({"model": "m", "messages": [{"role": "user", + "content": "Count from 1 to 50, each number on new line."}], + "max_tokens": n, "temperature": 0.0}).encode() + r = urllib.request.Request(f"{BASE_URL}/v1/chat/completions", data=p, + headers={"Content-Type": "application/json"}) + t0 = time.time() + with urllib.request.urlopen(r, timeout=300) as resp: + res = json.loads(resp.read()) + dt = time.time() - t0 + ct = res.get("usage", {}).get("completion_tokens", 0) + return ct / dt if dt > 0 else 0 + + +def vram(): + try: + r = subprocess.run(["nvidia-smi", "--query-gpu=memory.used,memory.total", + "--format=csv,noheader,nounits"], capture_output=True, text=True, timeout=5) + a, b = r.stdout.strip().split(",") + return int(a.strip()), int(b.strip()) + except: + return 0, 0 + + +def test(label, ncpumoe, **kw): + kill() + print(f" [{label}] Starting...", end=" ", flush=True) + p = start(ncpumoe, **kw) + if not wait_ready(): + print("FAILED"); p.kill(); return None + vu, vt = vram() + print(f"VRAM:{vu}/{vt} | ", end="", flush=True) + try: bench(20) + except: pass + speeds = [] + for _ in range(RUNS): + try: speeds.append(bench()) + except: pass + p.kill() + if not speeds: + print("BENCH FAILED"); return None + avg, best = sum(speeds)/len(speeds), max(speeds) + print(f"AVG:{avg:.1f} BEST:{best:.1f} t/s") + return {"label": label, "ncpumoe": ncpumoe, "avg": avg, "best": best, + "vram": vu, **kw} + + +def main(): + print("=" * 60) + print(" Gemma4 26B 256K | --n-cpu-moe Sweep + Param Tune") + print("=" * 60) + results = [] + + # Phase 1: n-cpu-moe sweep (0, 5, 10, 15, 20, 25, 30) + print("\n--- Phase 1: --n-cpu-moe sweep ---") + for n in [0, 5, 10, 15, 20, 25, 30]: + nm = n > 15 # use --no-mmap when heavy CPU offload + r = test(f"ncpumoe={n}", n, nommap=nm) + if r: results.append(r) + + # Find best n-cpu-moe + best_r = max(results, key=lambda x: x["avg"]) + best_n = best_r["ncpumoe"] + print(f"\n ★ Best n-cpu-moe: {best_n} → {best_r['avg']:.1f} t/s") + + # Fine-tune around best + if best_n > 0: + print(f"\n--- Phase 1b: Fine-tune around ncpumoe={best_n} ---") + for n in [max(0, best_n-3), max(0, best_n-1), best_n+1, min(30, best_n+3)]: + if n == best_n: continue + nm = n > 15 + r = test(f"ncpumoe={n}", n, nommap=nm) + if r: results.append(r) + best_r = max(results, key=lambda x: x["avg"]) + best_n = best_r["ncpumoe"] + print(f"\n ★ Refined n-cpu-moe: {best_n} → {best_r['avg']:.1f} t/s") + + # Phase 2: Thread sweep at best n-cpu-moe + nm = best_n > 15 + print(f"\n--- Phase 2: Thread sweep (ncpumoe={best_n}) ---") + for t in [2, 4, 6, 8, 10]: + r = test(f"t={t}", best_n, t=t, nommap=nm) + if r: results.append(r) + best_t = max([x for x in results if x["ncpumoe"]==best_n], key=lambda x: x["avg"]) + bt = best_t.get("t", 4) + print(f"\n ★ Best threads: {bt}") + + # Phase 3: Batch sweep + print(f"\n--- Phase 3: Batch sweep ---") + for ub, b in [(256, 1024), (512, 2048), (512, 4096), (1024, 2048)]: + r = test(f"ub={ub},b={b}", best_n, t=bt, ub=ub, b=b, nommap=nm) + if r: results.append(r) + + # Phase 4: KV cache type + print(f"\n--- Phase 4: KV cache type ---") + for ctk, ctv in [("q4_0","q4_0"), ("q8_0","q8_0")]: + r = test(f"kv={ctk}", best_n, t=bt, ctk=ctk, ctv=ctv, nommap=nm) + if r: results.append(r) + + # Final report + best_all = max(results, key=lambda x: x["avg"]) + print(f"\n{'='*60}") + print(f" FINAL BEST: {best_all['label']} → {best_all['avg']:.1f} t/s (VRAM: {best_all['vram']})") + print(f"{'='*60}") + + with open("scripts/tune_results_gemma4_ncpumoe.json", "w") as f: + json.dump(results, f, indent=2, default=str) + print(" Saved: scripts/tune_results_gemma4_ncpumoe.json") + + +if __name__ == "__main__": + main() diff --git a/scripts/auto_tune_qwen35b_256k.py b/scripts/auto_tune_qwen35b_256k.py new file mode 100644 index 0000000..2a58847 --- /dev/null +++ b/scripts/auto_tune_qwen35b_256k.py @@ -0,0 +1,335 @@ +""" +Qwen3.5 35B-A3B Comprehensive Auto-Tuner | 256K Context | RTX 3060 12GB +Based on existing optimized setting: --cpu-moe -ngl 999 -c 4096 -t 6 (35 t/s) +Now tuning for -c 262144 (256K context). + +Phase 1: --cpu-moe vs no --cpu-moe baseline +Phase 2: -t / -tb sweep +Phase 3: -ub / -b sweep +Phase 4: --cache-type-k/v sweep +Phase 5: Misc (mmap, poll, prio) +""" +import subprocess +import time +import json +import urllib.request +import sys +import os + +try: + sys.stdout.reconfigure(encoding='utf-8') +except AttributeError: + pass + +BASE_URL = "http://127.0.0.1:8000" +LLAMA_SERVER = r"llama_bin_run\llama-server.exe" +MODEL = r"models\Qwen3.5-35B-A3B-Q4_K_M.gguf" +CONTEXT = 262144 +BENCHMARK_RUNS = 3 +BENCHMARK_TOKENS = 200 + +BEST = { + "ngl": 999, + "cpu_moe": True, + "t": 6, + "tb": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": True, + "mmap": True, + "prio": 2, + "poll": 50, +} + +ALL_RESULTS = [] + + +def kill_server(): + subprocess.run(["taskkill", "/F", "/IM", "llama-server.exe"], capture_output=True) + time.sleep(4) + + +def build_cmd(cfg): + cmd = [LLAMA_SERVER, "--model", MODEL, + "-ngl", str(cfg["ngl"]), + "-c", str(CONTEXT), + "-np", "1", + "-fa", cfg["fa"], + "--cache-type-k", cfg["ctk"], + "--cache-type-v", cfg["ctv"], + "-ub", str(cfg["ub"]), + "-b", str(cfg["b"]), + "-t", str(cfg["t"]), + "-tb", str(cfg["tb"]), + "--prio", str(cfg["prio"]), + "--poll", str(cfg["poll"]), + "--port", "8000", + "--host", "0.0.0.0"] + if cfg.get("cpu_moe"): + cmd.append("--cpu-moe") + if cfg["mlock"]: + cmd.append("--mlock") + if not cfg["mmap"]: + cmd.append("--no-mmap") + return cmd + + +def start_server(cfg): + cmd = build_cmd(cfg) + proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + cwd=os.getcwd(), text=True, encoding='utf-8', errors='replace' + ) + return proc + + +def wait_for_server(timeout=240): + start = time.time() + while time.time() - start < timeout: + try: + req = urllib.request.Request(f"{BASE_URL}/health") + with urllib.request.urlopen(req, timeout=3) as resp: + data = json.loads(resp.read()) + if data.get("status") == "ok": + return True + except: + pass + time.sleep(2) + return False + + +def run_benchmark(max_tokens=BENCHMARK_TOKENS): + payload = json.dumps({ + "model": "local-model", + "messages": [{"role": "user", "content": "Count from 1 to 50, writing each number on a new line."}], + "max_tokens": max_tokens, + "temperature": 0.0 + }).encode("utf-8") + + req = urllib.request.Request( + f"{BASE_URL}/v1/chat/completions", + data=payload, + headers={"Content-Type": "application/json"} + ) + + start = time.time() + with urllib.request.urlopen(req, timeout=300) as resp: + result = json.loads(resp.read()) + elapsed = time.time() - start + + usage = result.get("usage", {}) + ct = usage.get("completion_tokens", 0) + return ct / elapsed if elapsed > 0 else 0 + + +def get_vram(): + try: + r = subprocess.run( + ["nvidia-smi", "--query-gpu=memory.used,memory.total", + "--format=csv,noheader,nounits"], + capture_output=True, text=True, timeout=5 + ) + parts = r.stdout.strip().split(",") + return int(parts[0].strip()), int(parts[1].strip()) + except: + return 0, 0 + + +def test_config(cfg, label=""): + kill_server() + desc = label or str(cfg) + print(f" [{desc}] Starting server...", flush=True) + proc = start_server(cfg) + + if not wait_for_server(): + print(f" [{desc}] FAILED to start") + proc.kill() + return None + + vram_used, vram_total = get_vram() + print(f" [{desc}] VRAM: {vram_used}/{vram_total} MiB | ", end="", flush=True) + + # Warmup + try: + run_benchmark(max_tokens=20) + except: + pass + + speeds = [] + for i in range(BENCHMARK_RUNS): + try: + tps = run_benchmark() + speeds.append(tps) + except Exception as e: + print(f"ERR({e}) ", end="", flush=True) + + proc.kill() + + if not speeds: + print("ALL FAILED") + return None + + avg = sum(speeds) / len(speeds) + best = max(speeds) + print(f"AVG: {avg:.2f} t/s | BEST: {best:.2f} t/s") + + result = {**{k: v for k, v in cfg.items()}, "avg_tps": avg, "best_tps": best, + "vram_used": vram_used, "vram_total": vram_total, "label": label} + ALL_RESULTS.append(result) + return result + + +def phase_sweep(phase_name, param_name, values, base_cfg): + print(f"\n{'='*70}") + print(f" PHASE: {phase_name}") + print(f" Sweeping: {param_name} = {values}") + print(f"{'='*70}") + + best_result = None + for val in values: + cfg = {**base_cfg} + if isinstance(param_name, list): + for p, v in zip(param_name, val): + cfg[p] = v + label = " | ".join(f"{p}={v}" for p, v in zip(param_name, val)) + else: + cfg[param_name] = val + label = f"{param_name}={val}" + + r = test_config(cfg, label) + if r and (best_result is None or r["avg_tps"] > best_result["avg_tps"]): + best_result = r + + if best_result: + print(f"\n ★ Phase winner: {best_result['label']} → {best_result['avg_tps']:.2f} t/s") + return best_result + + +def main(): + print("=" * 70) + print(" Qwen3.5 35B-A3B COMPREHENSIVE Auto-Tuner") + print(" 256K Context | RTX 3060 12GB") + print(" Baseline: --cpu-moe -ngl 999 -c 4096 -t 6 → 35 t/s") + print("=" * 70) + print() + + cfg = dict(BEST) + + # ─── Phase 1: --cpu-moe critical test ─── + r = phase_sweep("CPU-MoE Mode", "cpu_moe", [True, False], cfg) + if r: + cfg["cpu_moe"] = r["cpu_moe"] + + # ─── Phase 2: CPU threads ─── + thread_combos = [ + (2, 2), (4, 4), (4, 6), (6, 6), (6, 8), + (8, 8), (8, 12), (10, 10), (12, 12) + ] + r = phase_sweep("CPU Threads (-t, -tb)", ["t", "tb"], thread_combos, cfg) + if r: + cfg["t"] = r["t"] + cfg["tb"] = r["tb"] + + # ─── Phase 3: Batch sizes ─── + batch_combos = [ + (128, 512), (256, 1024), (256, 2048), + (512, 1024), (512, 2048), (512, 4096), + (1024, 2048), (1024, 4096) + ] + r = phase_sweep("Batch Sizes (-ub, -b)", ["ub", "b"], batch_combos, cfg) + if r: + cfg["ub"] = r["ub"] + cfg["b"] = r["b"] + + # ─── Phase 4: KV cache ─── + kv_combos = [ + ("q4_0", "q4_0"), + ("q8_0", "q8_0"), + ("f16", "f16"), + ] + r = phase_sweep("KV Cache Type", ["ctk", "ctv"], kv_combos, cfg) + if r: + cfg["ctk"] = r["ctk"] + cfg["ctv"] = r["ctv"] + + # ─── Phase 5: Misc ─── + misc_combos = [ + (True, 50, 2), + (False, 50, 2), + (True, 0, 2), + (True, 100, 2), + (True, 50, 3), + ] + r = phase_sweep("Misc (mmap, poll, prio)", ["mmap", "poll", "prio"], misc_combos, cfg) + if r: + cfg["mmap"] = r["mmap"] + cfg["poll"] = r["poll"] + cfg["prio"] = r["prio"] + + # ─── Final Report ─── + print() + print("=" * 70) + print(" FINAL OPTIMAL CONFIGURATION") + print("=" * 70) + for k, v in cfg.items(): + print(f" {k:>12}: {v}") + print() + + # Final verification + print(" Running final verification (5 runs)...") + kill_server() + proc = start_server(cfg) + wait_for_server() + try: + run_benchmark(max_tokens=20) + except: + pass + final_speeds = [] + for i in range(5): + try: + tps = run_benchmark() + final_speeds.append(tps) + print(f" Run {i+1}: {tps:.2f} t/s") + except: + pass + proc.kill() + + if final_speeds: + avg = sum(final_speeds) / len(final_speeds) + best = max(final_speeds) + print(f"\n ★ FINAL: AVG {avg:.2f} t/s | BEST {best:.2f} t/s") + + print() + cmd_parts = [ + f"llama-server --model {MODEL}", + f"-ngl {cfg['ngl']} -c {CONTEXT}", + ] + if cfg.get("cpu_moe"): + cmd_parts.append("--cpu-moe") + cmd_parts.extend([ + f"-t {cfg['t']} -tb {cfg['tb']}", + f"-ub {cfg['ub']} -b {cfg['b']}", + f"-fa {cfg['fa']}", + f"--cache-type-k {cfg['ctk']} --cache-type-v {cfg['ctv']}", + f"--prio {cfg['prio']} --poll {cfg['poll']}", + ]) + if cfg["mlock"]: + cmd_parts.append("--mlock") + if not cfg["mmap"]: + cmd_parts.append("--no-mmap") + cmd_parts.append("--port 8000 --host 0.0.0.0") + + print(" Recommended command:") + print(f" {' '.join(cmd_parts)}") + print("=" * 70) + + with open("scripts/tune_results_qwen35b_256k.json", "w") as f: + json.dump(ALL_RESULTS, f, indent=2, default=str) + print(f"\n Full results saved: scripts/tune_results_qwen35b_256k.json") + + +if __name__ == "__main__": + main() diff --git a/scripts/boot_122b.txt b/scripts/boot_122b.txt new file mode 100644 index 0000000000000000000000000000000000000000..1fb6f840678af494ec022f0b806f07f68523f1b1 GIT binary patch literal 26954 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 literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_38.txt b/scripts/boot_122b_38.txt new file mode 100644 index 0000000000000000000000000000000000000000..5aa3ef2f7c06ee73079f51a95a76bea212527d7e GIT binary patch literal 25252 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 Sspl!PyOH0sx)SvWnEwO#I`roN literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_42.txt b/scripts/boot_122b_42.txt new file mode 100644 index 0000000000000000000000000000000000000000..606690ca395a5b321ba1f1cfc9f3a781d403e778 GIT binary patch literal 28522 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 literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_44.txt b/scripts/boot_122b_44.txt new file mode 100644 index 0000000000000000000000000000000000000000..a2c4e568e6590c7599649344306a016d31940c02 GIT binary patch literal 38504 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 literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_auto.txt b/scripts/boot_122b_auto.txt new file mode 100644 index 0000000000000000000000000000000000000000..6c03f60f691d3ac7ae9a3bc2445cd293ebe6b085 GIT binary patch literal 28162 zcmd6w*=`)i6^8r5R{?V82bd^vL?I?+?vjuM&~C$`Eo-w8MHo=r#E6?XLoJSxXUO~H z0gT+_GJclaB>BHOJgn-O?w;x%$~Fen^z?G->|0fr{`cR#Vy{>)_Vw2<o)^2tfxeH6 z6Mg?#SNz&+F(|fl{&o0wPXE5uU$3~Pza4${b!WfW&~uvs5^#2lVYt3s90qusx_3c; zH;c!`t>U_lep&oN_iqX6uAbe}7`<Yqc%=J>g0iPyPl^Y+b1lGGD3*%lVpV@Vo!KpJ z6qj@!7$1E9&A)oZ2micQ%<I~ffBgM>j=1tW{f<<0p>VdC6?KOJPcXWW{2GWyUkR&z z@wxui!ml@?<Dgh9=EC^PdU{?|F9_RNQ38H*;p5o-t@vp^j6XgyXM3j~cz08B7>c4D zap1UkS9~Vgkf-01exnOt1lc_-j`YliWVEfnE%A<PGZQprPWQy|;Zn2YuN4o4+p$JC z5adgG`f2g3_*mZ$G$t|w^V`x?bn8gJc6II`8KW2U<Bqfw9eF2No&>#%c;6QMV?A{& z%F$n=5uWS@Tezuf`}(X4?xx1_yNwQ~=9b2H9q$#NoI-gbn78zYCEV2=cfvT}3I&gf zZ}h#WyI1rFRU2WfBjE^L{9YGT>%nUGgwIeENc+XNm2m%)K<V14^}N$FgVXcTrc5`l z$g13?s&StG+2gz??yO4&*!a45^HTI86C~!>(8SdLl<(|GTaopCxaXRz`SGdO!_J=w zD!R5MKF=0k2U#)3v97^0WIWKQLtQ!2x!0xp(XV}-ZSqC#M`wk-`O!Jy%M-wN8Ya{C zptvu)?TzrEJ9W3h_~%F6vG}tgE$u?vP0{xFXH46MXgiU9ypZ$<1+f76rL+!YrGsF> z=o!8Xe~Ok8d53!PSWltJM8iMm&#Za;s%gSdynIo*6AiTZISS8g8<_Kg$}j#>jQW`v z;mz|f!Y^Mw9kEw@r=uAEEqeFl0Uzk>r}F1U@v=M~K5s>M^1XWco7m6qE2qD&=nngR zRo@2vv7p=*C+~*L;eG;@*vt6$b^W%k60xEGx;$-P@%g%ZE?D#h;j`jTy2r4=M_xI7 z*7Xm0Jq#l)p50~8ISU4(=da6y;L(lycJK8tKIh4c(kpj@gs|fB*|iWY(sMUN#b;yB z-z)CvPVT;=tM?LGY~(Kk7rqGNuj#lv&I8?lU(eoDUQ@o}I`cqUexQ%V-kvm_`13-V zM|3jY+zZgT!!yAX>6$+=AM&_Sea9A#_57!j#iM{%ns=JEqT9zB1sPbzx_JimC_E1a zj!{pRv?Uu`7iN0_H*9M&SlJ-h7}xd%fee5Vh~{9KMh?e1W`c$b8L{U;(L=|(nql;c ziyF;y%jRryG?Qd}tb@EV%|T~_ZD2q1x^gJW;~BD2qRffTUJSHaP5};Bv&)JK0Do^B zg9h@rp+3$B@{Z%cH@p-c$M_txoP9w6E?Fbli%a&opzZ{|L&2tgc?JiZ7a9SG<{^&) zY#{g;;B}-Y;Ms{hA9)Z@F;*(`*;9;rx^r6}Ffh%sr@_<42Ld_1RW{Fc7aC_dgW*Xw z=%b)<;19HepfMb6=mQ;K9`!E8!LrUY`llT1pJIyy_C?=b&^^QSFT!R+G+OSGa<!*1 z4c9oPOK?}R*%LN#6sRS4A!7m0r}7TeP|q|L*V^HR&d=)4eL3EDM%jOxoR;bhI991q zBUv6A;}=pR2VO0%>grV;8^xPJp0hf)qH8Ovz%0Aj5_LUEav(iNV(92jpxX4nEETJv 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 literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_maxmem.txt b/scripts/boot_122b_maxmem.txt new file mode 100644 index 0000000000000000000000000000000000000000..4349116fe85ceaa54dc689857bd7ee7ef7c37a20 GIT binary patch literal 28272 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 literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_row.txt b/scripts/boot_122b_row.txt new file mode 100644 index 0000000000000000000000000000000000000000..b321c04a85555c035278504c053da65cad759ed4 GIT binary patch literal 28366 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 GZTUaJq}~|- literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_row_dual.txt b/scripts/boot_122b_row_dual.txt new file mode 100644 index 0000000000000000000000000000000000000000..989349a5f7cd2c613303b6666b0df2b0b42f2b19 GIT binary patch literal 25252 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 QJY{w_@_SZSq8<VBe}lgD0{{R3 literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_single.txt b/scripts/boot_122b_single.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f7210185fcaeab30bacdd3ff8f86d7bab204395 GIT binary patch literal 37600 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 JmD*a<{{Wxuj@SSI literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_single2.txt b/scripts/boot_122b_single2.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a72269138715ab9e9c03aefa225b98fa8fb0977 GIT binary patch literal 30360 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 WAK`f;{5CX(okv4M-b0JQ=l=m6@+J%b literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_ts85.txt b/scripts/boot_122b_ts85.txt new file mode 100644 index 0000000000000000000000000000000000000000..f1eed644bf1a7d9a8040f7a4c3926e69d349e613 GIT binary patch literal 28564 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 QVe&7w*7UX4ih}$91Fru7z5oCK literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_tune.txt b/scripts/boot_122b_tune.txt new file mode 100644 index 0000000000000000000000000000000000000000..4c862e4fb765bba5dc50f5eff360a12045e2b151 GIT binary patch literal 25234 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 literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_tuned.txt b/scripts/boot_122b_tuned.txt new file mode 100644 index 0000000000000000000000000000000000000000..68229fed42563393bddd3c5bd7218ec16d017f6f GIT binary patch literal 40074 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 bQhlOacilo6m)O?bDS~D?GO>qPoudB(2!QwE literal 0 HcmV?d00001 diff --git a/scripts/boot_122b_v2.txt b/scripts/boot_122b_v2.txt new file mode 100644 index 0000000000000000000000000000000000000000..f379fd555aef1933b5ac2e2a52d9de709c868212 GIT binary patch literal 61200 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 Z$k~jkRpipNSgit^ju(Hs`{swU{|9LfyL$it literal 0 HcmV?d00001 diff --git a/scripts/boot_log.txt b/scripts/boot_log.txt new file mode 100644 index 0000000000000000000000000000000000000000..ad47a3e06c6dba042363bd795f27dddd5c11dc90 GIT binary patch literal 29276 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 V|K7+x17ESDH#y#=G@iNg{{c|Liv0ip literal 0 HcmV?d00001 diff --git a/scripts/boot_log2.txt b/scripts/boot_log2.txt new file mode 100644 index 0000000000000000000000000000000000000000..77536bdc5438e916723d27fd455fb14f3d7b1a4b GIT binary patch literal 27986 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 QtZ7@OydHQ?{~dhzKW-_uG5`Po literal 0 HcmV?d00001 diff --git a/scripts/boot_log3.txt b/scripts/boot_log3.txt new file mode 100644 index 0000000000000000000000000000000000000000..cfa94c7b042a8015699b351b4faaaf6cfe8a469a GIT binary patch literal 27936 zcmd6w$#NV?a)$FTCz<Ki4^W~sBFEGK8yhz*W?XyPnw;T~P0pBFZDk+`f)ELi#L|m2 zrf1Om=mExb(NX;@x=8xJ_~D4is;sQ2EMj`BtWa5*SsuQ-hlgk2|Ni%F^S0S;4)h*3 zFPi=4P@hN5sXqTnSK``AbKLCe{I}`vs{Vej_o%t0_nuw{x^vv@=yxv@B;f2fC+YgD z=3RpKQunUweYbhm+-q*@=+B!!)BU@Gy072vYK&2HrTI$tp9soZeS6+K(VdSHob_h2 z*=nxqJ<^%|=1%hqod?Fx{_wm18Z|%r&s)u!uHE>LfB(ZR-E|{f`x~8EO|OygKWSE) zr-Hnz*Lz*rX#Q4zGo3?tup*cT!Wl}yIUj@DvH19#<~v<G)M%^1Yek=1!anfgF0h>U z`nTp{UW`8*GFJmLu)HhDoQRq|QGC?=(0n1fkZ{~nexe<}O458PZtqIQ`?_~uM=z5^ zuguY%RsBAXj}-fL;g)23BCOE9J!!|N`MUY0`Ba}zG$wKZ^ZU|WwC%mV?d#lOF~%rK z;(_!Po%lhLJ58FH@xCwkNBY%~C`Y4BBRttp)^S(Y4)od<+?N_F?lv7k%{`4DT0d$& zJA?9EFz@M&ZG5gf9;9)=6$-v;ey7h3-F;1OsM<+my%&zq#rJJdwVmwvt?)S!1+oY6 z?OMA3d7|{unLYiW-yENv&sMf38(r6LuIrb!vTmH${^W6fEADJd2H5nrc=JZ|A`>JQ z*U-e$Bem}wNn4TiLAvLbEcw})AH=Gk3o5#{D?YC@zfH1Yj3ZrxXUO<iqn_x>d!75X zbwB!bptF6xCp_zlu(xMj6~6oe_(8*b`kpk8Ww)aVJ`AVsUK;=7QFkQ%>_|(8&~{g} zJ^K@;ZAY}7N<Usp`o}2_Ait8<V_E5OvS9QK--SO#ONqrN`sI-{2~8$G{=MF|=IN`} zgcI@db?Z(v&=GYKo^2bLYl6x*-X%ukN{ooybr|87ubz!KYJRVyoDn#lkK_TL=<F}$ z&rR``JRUypn(*ZF`q_7~0lwcj`+h@r`1?(L8u~Lqxi3zBo^pxD1yr&H#(%8q_jPqF zj|cs?<!Q%?(6{At!D1{3Uo`)ydyEY}^2XV3L;sN1(=^h?`CW$2SumJ9e_I{|k8bY! z-rH$>&XXyXR~{q@Va4sUk5aTK&)pFfUrhb}Ve?RTa`yvWeOS=qk$;`I@T)ZbBOSNL zd7}Ft>$i8CFDI^q&ODKpAL`}UJCddoe_l)Th)(9s!vvi>A~!shuEj6bB+Jp1zGDkV zI{!<_;;V#LnTc9k(QR@&WZ)e3<vG;%>Gxm|7>#5}yRxxuVfHrRhHbq}R(70hjB5vi zKnB1FM02n#BS&C8neCAyM-G%dbiS(}#;Cch(IS_eoja~b78}yg6=f)!($6pT2Gd9K zEJTe{(RVqWagG2?tTT8|j*tH~2f&HE>_o4ii#*~e@d2-c2QlAr)^Z>Sz$FVL8wp9h z5Y)ZIM=*M+Z;_E%y8G#xJsvqY85?<u#{jSQIs*?*<;lov&?CkwWw>&N@kn>R(hCf% zDgHHhdVC;|qkHc1LU*A-&H;=k*<Fl+27y1&4wD9Ow4)bvz>BO$B@WIa%V=J5aD0X> z5;zcjZ<8(=(|;B=JEGCKNXgZa#x$;Z%#h%|Wb;<o<WZoOT!ah+JinGUFiZMIV{vUU z+|c0_y~8)-O|K}MUnECmeg>SiG=9!2b38(7T>nfSeN(<WD1MpbX*=RPSIEVfw~Zvp zW9c~(Lr3=#)z$<50(i_8m#3NtadA|`ku~8tN%czN2zmY=hw3gpv^9qQf$2@*13#=) z!EbzN+b6*F@C#X1K-f(5PD03XorV=~7MCP*A>t944|8%fhod}N+tNy1!g{d&QL-_t zjPXnCJa0NoP?<qYa*OH%8A9-iii2Yd_Urqdkqw~?+_3ZS6GcSOf6=#5@)gkdBKcFx zj@W7J)+Imd8ydIL{Dc15YvJ3;8Q~b#RD+Xa25}MEG7lU_O23rjN9KLWOG_st4P3@2 zTJ5FWd8`q(MN#-Q{9a_l#0@;;Y^rZcZ*L}V!n~LWak2n?^J8MBX;PowX|y-O?=$(T ztz<c1N<21w<nxo9h70h9YFf@77=u};qX_(N$B{CNYIAm;_h$UQDg(Lt6;a^VEq!jv zr}J%9Z>|y}*7S09E^B=7<&Q<-EKj{Sh1Ys0BnpO2FG3-@i)M#RbF9Y;kXcbd;wVS@ zE%9`esun+_>;=6)mF#U>`+|sNZJ%q?NHKIH%`)%}hq{Wkb5H0`S({uew=DBYG{U(Q zo+q@Kiq+?ui9gVI&l46`gs*Fb<a)o*t40@sQ};wGnNsAVSQi}1oTApk%=xOY3Ens_ zaLsWyar{eRgEt3b#-|>)pKJ`yywnxEprZuUGjcP$O8Dv5I^s^xV_a)nU5FAa<c-F6 zZsJ;>kL&*8k%=^9<5UjFd7uQ3jem0XNOtNNKtA0EnYrQz>6t6iTPapg`}{6X6tZbk z<Vs-Qi1V&R%qgj)c1g_`|6W5;#&;1dYg4kGG-e*QaeY!Q$C3;h>+!9(tHPb?-#A5r zec1&zJ^O`h*5z4;)39-YG%#CxRYe2w;byAMIC2c9hj=raU(;8*rNa1Fc9shB$$EO$ zQoM^exQu+Z7Nn<}Lg*>2<uK`9y|r|&NJUj)xU`6!(-_yBDdpzzSCy|(GsRC~SwB5> z*B7R)qzd2tK~W9r$h4`okhgn<!kKK1r+cQ~9!r;rS+AGy6gL*8202br1T`@Oai;!4 zG%;tY^V?nvU0(BU4x+}InQ5|YkQBax>Jz-K*F3Mk5t(<Dv#lquVQr3;^l^%}lR17@ zWTlF+s5NCuoGU;D<7Y}t*<p%DbrO0lJv))FJQg-r^?EHGrp9PrO05*=tNQy+SHrX9 zBgW4w-(HI!&XeKUM#&qGznXF;D#T;abhLohF2)lo<I7ttbF5l@C}~BT!;_+Y&>pi) zOAY*(L7!H|c31PtYGTg4@~pL6qieI1KKwo7umv#-T^07r#0S>Y-H+j#`f%Nxm8<kq zQ&Pf=jq&$Vgdje+v%uA;I(rQZ98Sl)9HovP#z#7?=wGyr1=SbjcZO#+k^R6mcv9wV z%xJv^!L05`HG9kWQ`!1A6PXlqgRh%QveMF<TuQaAOY+97QZdGgY}vIzj@}jDFQxIn zNq>jg@1hydhpBlczQ~%%s>&OAUgE}L9%yrk8Pb~SvbA2c3wyA|TS9}mjL#!a*wb0o z-Rrz)S&cP6hGYnJSx*-o%zwz#SovgpNAsbyP#1K+-4MNE#@i>8F8uL}u5(2fm#Z>l zUheu?T(6=BVi*mxGLu^DjXAPlMv$?mc0l#0R<DL?6Du^`Qs|<9iZt~ZI$7{N&XjXK zaGdgH)-~N@=USzGoa-Q;N@u!#9Q9;Y2J<I99aPeX`>u6G`EKgMKj+Urdg1Qs*(%Zp z$tg0c+R6x7efc7T$*<wEbMDcFd6%W+6_v3(7g-0Z3(h?bB)PHVNyg9Wj&qOtx=*HN zc&50njI35#$gPWRyfgenOIh_PZK~dywzO(CTq-qL!z|o0-i+_nytZekrD7S++#m6~ zaPQJLC~&=gXoOi#4zpu^Ed{JGSPxi(`Dv(_j`m&HqxV^NUI|;*nB1T9U7~_)6I~jT zxv-YGKG-JcU&gZMV}7h`3Oob*HXX>~yLA1hqGMLP&vC>@U6j%baG*G{llX{-{Z`U& z_b~GV>SuNFe!9!0b~eshM6HkQqX9`UYr*cZjHm|IQs8b%+t%VaFe{#QRP@oXm*hZB zb1K^z5-FC+9XHad`lQ~pzxQEJjztCr58*#@fC^R&UFoaSB{X8V9O@%>A1#c}T*R~F z#kGmKA)QLY>6jI>yRd#GePOmRPTF72M;JdkE;&zMC^gwyt60Xa7mwLp**p|@g&#Wd ze596wYtriBQkWgF+jfz^QR~2i(p}8D%8~pl<Hu7JtFn=(d)BT_(1CQalvPZ}?2Pg3 zqe)tz19~zo^z7BIOO(1hR3=zfou>`DCaEw5mI+S`dUf0~E9Zx~21Zo@uf)s`s8Iv^ z3)R7x5nj?0G?&hg|ML9kug;Htetz`VXGe^GN!`(gu-tmYrgEV=o^<oVPfl7-^Bp{; z5#0|_7mPVGeO6sOi5W9oS^OBD<X<k!llr{EnvyBcmg|Td{2{uz(|4Ah@FWONt+n;! zlOIRTCqIU+eDb5e=QI1_*5SE^aP=g8g9Grye}~kAHmmZIZpwq|UHL1pU_PGjr1y`0 z%et6ueW5-#JSJ}T<|i4mQhGrH{pr*giRsX?_Yr8>lB|I>OfL0#iaTc~V~RUv8K0U0 zvE3d0&VOqo%fB{1S7n~N-MyOY_w87V$BXr?V?`scwq8l}=f1aPEp}GU6P|o}-Jec| z+{FOxUTJ17GghvHy+-y>udBnQ+Mn}eIwp_WYgvp>b<bVRr5~=dHlAS`PQ#)(Pg@4m zV$mYTM;`P!eqT^itC_z!U$ZHnR*w#imGeH&t7)`65^LzJOqqUnr&uqS@uKzlQ#Ut^ zA2^vC(LWk?=Uih_E6VR!V0+CC3niD|SM`@UR@Au1=iLvv-+`6omBbUTE|c@nw?9-% zc;2xu<LKjpIY5T&@%{8N(Pi=Mo+{ArJ~)2eM+Nnv(!YdmXKkZ);_fG8^Crc}#i_VH zAS&kEENcFygvu6O=$-{e_w}N3&d@(7pBbM^dqc24E_6RP7*gN~+K-9*mT>>_T*k~D zJ|^^QE$C#tUC@~$k#`WM`J@60z2V<E^4bS_<=Mr&E_UQOA8wW+o&*mP&uRLJiARc$ z<V5ti*FnrxwSdU1XjPfnO+o)$u<q#disBC|N<4dXM^DTCHa(+Z9rSJp(2p#znpjhS zcQJR){+qmwyEC=$kS8PYeN-~;H^0`%?O(^UhIusbCDX~7D9;~V(G2YG^z=2}h%WqB z$!_qBzt@vVm(QOT=!3)aaRG<O$Ep?3Y9U-xj-DZ)6~J)DS`VY7Cil8T&Q=eHr=NqG z&`__UzMn}E8KL)8ac!xTqxS=GFCwwu*}1ims_CRw<lkDNNS@s-{0uG;uNxM<(Vl%_ zg^#3neHzVt7C2>uopp@el$8tWH?DGKoork7x4HvNUr3H*qJEa68$Qq-!Dc~YWt7Su z+78@qI6hkgjW|p6C$A)Lz@EBc0&TKGWHy%QJ>4A43j1G%o4Yk?V4B19%6GpOftlBx zyJ5vyU`CfuACA%O5x<-D3B0J#*T9ROi#~X+mG?oTs!Cj$_X4}9miED8{}7%LoA&}< zzh?7H6TSs_X!v1z=BXR*d}z%0LIcOL?dZVa>Bq?z&{*AfHKLE&E{O3IRSC5o;-0)o zE&se`UWXrDY_nm5UtTk<gUD*&vJhVhM(*u!KS#;8^4KyabJ*1mSafjo`S3PmdPj1{ zNC}l=Ap7YVIum)OeIDc;d0Rh8g%y5Cy-$-+12P#<J<fDU;(cT8wy$IM3*jk~a)iaX zL42Sio=o#NMIctzOPD@h95R{diR9Q1*L<$$2JqN5V;aNhGsd}3YPSE}bubMhp8;B} z{bz{O7MQVjMK~bVSZfJwPx04ly=}<{ALmh<f8U2$4K%V9XmkD+($4%UkD$~&RkOSZ z&oaD|{Y0kTo`;mMj2k;m^wApYf@FlTBGHDDzk%Jnn4RH;l?0|4P8<Qxln_tcn>LGX zKZ{-V<IT>o%*EWL#t~kmj}IAs;7zAPcNRBaO4v(eg3Len<vYgWFH}xugg~p!1cSR7 z79Bo#CZ79Xr8;;UnhD*0^--VUGB06e{zTCDoVG{sbw!5Q^PTSMgM5(ekU5Mi;q89W zj4ek%{#yDPt6TBe?hT!#A?~;t3u9Ni=(uUa&)*oyYu7c<;E1~wGKBKT&~jHo4b0q6 zQ$otez|4I!^C1$KayLkZlaGKH{U7t-p}*M!W*Fs>G3<}_Stj7eoNXuJNj_1+;uv`S zHd?Ok&+}HMmf9?Pin5bdzn12{fxO?+G>(BM{XFcsNqwRpa(nfme3EIBesiF|Hs41> zo*B>g<#31U(fwYopamt=X^it3AeJlFeV78bz22MQOdbW?SpDsTdq>r(r^2JGb4<dE z@4$7v^|IE*y_4E3tDp+D2;5u;LqA*vo338<QNoB{EkMcgBeM73y#XV#QX4d~OH`}~ zwySJ?v_4Ebiqy!=7P6*#DAmu)&Hwy&zuedd@saL)teqe4&!H{!j4C-hOa@K4W=Q3a zZ{MHQ>iTGkk)NLJm(oXFdt`Qxu*Ss5?BPcyzu3s^5IIkt?UAYNwMRbCn5^tG({Yuv zhUOYgYLC1zHF8;@tr^q1SN2<edrWG&rgm0Ew`N>+{d_D)_43Wuf<)nLy8_%d>U1?e zyxH_;NiE$SLG7$+{KbsBjQ?w|J4{m;XG%(4?{8~ytu77aE_J=&?hsuXKJGHQ<Vs0f zbKB1f1b^*y^NgPPhM&J{uY<CZ6xU+T*Us3`y;wD+7mcSWz4sDxWbc}{ojN>OfnujD zu7j1k47_jL73B_KPrVx1vp>OG!QD;$ZLGN=1?Cylwb}iteOK&KHJb|WH#$j6xjz(? z#*TI7AuDw3Q;i+PFPT4<G{d28*c)k$&)UOWn0^DV#N~0wM)5PlM)FLKZpN$9SA6|& z$XPQuDtDkO<;^au#4Y-SCRe9MqIXMK`AtdIw0qCBdEDzB8ER@rrl;Yi#-t*^XYBS? zH*(NA)^5e@GJCSX$ly+unDtxM`pc1{i{_{Jgz}FwE|BQZz&_n2h8DYKif)&?5xZy5 zS~+`_*dE1oSMU7nN-h??l5FHvvbdq6PG;p7JznH=IVN57h}{wCqDSq@ZED9Xdp?mX z-5q`oAOGny?&)Ip=+aYqRe|-c_U~-lv#tV_87UEj`eH=J@HOb#^mDH55dwy5e(}rv z35>}aUq78-WN3leve0;rIsS7%hUMs(SFh${b*ef}7bNqEee>t@yR3=FlQmHnt>yQt z6v23ga1w%PBJ$EFK>pwz5c+B38GLs*=Klo~d;+JOXUwNBSCjM8<0Y)FXT%qQnrqZ; zsPkducwNS@_U@P7A$@l2_Lz>K<e+8j&Q@lAB58M(U?cezk(8OXckt}b7l@to^0}T} zju}tP_(}czUtqAG_-|DuY_c-1ba<VRY>ld9c@Mbeu8=Yx<Nr@k`=h4reALv#K{U9% zJFj~n1CK`qOzV@gTX$hOzjTJ!-Z2@eW^CG7WmJ8sGlDf*8c-u&%)f;aD)GU6vXGMQ z7(MqA@n1F=e@ofYwe*fYhSHjHoR6Jo@uN{worPEY$~)ah_mck?KzRDn_h;I`h?NQF T!_)M&X>t!>?9rMv5-9%<XOY0! literal 0 HcmV?d00001 diff --git a/scripts/boot_log4.txt b/scripts/boot_log4.txt new file mode 100644 index 0000000000000000000000000000000000000000..7c327acab2e6e0e27af63fa937f2ffe9a2125bda GIT binary patch literal 9184 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 N-;F93uQ39l^Dm5&S$zNi literal 0 HcmV?d00001 diff --git a/scripts/boot_log5.txt b/scripts/boot_log5.txt new file mode 100644 index 0000000000000000000000000000000000000000..e9f3acaf38d009eb5576a3b19035cdad46ccf168 GIT binary patch literal 27868 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 literal 0 HcmV?d00001 diff --git a/scripts/boot_qwen_iq4.txt b/scripts/boot_qwen_iq4.txt new file mode 100644 index 0000000000000000000000000000000000000000..db5b6f4e5f1e0047900673a1a1206ca29ce12505 GIT binary patch literal 29832 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 HG5Gu+c=zkv literal 0 HcmV?d00001 diff --git a/scripts/check_help.bat b/scripts/check_help.bat new file mode 100644 index 0000000..87648a4 --- /dev/null +++ b/scripts/check_help.bat @@ -0,0 +1,3 @@ +@echo off +.\llama_bin_run\llama-server.exe --help 2>&1 | findstr /i "split tensor device main-gpu cpu-moe n-cpu-moe" > scripts\help_gpu_flags.txt +echo Done. diff --git a/scripts/download_llama.py b/scripts/download_llama.py new file mode 100644 index 0000000..7322872 --- /dev/null +++ b/scripts/download_llama.py @@ -0,0 +1,38 @@ +import urllib.request +import json +import zipfile +import os +import ssl + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +url = "https://api.github.com/repos/ggerganov/llama.cpp/releases/latest" +req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) +try: + with urllib.request.urlopen(req, context=ctx) as response: + data = json.loads(response.read().decode()) + + download_url = None + for asset in data['assets']: + if "bin-win-cuda-cu12.2.0-x64.zip" in asset['name'] or ("bin-win-cu" in asset['name'] and asset['name'].endswith(".zip") and "x64" in asset['name']): + download_url = asset['browser_download_url'] + break + + if download_url: + print(f"Downloading {download_url}...") + zip_path = "llama.zip" + with urllib.request.urlopen(download_url, context=ctx) as resp, open(zip_path, 'wb') as out_file: + out_file.write(resp.read()) + print("Extracting to 'llama_bin'...") + with zipfile.ZipFile(zip_path, 'r') as zip_ref: + zip_ref.extractall("llama_bin") + print("Done extracting.") + os.remove(zip_path) + else: + print("Could not find the target zip. Available assets:") + for asset in data['assets']: + print(" -", asset['name']) +except Exception as e: + print(f"Error: {e}") diff --git a/scripts/download_models.py b/scripts/download_models.py new file mode 100644 index 0000000..aaa5c1b --- /dev/null +++ b/scripts/download_models.py @@ -0,0 +1,33 @@ +import os +from huggingface_hub import hf_hub_download + +os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1" + +models = [ + # 먼저 용량이 작은 Gemma4 26B 부터 다운로드 + ("ggml-org/gemma-4-26B-A4B-it-GGUF", "gemma-4-26B-A4B-it-Q4_K_M.gguf"), + # 다음 Qwen 35B + ("unsloth/Qwen3.5-35B-A3B-GGUF", "Qwen3.5-35B-A3B-Q4_K_M.gguf"), + # 마지막으로 122B (분할 압축되어 있음) + ("unsloth/Qwen3.5-122B-A10B-GGUF", "Q4_K_M/Qwen3.5-122B-A10B-Q4_K_M-00001-of-00003.gguf"), + ("unsloth/Qwen3.5-122B-A10B-GGUF", "Q4_K_M/Qwen3.5-122B-A10B-Q4_K_M-00002-of-00003.gguf"), + ("unsloth/Qwen3.5-122B-A10B-GGUF", "Q4_K_M/Qwen3.5-122B-A10B-Q4_K_M-00003-of-00003.gguf") +] + +print("=== 고속 다운로더 시작 (huggingface_hub & hf_transfer) ===") +os.makedirs("models", exist_ok=True) + +for repo, filename in models: + print(f"\n>>> 다운로드 중 (백그라운드 진행): [{repo}] 의 [{filename}]...") + try: + path = hf_hub_download( + repo_id=repo, + filename=filename, + local_dir="./models", + local_dir_use_symlinks=False + ) + print(f"완료: {path}") + except Exception as e: + print(f"다운로드 실패: {e}") + +print("\n모든 다운로드 프로세스가 종료되었습니다.") diff --git a/scripts/download_true_llama.py b/scripts/download_true_llama.py new file mode 100644 index 0000000..37862a7 --- /dev/null +++ b/scripts/download_true_llama.py @@ -0,0 +1,56 @@ +import urllib.request +import json +import zipfile +import os +import ssl +import shutil + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + +url = "https://api.github.com/repos/ggerganov/llama.cpp/releases/latest" +req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) +try: + with urllib.request.urlopen(req, context=ctx) as response: + data = json.loads(response.read().decode()) + + download_url = None + for asset in data['assets']: + if "bin-win-cuda-12.4-x64.zip" in asset['name'] and "cudart" not in asset['name']: + download_url = asset['browser_download_url'] + break + + if download_url: + print(f"Downloading true binaries: {download_url}...") + zip_path = "llama_main.zip" + with urllib.request.urlopen(download_url, context=ctx) as resp, open(zip_path, 'wb') as out_file: + out_file.write(resp.read()) + + print("Extracting to temporary folder 'llama_temp'...") + with zipfile.ZipFile(zip_path, 'r') as zip_ref: + zip_ref.extractall("llama_temp") + + print("Moving exact files to 'llama_bin_run'...") + os.makedirs("llama_bin_run", exist_ok=True) + for root, dirs, files in os.walk("llama_temp"): + for file in files: + shutil.move(os.path.join(root, file), os.path.join("llama_bin_run", file)) + + if os.path.exists("llama_bin"): + for item in os.listdir("llama_bin"): + src = os.path.join("llama_bin", item) + dst = os.path.join("llama_bin_run", item) + if not os.path.exists(dst): + try: + shutil.copy(src, dst) + except: + pass + + os.remove(zip_path) + shutil.rmtree("llama_temp", ignore_errors=True) + print("Download and path extraction fully complete.") + else: + print("Could not find the target zip.") +except Exception as e: + print(f"Error: {e}") diff --git a/scripts/dual_gpu_benchmark.mjs b/scripts/dual_gpu_benchmark.mjs new file mode 100644 index 0000000..aa60a56 --- /dev/null +++ b/scripts/dual_gpu_benchmark.mjs @@ -0,0 +1,531 @@ +/** + * Dual-GPU (2x RTX 3060 24GB) Comprehensive Model Benchmark + * =========================================================== + * Tests 4 models across multiple parameter configurations to find + * the absolute best model + settings for 256K context coding agent. + * + * Models: + * 1. Qwen3.5-35B-A3B Q4_K_M (~20.5 GB) + * 2. Qwen3.5-35B-A3B MXFP4_MOE (~20.1 GB) + * 3. Gemma4 26B-A4B Q4_K_M (~15.6 GB) + * 4. Gemma4 26B-A4B MXFP4_MOE (~15.5 GB) + * + * Run: node scripts/dual_gpu_benchmark.mjs + */ + +import { spawn, execSync } from "child_process"; +import { writeFileSync, statSync, existsSync } from "fs"; +import { resolve } from "path"; + +// ─── Configuration ───────────────────────────────────────────── +const BASE_URL = "http://127.0.0.1:8000"; +const LLAMA_SERVER = String.raw`llama_bin_run\llama-server.exe`; +const CONTEXT = 262144; // 256K +const BENCHMARK_RUNS = 3; +const BENCHMARK_TOKENS = 200; +const SERVER_TIMEOUT = 300_000; // ms + +const MODELS = [ + { + name: "Qwen3.5-35B-A3B Q4_K_M", + path: String.raw`models\Qwen3.5-35B-A3B-Q4_K_M.gguf`, + type: "qwen", quant: "Q4_K_M", totalLayers: 64, + }, + { + name: "Qwen3.5-35B-A3B MXFP4_MOE", + path: String.raw`models\Qwen3.5-35B-A3B-MXFP4_MOE.gguf`, + type: "qwen", quant: "MXFP4_MOE", totalLayers: 64, + }, + { + name: "Gemma4 26B-A4B Q4_K_M", + path: String.raw`models\gemma-4-26B-A4B-it-Q4_K_M.gguf`, + type: "gemma4", quant: "Q4_K_M", totalLayers: 30, + }, + { + name: "Gemma4 26B-A4B MXFP4_MOE", + path: String.raw`models\gemma-4-26B-A4B-it-MXFP4_MOE.gguf`, + type: "gemma4", quant: "MXFP4_MOE", totalLayers: 30, + }, +]; + +const ALL_RESULTS = []; + +// ─── Utility ─────────────────────────────────────────────────── + +function log(msg) { + const ts = new Date().toLocaleTimeString("ko-KR", { hour12: false }); + console.log(`[${ts}] ${msg}`); +} + +function sleep(ms) { + return new Promise((r) => setTimeout(r, ms)); +} + +function killServer() { + try { + execSync("taskkill /F /IM llama-server.exe", { stdio: "ignore" }); + } catch {} + return sleep(5000); +} + +function getVramAll() { + try { + const out = execSync( + 'nvidia-smi --query-gpu=index,memory.used,memory.total --format=csv,noheader,nounits', + { encoding: "utf-8", timeout: 5000 } + ); + return out.trim().split("\n").map((line) => { + const [gpu, used, total] = line.split(",").map((s) => parseInt(s.trim())); + return { gpu, used, total }; + }); + } catch { + return []; + } +} + +function buildCmd(modelPath, params) { + const { + ngl, t, ub, b, ctk, ctv, + cpuMoe = false, nCpuMoe = 0, + prio = 3, nommap = false + } = params; + + const cmd = [ + LLAMA_SERVER, + "--model", modelPath, + "-ngl", String(ngl), + "-c", String(CONTEXT), + "-np", "1", + "-fa", "on", + "--cache-type-k", ctk, + "--cache-type-v", ctv, + "-ub", String(ub), + "-b", String(b), + "-t", String(t), + "-tb", String(t), + "--prio", String(prio), + "--poll", "50", + "--mlock", + "--port", "8000", + "--host", "0.0.0.0", + ]; + + if (cpuMoe) cmd.push("--cpu-moe"); + else if (nCpuMoe > 0) cmd.push("--n-cpu-moe", String(nCpuMoe)); + if (nommap) cmd.push("--no-mmap"); + + return cmd; +} + +function startServer(modelPath, params) { + const args = buildCmd(modelPath, params); + const exe = args.shift(); + log(` CMD: ${exe} ${args.slice(-12).join(" ")} ...`); + return spawn(exe, args, { + cwd: process.cwd(), + stdio: ["ignore", "pipe", "pipe"], + }); +} + +async function waitForServer(timeoutMs = SERVER_TIMEOUT) { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + try { + const resp = await fetch(`${BASE_URL}/health`, { signal: AbortSignal.timeout(3000) }); + const data = await resp.json(); + if (data.status === "ok") return { ok: true, bootTime: (Date.now() - start) / 1000 }; + } catch {} + await sleep(3000); + } + return { ok: false, bootTime: timeoutMs / 1000 }; +} + +async function runBenchmark(maxTokens = BENCHMARK_TOKENS) { + const payload = JSON.stringify({ + model: "local-model", + messages: [{ role: "user", content: "Count from 1 to 50, writing each number on a new line." }], + max_tokens: maxTokens, + temperature: 0.0, + }); + + const start = Date.now(); + const resp = await fetch(`${BASE_URL}/v1/chat/completions`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: payload, + signal: AbortSignal.timeout(600_000), + }); + const result = await resp.json(); + const elapsed = (Date.now() - start) / 1000; + + const usage = result.usage || {}; + const ct = usage.completion_tokens || 0; + return { + tps: elapsed > 0 ? ct / elapsed : 0, + completionTokens: ct, + promptTokens: usage.prompt_tokens || 0, + elapsed, + }; +} + +async function testConfig(model, label, params) { + await killServer(); + log(` [${label}] Starting server...`); + + const proc = startServer(model.path, params); + const { ok, bootTime } = await waitForServer(); + + if (!ok) { + log(` [${label}] FAILED to start (timeout ${SERVER_TIMEOUT / 1000}s)`); + proc.kill("SIGKILL"); + return null; + } + + const vram = getVramAll(); + const vramStr = vram.map((g) => `GPU${g.gpu}:${g.used}/${g.total}MiB`).join(" | "); + log(` [${label}] Boot: ${bootTime.toFixed(0)}s | VRAM: ${vramStr}`); + + // Warmup + try { await runBenchmark(20); } catch {} + + // Benchmark + const speeds = []; + for (let i = 0; i < BENCHMARK_RUNS; i++) { + try { + const r = await runBenchmark(); + speeds.push(r.tps); + log(` Run ${i + 1}: ${r.tps.toFixed(2)} t/s`); + } catch (e) { + log(` Run ${i + 1}: ERROR (${e.message})`); + } + } + + proc.kill("SIGKILL"); + + if (speeds.length === 0) { + log(` [${label}] ALL BENCHMARK RUNS FAILED`); + return null; + } + + const avg = speeds.reduce((a, b) => a + b) / speeds.length; + const best = Math.max(...speeds); + log(` [${label}] => AVG: ${avg.toFixed(2)} t/s | BEST: ${best.toFixed(2)} t/s`); + + const result = { + model: model.name, quant: model.quant, label, + avg_tps: +avg.toFixed(2), best_tps: +best.toFixed(2), + boot_time: +bootTime.toFixed(1), vram, params, + }; + ALL_RESULTS.push(result); + return result; +} + +// ─── Phase Runners ───────────────────────────────────────────── + +async function phase0_bootTest(model) { + log(`\n${"=".repeat(70)}`); + log(` PHASE 0: Boot Test — ${model.name}`); + log(`${"=".repeat(70)}`); + + // Try full GPU first + let r = await testConfig(model, "boot-ngl999", { + ngl: 999, t: 6, ub: 512, b: 2048, ctk: "q4_0", ctv: "q4_0", + }); + if (r) return r; + + // Try with cpu-moe + log(" Full GPU failed, trying with --cpu-moe..."); + r = await testConfig(model, "boot-cpumoe", { + ngl: 999, t: 6, ub: 512, b: 2048, ctk: "q4_0", ctv: "q4_0", cpuMoe: true, + }); + if (r) return r; + + // Reduced layers + log(" --cpu-moe also failed, trying reduced layers..."); + r = await testConfig(model, "boot-ngl-half", { + ngl: Math.floor(model.totalLayers / 2), t: 6, ub: 512, b: 2048, + ctk: "q4_0", ctv: "q4_0", + }); + return r; +} + +async function phase1_gpuOffload(model, baseline) { + log(`\n${"=".repeat(70)}`); + log(` PHASE 1: GPU Offload Strategy — ${model.name}`); + log(`${"=".repeat(70)}`); + + const results = baseline ? [baseline] : []; + + // Test --cpu-moe on/off + for (const cpuMoe of [true, false]) { + const lbl = `ngl=999 cpuMoe=${cpuMoe}`; + if (baseline?.params?.cpuMoe === cpuMoe && baseline.params.ngl === 999) continue; + const r = await testConfig(model, lbl, { + ngl: 999, t: 6, ub: 512, b: 2048, ctk: "q4_0", ctv: "q4_0", cpuMoe, + }); + if (r) results.push(r); + } + + // n-cpu-moe sweep + for (const n of [0, 5, 10, 15, 20]) { + if (n > model.totalLayers) continue; + const r = await testConfig(model, `n-cpu-moe=${n}`, { + ngl: 999, t: 6, ub: 512, b: 2048, ctk: "q4_0", ctv: "q4_0", nCpuMoe: n, + }); + if (r) results.push(r); + } + + if (results.length === 0) { log(" PHASE 1: No config worked!"); return null; } + const best = results.reduce((a, b) => a.avg_tps > b.avg_tps ? a : b); + log(`\n ★ Phase 1 winner: ${best.label} → ${best.avg_tps.toFixed(2)} t/s`); + return best; +} + +async function phase2_threads(model, prev) { + log(`\n${"=".repeat(70)}`); + log(` PHASE 2: CPU Thread Sweep — ${model.name}`); + log(`${"=".repeat(70)}`); + + const p = prev.params; + const results = [prev]; + + for (const t of [2, 4, 6, 8, 10, 12]) { + if (t === p.t) continue; + const r = await testConfig(model, `t=${t}`, { + ...p, t, + }); + if (r) results.push(r); + } + + const best = results.reduce((a, b) => a.avg_tps > b.avg_tps ? a : b); + log(`\n ★ Phase 2 winner: ${best.label} → ${best.avg_tps.toFixed(2)} t/s`); + return best; +} + +async function phase3_batch(model, prev) { + log(`\n${"=".repeat(70)}`); + log(` PHASE 3: Batch Size Sweep — ${model.name}`); + log(`${"=".repeat(70)}`); + + const p = prev.params; + const results = [prev]; + + for (const [ub, b] of [ + [128, 512], [256, 1024], [256, 2048], + [512, 1024], [512, 2048], [512, 4096], + [1024, 2048], [1024, 4096], + ]) { + if (ub === p.ub && b === p.b) continue; + const r = await testConfig(model, `ub=${ub} b=${b}`, { ...p, ub, b }); + if (r) results.push(r); + } + + const best = results.reduce((a, b) => a.avg_tps > b.avg_tps ? a : b); + log(`\n ★ Phase 3 winner: ${best.label} → ${best.avg_tps.toFixed(2)} t/s`); + return best; +} + +async function phase4_kvcache(model, prev) { + log(`\n${"=".repeat(70)}`); + log(` PHASE 4: KV Cache Type Sweep — ${model.name}`); + log(`${"=".repeat(70)}`); + + const p = prev.params; + const results = [prev]; + + for (const [ctk, ctv] of [ + ["q4_0", "q4_0"], ["q8_0", "q8_0"], + ["q4_0", "q8_0"], ["f16", "f16"], + ]) { + if (ctk === p.ctk && ctv === p.ctv) continue; + const r = await testConfig(model, `kv=${ctk}/${ctv}`, { ...p, ctk, ctv }); + if (r) results.push(r); + } + + const best = results.reduce((a, b) => a.avg_tps > b.avg_tps ? a : b); + log(`\n ★ Phase 4 winner: ${best.label} → ${best.avg_tps.toFixed(2)} t/s`); + return best; +} + +async function phase5_final(model, prev) { + log(`\n${"=".repeat(70)}`); + log(` PHASE 5: Final Verification (5 runs) — ${model.name}`); + log(`${"=".repeat(70)}`); + + await killServer(); + const proc = startServer(model.path, prev.params); + const { ok, bootTime } = await waitForServer(); + if (!ok) { log(" FAILED to start!"); proc.kill("SIGKILL"); return prev; } + + const vram = getVramAll(); + try { await runBenchmark(20); } catch {} + + const speeds = []; + for (let i = 0; i < 5; i++) { + try { + const r = await runBenchmark(); + speeds.push(r.tps); + log(` Final Run ${i + 1}: ${r.tps.toFixed(2)} t/s`); + } catch (e) { + log(` Final Run ${i + 1}: ERROR (${e.message})`); + } + } + proc.kill("SIGKILL"); + + if (speeds.length > 0) { + const avg = speeds.reduce((a, b) => a + b) / speeds.length; + const best = Math.max(...speeds); + log(`\n ★ FINAL: AVG ${avg.toFixed(2)} t/s | BEST ${best.toFixed(2)} t/s`); + + const final_ = { + model: model.name, quant: model.quant, + label: `FINAL-${model.name}`, + avg_tps: +avg.toFixed(2), best_tps: +best.toFixed(2), + boot_time: +bootTime.toFixed(1), vram, params: prev.params, + }; + ALL_RESULTS.push(final_); + return final_; + } + return prev; +} + +// ─── Main ────────────────────────────────────────────────────── + +async function runModelBenchmark(model) { + log(`\n${"#".repeat(70)}`); + log(` MODEL: ${model.name}`); + log(` File: ${model.path}`); + try { + const sz = statSync(model.path).size / 1024 ** 3; + log(` Size: ${sz.toFixed(2)} GB`); + } catch { log(` Size: unknown`); } + log(`${"#".repeat(70)}`); + + if (!existsSync(model.path)) { + log(` SKIP: Model file not found!`); + return null; + } + + const baseline = await phase0_bootTest(model); + if (!baseline) { log(` SKIP: Cannot boot at 256K!`); return null; } + + let best = await phase1_gpuOffload(model, baseline); + if (!best) return baseline; + + best = await phase2_threads(model, best); + best = await phase3_batch(model, best); + best = await phase4_kvcache(model, best); + best = await phase5_final(model, best); + + return best; +} + +async function main() { + const startTime = Date.now(); + + log("=".repeat(70)); + log(" DUAL-GPU COMPREHENSIVE MODEL BENCHMARK"); + log(" 2x RTX 3060 (24GB Total) | 256K Context"); + log(` Models: ${MODELS.length}`); + log(` Started: ${new Date().toISOString()}`); + log("=".repeat(70)); + + const gpus = getVramAll(); + gpus.forEach((g) => log(` GPU ${g.gpu}: ${g.used}/${g.total} MiB used`)); + + const winners = []; + + for (let i = 0; i < MODELS.length; i++) { + log(`\n${"=".repeat(70)}`); + log(` STARTING MODEL ${i + 1}/${MODELS.length}: ${MODELS[i].name}`); + log(`${"=".repeat(70)}`); + + const winner = await runModelBenchmark(MODELS[i]); + if (winner) winners.push(winner); + + // Save intermediate + writeFileSync("scripts/dual_gpu_results.json", + JSON.stringify(ALL_RESULTS, null, 2)); + log(` Intermediate saved (${ALL_RESULTS.length} configs tested)`); + } + + // ─── Grand Final ─────────────────────────────────────────── + const elapsed = (Date.now() - startTime) / 60000; + + log(`\n${"=".repeat(70)}`); + log(` GRAND FINAL COMPARISON`); + log(` Total time: ${elapsed.toFixed(1)} minutes`); + log(` Configs tested: ${ALL_RESULTS.length}`); + log(`${"=".repeat(70)}`); + + if (winners.length === 0) { + log(" No models ran at 256K!"); + return; + } + + winners.sort((a, b) => b.avg_tps - a.avg_tps); + const medals = ["🥇", "🥈", "🥉", " "]; + + const lines = [ + `Dual-GPU Benchmark Results — ${new Date().toISOString()}`, + `Hardware: 2x RTX 3060 12GB | Context: 256K`, + `Configs tested: ${ALL_RESULTS.length} | Time: ${elapsed.toFixed(1)} min`, + "", "=".repeat(60), " RANKING (by AVG t/s)", "=".repeat(60), + ]; + + for (let i = 0; i < winners.length; i++) { + const w = winners[i]; + const p = w.params; + lines.push(""); + lines.push(` ${medals[i] || " "} #${i + 1}: ${w.model}`); + lines.push(` AVG: ${w.avg_tps.toFixed(2)} t/s | BEST: ${w.best_tps.toFixed(2)} t/s`); + lines.push(` Boot: ${w.boot_time.toFixed(0)}s`); + lines.push(` ngl=${p.ngl} t=${p.t} ub=${p.ub} b=${p.b}`); + lines.push(` ctk=${p.ctk} ctv=${p.ctv}`); + if (p.cpuMoe) lines.push(` --cpu-moe`); + else if ((p.nCpuMoe || 0) > 0) lines.push(` --n-cpu-moe ${p.nCpuMoe}`); + } + + const champ = winners[0]; + const cp = champ.params; + lines.push("", "=".repeat(60)); + lines.push(` ★ CHAMPION: ${champ.model}`); + lines.push(` ${champ.avg_tps.toFixed(2)} t/s average`); + lines.push("=".repeat(60)); + + // Build recommended command + const cmdParts = [ + `llama-server --model ${MODELS.find((m) => m.name === champ.model).path}`, + `-ngl ${cp.ngl} -c ${CONTEXT}`, + `-t ${cp.t} -tb ${cp.t}`, + `-ub ${cp.ub} -b ${cp.b}`, + `-fa on`, + `--cache-type-k ${cp.ctk} --cache-type-v ${cp.ctv}`, + `--prio ${cp.prio || 3} --poll 50`, + `--mlock`, + ]; + if (cp.cpuMoe) cmdParts.push("--cpu-moe"); + else if ((cp.nCpuMoe || 0) > 0) cmdParts.push(`--n-cpu-moe ${cp.nCpuMoe}`); + if (cp.nommap) cmdParts.push("--no-mmap"); + cmdParts.push("--port 8000 --host 0.0.0.0"); + + lines.push("", " Recommended command:"); + lines.push(` ${cmdParts.join(" ")}`); + + const summary = lines.join("\n"); + console.log(summary); + writeFileSync("scripts/dual_gpu_summary.txt", summary, "utf-8"); + writeFileSync("scripts/dual_gpu_results.json", + JSON.stringify(ALL_RESULTS, null, 2)); + + log(`\n Results: scripts/dual_gpu_results.json`); + log(` Summary: scripts/dual_gpu_summary.txt`); + log(` DONE!`); + + await killServer(); +} + +main().catch((e) => { + console.error("Fatal error:", e); + process.exit(1); +}); diff --git a/scripts/dual_gpu_benchmark.py b/scripts/dual_gpu_benchmark.py new file mode 100644 index 0000000..4dd4089 --- /dev/null +++ b/scripts/dual_gpu_benchmark.py @@ -0,0 +1,644 @@ +""" +Dual-GPU (2x RTX 3060 24GB) Comprehensive Model Benchmark +========================================================== +Tests 4 models across multiple parameter configurations to find +the absolute best model + settings for 256K context coding agent. + +Models: + 1. Qwen3.5-35B-A3B Q4_K_M (~20.5 GB) + 2. Qwen3.5-35B-A3B MXFP4_MOE (~20.1 GB) + 3. Gemma4 26B-A4B Q4_K_M (~15.6 GB) + 4. Gemma4 26B-A4B MXFP4_MOE (~15.5 GB) + +Test Phases (per model): + Phase 0: Basic dual-GPU startup test (can it even boot at 256K?) + Phase 1: GPU layer + MoE offload strategy sweep + Phase 2: CPU thread sweep (carry best from P1) + Phase 3: Batch size sweep (carry best from P1+P2) + Phase 4: KV cache type sweep (carry best from P1+P2+P3) + Phase 5: Final verification (5 runs) + +Output: scripts/dual_gpu_results.json (all raw data) + scripts/dual_gpu_summary.txt (human-readable winner) +""" +import subprocess +import time +import json +import urllib.request +import sys +import os +import datetime + +try: + sys.stdout.reconfigure(encoding='utf-8') +except Exception: + pass + +# ─── Configuration ─────────────────────────────────────────────── +BASE_URL = "http://127.0.0.1:8000" +LLAMA_SERVER = r"llama_bin_run\llama-server.exe" +CONTEXT = 262144 # 256K +BENCHMARK_RUNS = 3 +BENCHMARK_TOKENS = 200 +SERVER_TIMEOUT = 300 # seconds to wait for server startup + +MODELS = [ + { + "name": "Qwen3.5-35B-A3B Q4_K_M", + "path": r"models\Qwen3.5-35B-A3B-Q4_K_M.gguf", + "type": "qwen", + "quant": "Q4_K_M", + "is_mxfp4": False, + "total_layers": 64, # Qwen3.5 35B has 64 layers + }, + { + "name": "Qwen3.5-35B-A3B MXFP4_MOE", + "path": r"models\Qwen3.5-35B-A3B-MXFP4_MOE.gguf", + "type": "qwen", + "quant": "MXFP4_MOE", + "is_mxfp4": True, + "total_layers": 64, + }, + { + "name": "Gemma4 26B-A4B Q4_K_M", + "path": r"models\gemma-4-26B-A4B-it-Q4_K_M.gguf", + "type": "gemma4", + "quant": "Q4_K_M", + "is_mxfp4": False, + "total_layers": 30, # Gemma4 26B has 30 layers + }, + { + "name": "Gemma4 26B-A4B MXFP4_MOE", + "path": r"models\gemma-4-26B-A4B-it-MXFP4_MOE.gguf", + "type": "gemma4", + "quant": "MXFP4_MOE", + "is_mxfp4": True, + "total_layers": 30, + }, +] + +ALL_RESULTS = [] + + +# ─── Utility Functions ────────────────────────────────────────── +def log(msg): + ts = datetime.datetime.now().strftime("%H:%M:%S") + print(f"[{ts}] {msg}", flush=True) + + +def kill_server(): + subprocess.run(["taskkill", "/F", "/IM", "llama-server.exe"], + capture_output=True) + time.sleep(5) + + +def get_vram_all(): + """Returns list of (used, total) tuples for each GPU.""" + try: + r = subprocess.run( + ["nvidia-smi", "--query-gpu=index,memory.used,memory.total", + "--format=csv,noheader,nounits"], + capture_output=True, text=True, timeout=5 + ) + gpus = [] + for line in r.stdout.strip().split("\n"): + parts = [p.strip() for p in line.split(",")] + if len(parts) >= 3: + gpus.append({ + "gpu": int(parts[0]), + "used": int(parts[1]), + "total": int(parts[2]), + }) + return gpus + except Exception: + return [] + + +def build_cmd(model_path, ngl, t, ub, b, ctk, ctv, + cpu_moe=False, n_cpu_moe=0, prio=3, nommap=False): + """Build llama-server command for dual-GPU.""" + cmd = [ + LLAMA_SERVER, + "--model", model_path, + "-ngl", str(ngl), + "-c", str(CONTEXT), + "-np", "1", + "-fa", "on", + "--cache-type-k", ctk, + "--cache-type-v", ctv, + "-ub", str(ub), + "-b", str(b), + "-t", str(t), + "-tb", str(t), + "--prio", str(prio), + "--poll", "50", + "--mlock", + "--port", "8000", + "--host", "0.0.0.0", + ] + # MoE offloading options + if cpu_moe: + cmd.append("--cpu-moe") + elif n_cpu_moe > 0: + cmd.extend(["--n-cpu-moe", str(n_cpu_moe)]) + if nommap: + cmd.append("--no-mmap") + return cmd + + +def start_server(model_path, **kwargs): + cmd = build_cmd(model_path, **kwargs) + log(f" CMD: {' '.join(cmd[-20:])}") # show last 20 args + proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + cwd=os.getcwd(), text=True, encoding='utf-8', errors='replace' + ) + return proc + + +def wait_for_server(timeout=SERVER_TIMEOUT): + start = time.time() + while time.time() - start < timeout: + try: + req = urllib.request.Request(f"{BASE_URL}/health") + with urllib.request.urlopen(req, timeout=3) as resp: + data = json.loads(resp.read()) + if data.get("status") == "ok": + boot_time = time.time() - start + return True, boot_time + except Exception: + pass + time.sleep(3) + return False, timeout + + +def run_benchmark(max_tokens=BENCHMARK_TOKENS): + payload = json.dumps({ + "model": "local-model", + "messages": [{"role": "user", + "content": "Count from 1 to 50, writing each number on a new line."}], + "max_tokens": max_tokens, + "temperature": 0.0, + }).encode("utf-8") + + req = urllib.request.Request( + f"{BASE_URL}/v1/chat/completions", + data=payload, + headers={"Content-Type": "application/json"}, + ) + + start = time.time() + with urllib.request.urlopen(req, timeout=600) as resp: + result = json.loads(resp.read()) + elapsed = time.time() - start + + usage = result.get("usage", {}) + ct = usage.get("completion_tokens", 0) + pt = usage.get("prompt_tokens", 0) + return { + "tps": ct / elapsed if elapsed > 0 else 0, + "completion_tokens": ct, + "prompt_tokens": pt, + "elapsed": elapsed, + } + + +def test_config(model_info, label, **kwargs): + """Test a single configuration. Returns result dict or None.""" + kill_server() + log(f" [{label}] Starting server...") + + proc = start_server(model_info["path"], **kwargs) + ok, boot_time = wait_for_server() + + if not ok: + log(f" [{label}] FAILED to start (timeout {SERVER_TIMEOUT}s)") + proc.kill() + return None + + vram = get_vram_all() + vram_str = " | ".join(f"GPU{g['gpu']}:{g['used']}/{g['total']}MiB" for g in vram) + log(f" [{label}] Boot: {boot_time:.0f}s | VRAM: {vram_str}") + + # Warmup + try: + run_benchmark(max_tokens=20) + except Exception: + pass + + # Benchmark runs + speeds = [] + for i in range(BENCHMARK_RUNS): + try: + r = run_benchmark() + speeds.append(r["tps"]) + log(f" Run {i+1}: {r['tps']:.2f} t/s") + except Exception as e: + log(f" Run {i+1}: ERROR ({e})") + + proc.kill() + + if not speeds: + log(f" [{label}] ALL BENCHMARK RUNS FAILED") + return None + + avg = sum(speeds) / len(speeds) + best = max(speeds) + log(f" [{label}] => AVG: {avg:.2f} t/s | BEST: {best:.2f} t/s") + + result = { + "model": model_info["name"], + "quant": model_info["quant"], + "label": label, + "avg_tps": round(avg, 2), + "best_tps": round(best, 2), + "boot_time": round(boot_time, 1), + "vram": vram, + "params": kwargs, + } + ALL_RESULTS.append(result) + return result + + +# ─── Phase Runners ─────────────────────────────────────────────── + +def phase0_boot_test(model): + """Quick test: can the model even boot with 256K on dual GPU?""" + log(f"\n{'='*70}") + log(f" PHASE 0: Boot Test — {model['name']}") + log(f"{'='*70}") + + # Try -ngl 999 (all layers to GPU) as baseline + r = test_config( + model, f"boot-ngl999", + ngl=999, t=6, ub=512, b=2048, ctk="q4_0", ctv="q4_0", + ) + if r: + return r + + # If full GPU fails, try with cpu-moe + log(" Full GPU failed, trying with --cpu-moe...") + r = test_config( + model, f"boot-cpumoe", + ngl=999, t=6, ub=512, b=2048, ctk="q4_0", ctv="q4_0", + cpu_moe=True, + ) + if r: + return r + + # Extreme fallback: fewer layers + log(" --cpu-moe also failed, trying reduced layers...") + r = test_config( + model, f"boot-ngl-half", + ngl=model["total_layers"] // 2, t=6, ub=512, b=2048, + ctk="q4_0", ctv="q4_0", + ) + return r + + +def phase1_gpu_offload(model, baseline): + """Find optimal GPU layer count and MoE offload strategy.""" + log(f"\n{'='*70}") + log(f" PHASE 1: GPU Offload Strategy — {model['name']}") + log(f"{'='*70}") + + results = [] + if baseline: + results.append(baseline) + + total = model["total_layers"] + + # Strategy A: All GPU + cpu-moe variations + for cpu_moe in [True, False]: + label = f"ngl=999 cpu_moe={cpu_moe}" + # Skip if already tested in baseline + if baseline and baseline["label"] in [f"boot-ngl999", f"boot-cpumoe"] and \ + baseline["params"].get("cpu_moe", False) == cpu_moe: + continue + r = test_config( + model, label, + ngl=999, t=6, ub=512, b=2048, ctk="q4_0", ctv="q4_0", + cpu_moe=cpu_moe, + ) + if r: + results.append(r) + + # Strategy B: n-cpu-moe sweep (selective expert offload) + for n in [0, 5, 10, 15, 20]: + if n > total: + continue + r = test_config( + model, f"n-cpu-moe={n}", + ngl=999, t=6, ub=512, b=2048, ctk="q4_0", ctv="q4_0", + n_cpu_moe=n, + ) + if r: + results.append(r) + + if not results: + log(" PHASE 1: No configuration worked!") + return None + + best = max(results, key=lambda x: x["avg_tps"]) + log(f"\n ★ Phase 1 winner: {best['label']} → {best['avg_tps']:.2f} t/s") + return best + + +def phase2_threads(model, prev_best): + """Sweep CPU threads with best GPU config locked.""" + log(f"\n{'='*70}") + log(f" PHASE 2: CPU Thread Sweep — {model['name']}") + log(f"{'='*70}") + + p = prev_best["params"] + results = [prev_best] + + for t in [2, 4, 6, 8, 10, 12]: + if t == p.get("t", 6): + continue + r = test_config( + model, f"t={t}", + ngl=p["ngl"], t=t, ub=p["ub"], b=p["b"], + ctk=p["ctk"], ctv=p["ctv"], + cpu_moe=p.get("cpu_moe", False), + n_cpu_moe=p.get("n_cpu_moe", 0), + ) + if r: + results.append(r) + + best = max(results, key=lambda x: x["avg_tps"]) + log(f"\n ★ Phase 2 winner: {best['label']} → {best['avg_tps']:.2f} t/s") + return best + + +def phase3_batch(model, prev_best): + """Sweep batch sizes.""" + log(f"\n{'='*70}") + log(f" PHASE 3: Batch Size Sweep — {model['name']}") + log(f"{'='*70}") + + p = prev_best["params"] + best_t = p["t"] + results = [prev_best] + + for ub, b in [(128, 512), (256, 1024), (256, 2048), + (512, 1024), (512, 2048), (512, 4096), + (1024, 2048), (1024, 4096)]: + if ub == p["ub"] and b == p["b"]: + continue + r = test_config( + model, f"ub={ub} b={b}", + ngl=p["ngl"], t=best_t, ub=ub, b=b, + ctk=p["ctk"], ctv=p["ctv"], + cpu_moe=p.get("cpu_moe", False), + n_cpu_moe=p.get("n_cpu_moe", 0), + ) + if r: + results.append(r) + + best = max(results, key=lambda x: x["avg_tps"]) + log(f"\n ★ Phase 3 winner: {best['label']} → {best['avg_tps']:.2f} t/s") + return best + + +def phase4_kvcache(model, prev_best): + """Sweep KV cache precision.""" + log(f"\n{'='*70}") + log(f" PHASE 4: KV Cache Type Sweep — {model['name']}") + log(f"{'='*70}") + + p = prev_best["params"] + results = [prev_best] + + for ctk, ctv in [("q4_0", "q4_0"), ("q8_0", "q8_0"), + ("q4_0", "q8_0"), ("f16", "f16")]: + if ctk == p["ctk"] and ctv == p["ctv"]: + continue + r = test_config( + model, f"kv={ctk}/{ctv}", + ngl=p["ngl"], t=p["t"], ub=p["ub"], b=p["b"], + ctk=ctk, ctv=ctv, + cpu_moe=p.get("cpu_moe", False), + n_cpu_moe=p.get("n_cpu_moe", 0), + ) + if r: + results.append(r) + + best = max(results, key=lambda x: x["avg_tps"]) + log(f"\n ★ Phase 4 winner: {best['label']} → {best['avg_tps']:.2f} t/s") + return best + + +def phase5_final(model, prev_best): + """Final verification with 5 runs.""" + log(f"\n{'='*70}") + log(f" PHASE 5: Final Verification (5 runs) — {model['name']}") + log(f"{'='*70}") + + p = prev_best["params"] + kill_server() + proc = start_server(model["path"], **p) + ok, boot_time = wait_for_server() + if not ok: + log(" FAILED to start for final verification!") + proc.kill() + return prev_best + + vram = get_vram_all() + + # Warmup + try: + run_benchmark(max_tokens=20) + except Exception: + pass + + speeds = [] + for i in range(5): + try: + r = run_benchmark() + speeds.append(r["tps"]) + log(f" Final Run {i+1}: {r['tps']:.2f} t/s") + except Exception as e: + log(f" Final Run {i+1}: ERROR ({e})") + + proc.kill() + + if speeds: + avg = sum(speeds) / len(speeds) + best_tps = max(speeds) + log(f"\n ★ FINAL: AVG {avg:.2f} t/s | BEST {best_tps:.2f} t/s") + + final = { + "model": model["name"], + "quant": model["quant"], + "label": f"FINAL-{model['name']}", + "avg_tps": round(avg, 2), + "best_tps": round(best_tps, 2), + "boot_time": round(boot_time, 1), + "vram": vram, + "params": p, + } + ALL_RESULTS.append(final) + return final + + return prev_best + + +# ─── Main ──────────────────────────────────────────────────────── + +def run_full_benchmark_for_model(model): + """Run all phases for a single model.""" + log(f"\n{'#'*70}") + log(f" MODEL: {model['name']}") + log(f" File: {model['path']}") + log(f" Size: {os.path.getsize(model['path'])/1024**3:.2f} GB") + log(f"{'#'*70}") + + # Check model exists + if not os.path.exists(model["path"]): + log(f" SKIP: Model file not found!") + return None + + # Phase 0: Can it boot? + baseline = phase0_boot_test(model) + if not baseline: + log(f" SKIP: {model['name']} cannot boot at 256K context!") + return None + + # Phase 1: GPU offload strategy + best = phase1_gpu_offload(model, baseline) + if not best: + return baseline + + # Phase 2: CPU threads + best = phase2_threads(model, best) + + # Phase 3: Batch sizes + best = phase3_batch(model, best) + + # Phase 4: KV cache + best = phase4_kvcache(model, best) + + # Phase 5: Final verification + final = phase5_final(model, best) + + return final + + +def main(): + start_time = time.time() + + log("=" * 70) + log(" DUAL-GPU COMPREHENSIVE MODEL BENCHMARK") + log(" 2x RTX 3060 (24GB Total) | 256K Context") + log(f" Models: {len(MODELS)}") + log(f" Started: {datetime.datetime.now().isoformat()}") + log("=" * 70) + + # Show GPU info + gpus = get_vram_all() + for g in gpus: + log(f" GPU {g['gpu']}: {g['used']}/{g['total']} MiB used") + + # Run benchmarks for each model + model_winners = [] + for i, model in enumerate(MODELS): + log(f"\n{'='*70}") + log(f" STARTING MODEL {i+1}/{len(MODELS)}: {model['name']}") + log(f"{'='*70}") + + winner = run_full_benchmark_for_model(model) + if winner: + model_winners.append(winner) + + # Save intermediate results + with open("scripts/dual_gpu_results.json", "w") as f: + json.dump(ALL_RESULTS, f, indent=2, default=str) + log(f" Intermediate results saved ({len(ALL_RESULTS)} configs tested)") + + # ─── Grand Final Comparison ────────────────────────────────── + elapsed = (time.time() - start_time) / 60 + + log(f"\n{'='*70}") + log(f" GRAND FINAL COMPARISON") + log(f" Total time: {elapsed:.1f} minutes") + log(f" Configs tested: {len(ALL_RESULTS)}") + log(f"{'='*70}") + + if not model_winners: + log(" No models were able to run at 256K context!") + return + + # Sort by avg t/s + model_winners.sort(key=lambda x: x["avg_tps"], reverse=True) + + summary_lines = [] + summary_lines.append(f"Dual-GPU Benchmark Results — {datetime.datetime.now().isoformat()}") + summary_lines.append(f"Hardware: 2x RTX 3060 12GB | Context: 256K") + summary_lines.append(f"Total configs tested: {len(ALL_RESULTS)}") + summary_lines.append(f"Total time: {elapsed:.1f} minutes") + summary_lines.append("") + summary_lines.append("=" * 60) + summary_lines.append(" RANKING (by AVG t/s)") + summary_lines.append("=" * 60) + + for rank, w in enumerate(model_winners, 1): + medal = {1: "🥇", 2: "🥈", 3: "🥉", 4: " "}.get(rank, " ") + summary_lines.append(f"\n {medal} #{rank}: {w['model']}") + summary_lines.append(f" AVG: {w['avg_tps']:.2f} t/s | BEST: {w['best_tps']:.2f} t/s") + summary_lines.append(f" Boot: {w['boot_time']:.0f}s") + p = w["params"] + summary_lines.append(f" ngl={p['ngl']} t={p['t']} ub={p['ub']} b={p['b']}") + summary_lines.append(f" ctk={p['ctk']} ctv={p['ctv']}") + if p.get("cpu_moe"): + summary_lines.append(f" --cpu-moe") + elif p.get("n_cpu_moe", 0) > 0: + summary_lines.append(f" --n-cpu-moe {p['n_cpu_moe']}") + + champion = model_winners[0] + summary_lines.append(f"\n{'='*60}") + summary_lines.append(f" ★ CHAMPION: {champion['model']}") + summary_lines.append(f" {champion['avg_tps']:.2f} t/s average") + summary_lines.append(f"{'='*60}") + + # Build recommended command + p = champion["params"] + cmd_parts = [ + f"llama-server --model {MODELS[[m['name'] for m in MODELS].index(champion['model'])]['path']}", + f"-ngl {p['ngl']} -c {CONTEXT}", + f"-t {p['t']} -tb {p['t']}", + f"-ub {p['ub']} -b {p['b']}", + "-fa on", + f"--cache-type-k {p['ctk']} --cache-type-v {p['ctv']}", + f"--prio {p.get('prio', 3)} --poll 50", + "--mlock", + ] + if p.get("cpu_moe"): + cmd_parts.append("--cpu-moe") + elif p.get("n_cpu_moe", 0) > 0: + cmd_parts.append(f"--n-cpu-moe {p['n_cpu_moe']}") + if p.get("nommap"): + cmd_parts.append("--no-mmap") + cmd_parts.append("--port 8000 --host 0.0.0.0") + + summary_lines.append(f"\n Recommended command:") + summary_lines.append(f" {' '.join(cmd_parts)}") + + summary = "\n".join(summary_lines) + print(summary) + + with open("scripts/dual_gpu_summary.txt", "w", encoding="utf-8") as f: + f.write(summary) + + with open("scripts/dual_gpu_results.json", "w") as f: + json.dump(ALL_RESULTS, f, indent=2, default=str) + + log(f"\n Results: scripts/dual_gpu_results.json") + log(f" Summary: scripts/dual_gpu_summary.txt") + log(f" DONE!") + + kill_server() + + +if __name__ == "__main__": + main() diff --git a/scripts/dual_gpu_benchmark_v2.mjs b/scripts/dual_gpu_benchmark_v2.mjs new file mode 100644 index 0000000..a19499d --- /dev/null +++ b/scripts/dual_gpu_benchmark_v2.mjs @@ -0,0 +1,330 @@ +/** + * Dual-GPU (2x RTX 3060 24GB) Smart Model Benchmark v2 + * ===================================================== + * Informed by VRAM analysis — tests models in optimal order. + * + * Key insights applied: + * - Gemma4 fits entirely in 24GB GPU (KV cache ~0.18 GB with SWA) + * - Qwen3.5 is tight (~22.5-22.9 GB needed) — try full GPU first + * - Skip configs known to fail, minimize wasted time + * + * Run: node scripts/dual_gpu_benchmark_v2.mjs + * Results: scripts/dual_gpu_results.json + scripts/dual_gpu_summary.txt + */ + +import { spawn, execSync } from "child_process"; +import { writeFileSync, existsSync, statSync } from "fs"; + +const BASE_URL = "http://127.0.0.1:8000"; +const LLAMA = String.raw`llama_bin_run\llama-server.exe`; +const CTX = 262144; +const RUNS = 3; +const TOKENS = 200; +const BOOT_TIMEOUT = 300_000; + +// Models ordered: smallest first (most likely to succeed fully on GPU) +const MODELS = [ + { + name: "Gemma4-26B MXFP4_MOE", + path: String.raw`models\gemma-4-26B-A4B-it-MXFP4_MOE.gguf`, + quant: "MXFP4_MOE", + fitsGPU: true, // 15.5 + 0.18 + 1 = 16.72 GB << 23 GB + }, + { + name: "Gemma4-26B Q4_K_M", + path: String.raw`models\gemma-4-26B-A4B-it-Q4_K_M.gguf`, + quant: "Q4_K_M", + fitsGPU: true, // 15.6 + 0.18 + 1 = 16.82 GB << 23 GB + }, + { + name: "Qwen3.5-35B MXFP4_MOE", + path: String.raw`models\Qwen3.5-35B-A3B-MXFP4_MOE.gguf`, + quant: "MXFP4_MOE", + fitsGPU: "maybe", // 20.1 + 1.41 + 1 = 22.51 GB — tight + }, + { + name: "Qwen3.5-35B Q4_K_M", + path: String.raw`models\Qwen3.5-35B-A3B-Q4_K_M.gguf`, + quant: "Q4_K_M", + fitsGPU: "maybe", // 20.5 + 1.41 + 1 = 22.91 GB — very tight + }, +]; + +const ALL = []; +let currentProc = null; + +// ─── Utilities ───────────────────────────────────────────────── +const log = (m) => console.log(`[${new Date().toLocaleTimeString("ko-KR",{hour12:false})}] ${m}`); +const sleep = (ms) => new Promise(r => setTimeout(r, ms)); + +async function kill() { + if (currentProc) { try { currentProc.kill("SIGKILL"); } catch {} currentProc = null; } + try { execSync("taskkill /F /IM llama-server.exe", { stdio: "ignore" }); } catch {} + await sleep(5000); +} + +function vram() { + try { + return execSync('nvidia-smi --query-gpu=index,memory.used,memory.total --format=csv,noheader,nounits', + { encoding: "utf-8", timeout: 5000 }).trim().split("\n").map(l => { + const [g, u, t] = l.split(",").map(s => parseInt(s)); + return { gpu: g, used: u, total: t }; + }); + } catch { return []; } +} + +function startServer(modelPath, p) { + const args = [ + "--model", modelPath, "-ngl", String(p.ngl), + "-c", String(CTX), "-np", "1", "-fa", "on", + "--cache-type-k", p.ctk, "--cache-type-v", p.ctv, + "-ub", String(p.ub), "-b", String(p.b), + "-t", String(p.t), "-tb", String(p.t), + "--prio", String(p.prio || 3), "--poll", "50", "--mlock", + "--port", "8000", "--host", "0.0.0.0", + ]; + if (p.cpuMoe) args.push("--cpu-moe"); + else if ((p.nCpuMoe || 0) > 0) args.push("--n-cpu-moe", String(p.nCpuMoe)); + if (p.nommap) args.push("--no-mmap"); + + currentProc = spawn(LLAMA, args, { cwd: process.cwd(), stdio: ["ignore", "pipe", "pipe"] }); + return currentProc; +} + +async function waitReady(timeout = BOOT_TIMEOUT) { + const t0 = Date.now(); + while (Date.now() - t0 < timeout) { + try { + const r = await fetch(`${BASE_URL}/health`, { signal: AbortSignal.timeout(3000) }); + const d = await r.json(); + if (d.status === "ok") return { ok: true, boot: (Date.now() - t0) / 1000 }; + } catch {} + await sleep(3000); + } + return { ok: false, boot: timeout / 1000 }; +} + +async function bench(n = TOKENS) { + const t0 = Date.now(); + const r = await fetch(`${BASE_URL}/v1/chat/completions`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: "m", + messages: [{ role: "user", content: "Count from 1 to 50, each on new line." }], + max_tokens: n, temperature: 0, + }), + signal: AbortSignal.timeout(600_000), + }); + const d = await r.json(); + const dt = (Date.now() - t0) / 1000; + const ct = d.usage?.completion_tokens || 0; + return { tps: ct / dt, ct, dt }; +} + +async function testConfig(model, label, params) { + await kill(); + log(` [${label}] Starting...`); + startServer(model.path, params); + const { ok, boot } = await waitReady(); + if (!ok) { log(` [${label}] ✗ FAILED (timeout)`); await kill(); return null; } + + const v = vram(); + const vs = v.map(g => `GPU${g.gpu}:${g.used}/${g.total}`).join(" | "); + log(` [${label}] Boot:${boot.toFixed(0)}s | VRAM: ${vs}`); + + try { await bench(20); } catch {} // warmup + + const speeds = []; + for (let i = 0; i < RUNS; i++) { + try { const r = await bench(); speeds.push(r.tps); log(` Run${i+1}: ${r.tps.toFixed(2)} t/s`); + } catch (e) { log(` Run${i+1}: ERR ${e.message}`); } + } + await kill(); + + if (!speeds.length) { log(` [${label}] ✗ ALL RUNS FAILED`); return null; } + const avg = speeds.reduce((a,b)=>a+b) / speeds.length; + const best = Math.max(...speeds); + log(` [${label}] ⇒ AVG:${avg.toFixed(2)} BEST:${best.toFixed(2)} t/s`); + + const res = { model: model.name, quant: model.quant, label, + avg_tps: +avg.toFixed(2), best_tps: +best.toFixed(2), + boot: +boot.toFixed(1), vram: v, params }; + ALL.push(res); + return res; +} + +// Save intermediate results after each test +function saveIntermediate() { + writeFileSync("scripts/dual_gpu_results.json", JSON.stringify(ALL, null, 2)); +} + +// ─── Smart Phase Runner ──────────────────────────────────────── + +async function tuneModel(model) { + log(`\n${"#".repeat(65)}`); + log(` ${model.name} (${model.quant})`); + if (!existsSync(model.path)) { log(" ✗ File not found, SKIP"); return null; } + const sz = (statSync(model.path).size / 1024**3).toFixed(2); + log(` Size: ${sz} GB | Fits GPU: ${model.fitsGPU}`); + log(`${"#".repeat(65)}`); + + // ── Step 1: Find working GPU config ── + log(`\n ── Step 1: Find optimal GPU offload ──`); + let baseline = null; + + if (model.fitsGPU === true || model.fitsGPU === "maybe") { + // Try full GPU, no CPU offload + baseline = await testConfig(model, "ngl=999 pure-GPU", { + ngl: 999, t: 6, ub: 512, b: 2048, ctk: "q4_0", ctv: "q4_0" }); + saveIntermediate(); + } + + if (!baseline) { + // Try n-cpu-moe values (ascending — find minimum needed) + for (const n of [5, 10, 15, 20]) { + baseline = await testConfig(model, `n-cpu-moe=${n}`, { + ngl: 999, t: 6, ub: 512, b: 2048, ctk: "q4_0", ctv: "q4_0", nCpuMoe: n }); + saveIntermediate(); + if (baseline) break; // found minimum working offload + } + } + + if (!baseline) { + // Last resort: full cpu-moe + baseline = await testConfig(model, "cpu-moe", { + ngl: 999, t: 6, ub: 512, b: 2048, ctk: "q4_0", ctv: "q4_0", cpuMoe: true }); + saveIntermediate(); + } + + if (!baseline) { log(` ✗ ${model.name} cannot boot at 256K!`); return null; } + + const bp = baseline.params; // carry forward best params + + // If pure GPU worked, also test cpu-moe to compare (it might be faster due to memory) + if (!bp.cpuMoe && !bp.nCpuMoe) { + const alt = await testConfig(model, "compare: cpu-moe", { + ...bp, cpuMoe: true }); + saveIntermediate(); + if (alt && alt.avg_tps > baseline.avg_tps) { baseline = alt; } + } + + let best = baseline; + + // ── Step 2: Thread sweep ── + log(`\n ── Step 2: Thread sweep ──`); + for (const t of [2, 4, 8, 10, 12]) { + if (t === best.params.t) continue; + const r = await testConfig(model, `t=${t}`, { ...best.params, t }); + saveIntermediate(); + if (r && r.avg_tps > best.avg_tps) best = r; + } + + // ── Step 3: Batch sweep ── + log(`\n ── Step 3: Batch sweep ──`); + for (const [ub, b] of [[256, 1024], [256, 2048], [512, 2048], [512, 4096], [1024, 2048], [1024, 4096]]) { + if (ub === best.params.ub && b === best.params.b) continue; + const r = await testConfig(model, `ub=${ub} b=${b}`, { ...best.params, ub, b }); + saveIntermediate(); + if (r && r.avg_tps > best.avg_tps) best = r; + } + + // ── Step 4: KV cache sweep ── + log(`\n ── Step 4: KV cache type ──`); + for (const [ctk, ctv] of [["q8_0","q8_0"], ["q4_0","q8_0"], ["f16","f16"]]) { + if (ctk === best.params.ctk && ctv === best.params.ctv) continue; + const r = await testConfig(model, `kv=${ctk}/${ctv}`, { ...best.params, ctk, ctv }); + saveIntermediate(); + if (r && r.avg_tps > best.avg_tps) best = r; + } + + // ── Step 5: Final verification (5 runs) ── + log(`\n ── Step 5: Final verification ──`); + await kill(); + startServer(model.path, best.params); + const { ok, boot } = await waitReady(); + if (!ok) { await kill(); return best; } + const v = vram(); + try { await bench(20); } catch {} + + const finals = []; + for (let i = 0; i < 5; i++) { + try { const r = await bench(); finals.push(r.tps); log(` Final ${i+1}: ${r.tps.toFixed(2)} t/s`); + } catch (e) { log(` Final ${i+1}: ERR`); } + } + await kill(); + + if (finals.length > 0) { + const avg = finals.reduce((a,b)=>a+b) / finals.length; + const bst = Math.max(...finals); + log(` ★ FINAL: AVG ${avg.toFixed(2)} | BEST ${bst.toFixed(2)} t/s`); + const final = { model: model.name, quant: model.quant, label: `FINAL`, + avg_tps: +avg.toFixed(2), best_tps: +bst.toFixed(2), + boot: +boot.toFixed(1), vram: v, params: best.params }; + ALL.push(final); + saveIntermediate(); + return final; + } + return best; +} + +// ─── Main ────────────────────────────────────────────────────── +async function main() { + const t0 = Date.now(); + log("=" .repeat(65)); + log(" DUAL-GPU BENCHMARK v2 — Smart Strategy"); + log(" 2x RTX 3060 (24GB) | 256K Context"); + log(" " + new Date().toISOString()); + log("=".repeat(65)); + vram().forEach(g => log(` GPU${g.gpu}: ${g.used}/${g.total} MiB`)); + + const winners = []; + for (let i = 0; i < MODELS.length; i++) { + log(`\n${"=".repeat(65)}`); + log(` MODEL ${i+1}/${MODELS.length}: ${MODELS[i].name}`); + log("=".repeat(65)); + const w = await tuneModel(MODELS[i]); + if (w) winners.push(w); + saveIntermediate(); + } + + // ─── Summary ────────────────────────────────────────────── + const elapsed = ((Date.now() - t0) / 60000).toFixed(1); + winners.sort((a, b) => b.avg_tps - a.avg_tps); + const medals = ["🥇", "🥈", "🥉", " "]; + + const lines = [ + `Dual-GPU Benchmark v2 — ${new Date().toISOString()}`, + `2x RTX 3060 12GB | 256K Context | ${ALL.length} configs | ${elapsed} min`, + "", "=" .repeat(55), " RANKING", "=".repeat(55), + ]; + for (let i = 0; i < winners.length; i++) { + const w = winners[i], p = w.params; + lines.push("", ` ${medals[i]||" "} #${i+1}: ${w.model}`); + lines.push(` AVG: ${w.avg_tps} t/s | BEST: ${w.best_tps} t/s | Boot: ${w.boot}s`); + lines.push(` ngl=${p.ngl} t=${p.t} ub=${p.ub} b=${p.b} ctk=${p.ctk} ctv=${p.ctv}`); + if (p.cpuMoe) lines.push(` --cpu-moe`); + else if (p.nCpuMoe) lines.push(` --n-cpu-moe ${p.nCpuMoe}`); + } + if (winners.length > 0) { + const c = winners[0], cp = c.params; + lines.push("", "=".repeat(55), ` ★ CHAMPION: ${c.model} — ${c.avg_tps} t/s`, "=".repeat(55)); + const cmd = [`llama-server --model ${MODELS.find(m=>m.name===c.model).path}`, + `-ngl ${cp.ngl} -c ${CTX} -t ${cp.t} -tb ${cp.t}`, + `-ub ${cp.ub} -b ${cp.b} -fa on`, + `--cache-type-k ${cp.ctk} --cache-type-v ${cp.ctv}`, + `--prio ${cp.prio||3} --poll 50 --mlock`, + cp.cpuMoe ? "--cpu-moe" : cp.nCpuMoe ? `--n-cpu-moe ${cp.nCpuMoe}` : "", + "--port 8000 --host 0.0.0.0"].filter(Boolean).join(" "); + lines.push("", " Recommended:", ` ${cmd}`); + } + const summary = lines.join("\n"); + console.log("\n" + summary); + writeFileSync("scripts/dual_gpu_summary.txt", summary, "utf-8"); + writeFileSync("scripts/dual_gpu_results.json", JSON.stringify(ALL, null, 2)); + log(`\n Saved: dual_gpu_results.json + dual_gpu_summary.txt`); + log(" DONE!"); + await kill(); +} + +main().catch(e => { console.error("FATAL:", e); process.exit(1); }); diff --git a/scripts/dual_gpu_results.json b/scripts/dual_gpu_results.json new file mode 100644 index 0000000..31a529d --- /dev/null +++ b/scripts/dual_gpu_results.json @@ -0,0 +1,1654 @@ +[ + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ngl=999 pure-GPU", + "avg_tps": 63.21, + "best_tps": 63.78, + "boot": 9.1, + "vram": [ + { + "gpu": 0, + "used": 11770, + "total": 12288 + }, + { + "gpu": 1, + "used": 10411, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "compare: cpu-moe", + "avg_tps": 12.92, + "best_tps": 14.21, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 3096, + "total": 12288 + }, + { + "gpu": 1, + "used": 3497, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "cpuMoe": true + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=2", + "avg_tps": 64.1, + "best_tps": 64.27, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11728, + "total": 12288 + }, + { + "gpu": 1, + "used": 10411, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=4", + "avg_tps": 64, + "best_tps": 64.39, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11728, + "total": 12288 + }, + { + "gpu": 1, + "used": 10411, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=8", + "avg_tps": 63.75, + "best_tps": 63.9, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11728, + "total": 12288 + }, + { + "gpu": 1, + "used": 10411, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=10", + "avg_tps": 64.01, + "best_tps": 64.14, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11728, + "total": 12288 + }, + { + "gpu": 1, + "used": 10411, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 10, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=12", + "avg_tps": 63.86, + "best_tps": 63.98, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11728, + "total": 12288 + }, + { + "gpu": 1, + "used": 10411, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 12, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=256 b=1024", + "avg_tps": 63.8, + "best_tps": 64.12, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10504, + "total": 12288 + }, + { + "gpu": 1, + "used": 9619, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 256, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=256 b=2048", + "avg_tps": 63.88, + "best_tps": 64.04, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10504, + "total": 12288 + }, + { + "gpu": 1, + "used": 9619, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 256, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=512 b=4096", + "avg_tps": 63.91, + "best_tps": 64.18, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11728, + "total": 12288 + }, + { + "gpu": 1, + "used": 10411, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=1024 b=2048", + "avg_tps": 63.86, + "best_tps": 64.1, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10956, + "total": 12288 + }, + { + "gpu": 1, + "used": 9907, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 1024, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=1024 b=4096", + "avg_tps": 63.85, + "best_tps": 64.06, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10956, + "total": 12288 + }, + { + "gpu": 1, + "used": 9907, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 1024, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "kv=q8_0/q8_0", + "avg_tps": 64.14, + "best_tps": 64.39, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10670, + "total": 12288 + }, + { + "gpu": 1, + "used": 10169, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "kv=q4_0/q8_0", + "avg_tps": 37.52, + "best_tps": 37.86, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10394, + "total": 12288 + }, + { + "gpu": 1, + "used": 9753, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q8_0" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "kv=f16/f16", + "avg_tps": 63.48, + "best_tps": 64.31, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11700, + "total": 12288 + }, + { + "gpu": 1, + "used": 11667, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 2048, + "ctk": "f16", + "ctv": "f16" + } + }, + { + "model": "Gemma4-26B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "FINAL", + "avg_tps": 64.05, + "best_tps": 64.29, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10667, + "total": 12288 + }, + { + "gpu": 1, + "used": 10169, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "ngl=999 pure-GPU", + "avg_tps": 76.01, + "best_tps": 76.31, + "boot": 12.1, + "vram": [ + { + "gpu": 0, + "used": 11784, + "total": 12288 + }, + { + "gpu": 1, + "used": 10454, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "compare: cpu-moe", + "avg_tps": 10.19, + "best_tps": 10.49, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 2652, + "total": 12288 + }, + { + "gpu": 1, + "used": 2982, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "cpuMoe": true + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=2", + "avg_tps": 75.67, + "best_tps": 75.87, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11783, + "total": 12288 + }, + { + "gpu": 1, + "used": 10454, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=4", + "avg_tps": 75.61, + "best_tps": 75.87, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11783, + "total": 12288 + }, + { + "gpu": 1, + "used": 10454, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=8", + "avg_tps": 75.42, + "best_tps": 75.59, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11783, + "total": 12288 + }, + { + "gpu": 1, + "used": 10454, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=10", + "avg_tps": 75.71, + "best_tps": 75.82, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11783, + "total": 12288 + }, + { + "gpu": 1, + "used": 10454, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 10, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=12", + "avg_tps": 75.08, + "best_tps": 75.7, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11783, + "total": 12288 + }, + { + "gpu": 1, + "used": 10454, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 12, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=256 b=1024", + "avg_tps": 75.16, + "best_tps": 75.64, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10559, + "total": 12288 + }, + { + "gpu": 1, + "used": 9662, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 256, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=256 b=2048", + "avg_tps": 75.68, + "best_tps": 76.05, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10559, + "total": 12288 + }, + { + "gpu": 1, + "used": 9662, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 256, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=512 b=4096", + "avg_tps": 75.92, + "best_tps": 76.16, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11784, + "total": 12288 + }, + { + "gpu": 1, + "used": 10454, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=1024 b=2048", + "avg_tps": 75.7, + "best_tps": 75.9, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11012, + "total": 12288 + }, + { + "gpu": 1, + "used": 9950, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 1024, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=1024 b=4096", + "avg_tps": 75.77, + "best_tps": 75.99, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11011, + "total": 12288 + }, + { + "gpu": 1, + "used": 9950, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 1024, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "kv=q8_0/q8_0", + "avg_tps": 76.3, + "best_tps": 76.69, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10725, + "total": 12288 + }, + { + "gpu": 1, + "used": 10212, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "kv=q4_0/q8_0", + "avg_tps": 42.88, + "best_tps": 44.58, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 10439, + "total": 12288 + }, + { + "gpu": 1, + "used": 9796, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q8_0" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "kv=f16/f16", + "avg_tps": 76.36, + "best_tps": 76.78, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11761, + "total": 12288 + }, + { + "gpu": 1, + "used": 11710, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "f16", + "ctv": "f16" + } + }, + { + "model": "Gemma4-26B Q4_K_M", + "quant": "Q4_K_M", + "label": "FINAL", + "avg_tps": 76.4, + "best_tps": 76.75, + "boot": 9, + "vram": [ + { + "gpu": 0, + "used": 11761, + "total": 12288 + }, + { + "gpu": 1, + "used": 11710, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "f16", + "ctv": "f16" + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "n-cpu-moe=5", + "avg_tps": 51.43, + "best_tps": 52.07, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10365, + "total": 12288 + }, + { + "gpu": 1, + "used": 11152, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=2", + "avg_tps": 43.8, + "best_tps": 46.4, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10365, + "total": 12288 + }, + { + "gpu": 1, + "used": 11152, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=4", + "avg_tps": 49.21, + "best_tps": 52.78, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10353, + "total": 12288 + }, + { + "gpu": 1, + "used": 11152, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=8", + "avg_tps": 46.43, + "best_tps": 50.49, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10397, + "total": 12288 + }, + { + "gpu": 1, + "used": 11152, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=10", + "avg_tps": 46.12, + "best_tps": 50.06, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10351, + "total": 12288 + }, + { + "gpu": 1, + "used": 11152, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 10, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "t=12", + "avg_tps": 45.23, + "best_tps": 47.1, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10337, + "total": 12288 + }, + { + "gpu": 1, + "used": 11152, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 12, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=256 b=1024", + "avg_tps": 48.9, + "best_tps": 52.3, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 9834, + "total": 12288 + }, + { + "gpu": 1, + "used": 10906, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 256, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=256 b=2048", + "avg_tps": 49.62, + "best_tps": 52.52, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 9833, + "total": 12288 + }, + { + "gpu": 1, + "used": 10906, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 256, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=512 b=4096", + "avg_tps": 48.78, + "best_tps": 52.14, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10337, + "total": 12288 + }, + { + "gpu": 1, + "used": 11152, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=1024 b=2048", + "avg_tps": 49.95, + "best_tps": 52.53, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 11124, + "total": 12288 + }, + { + "gpu": 1, + "used": 11644, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 1024, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "ub=1024 b=4096", + "avg_tps": 48.75, + "best_tps": 52.06, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 11123, + "total": 12288 + }, + { + "gpu": 1, + "used": 11644, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 1024, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "kv=q4_0/q8_0", + "avg_tps": 42.81, + "best_tps": 44.14, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10681, + "total": 12288 + }, + { + "gpu": 1, + "used": 11472, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q8_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B MXFP4_MOE", + "quant": "MXFP4_MOE", + "label": "FINAL", + "avg_tps": 46.66, + "best_tps": 47.09, + "boot": 15, + "vram": [ + { + "gpu": 0, + "used": 10476, + "total": 12288 + }, + { + "gpu": 1, + "used": 11152, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "n-cpu-moe=5", + "avg_tps": 49.01, + "best_tps": 53.09, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10606, + "total": 12288 + }, + { + "gpu": 1, + "used": 11338, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=2", + "avg_tps": 45.73, + "best_tps": 47.87, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10599, + "total": 12288 + }, + { + "gpu": 1, + "used": 11338, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 2, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=4", + "avg_tps": 50.98, + "best_tps": 54.33, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10601, + "total": 12288 + }, + { + "gpu": 1, + "used": 11338, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=8", + "avg_tps": 48.45, + "best_tps": 52.1, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10596, + "total": 12288 + }, + { + "gpu": 1, + "used": 11338, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=10", + "avg_tps": 47.83, + "best_tps": 51.45, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10595, + "total": 12288 + }, + { + "gpu": 1, + "used": 11338, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 10, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "t=12", + "avg_tps": 43.77, + "best_tps": 46.79, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10589, + "total": 12288 + }, + { + "gpu": 1, + "used": 11338, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 12, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=256 b=1024", + "avg_tps": 52.14, + "best_tps": 53.82, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10089, + "total": 12288 + }, + { + "gpu": 1, + "used": 11092, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 256, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=256 b=2048", + "avg_tps": 50.23, + "best_tps": 53.66, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10091, + "total": 12288 + }, + { + "gpu": 1, + "used": 11092, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 256, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=512 b=2048", + "avg_tps": 49.89, + "best_tps": 53.89, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10595, + "total": 12288 + }, + { + "gpu": 1, + "used": 11338, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "ub=512 b=4096", + "avg_tps": 50.4, + "best_tps": 54.19, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10564, + "total": 12288 + }, + { + "gpu": 1, + "used": 11338, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "kv=q8_0/q8_0", + "avg_tps": 51.84, + "best_tps": 53.53, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10726, + "total": 12288 + }, + { + "gpu": 1, + "used": 11732, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 256, + "b": 1024, + "ctk": "q8_0", + "ctv": "q8_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "kv=q4_0/q8_0", + "avg_tps": 43.22, + "best_tps": 45.99, + "boot": 12, + "vram": [ + { + "gpu": 0, + "used": 10410, + "total": 12288 + }, + { + "gpu": 1, + "used": 11412, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 256, + "b": 1024, + "ctk": "q4_0", + "ctv": "q8_0", + "nCpuMoe": 5 + } + }, + { + "model": "Qwen3.5-35B Q4_K_M", + "quant": "Q4_K_M", + "label": "FINAL", + "avg_tps": 52.05, + "best_tps": 54.48, + "boot": 12.1, + "vram": [ + { + "gpu": 0, + "used": 10062, + "total": 12288 + }, + { + "gpu": 1, + "used": 11092, + "total": 12288 + } + ], + "params": { + "ngl": 999, + "t": 4, + "ub": 256, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0", + "nCpuMoe": 5 + } + } +] \ No newline at end of file diff --git a/scripts/dual_gpu_summary.txt b/scripts/dual_gpu_summary.txt new file mode 100644 index 0000000..29e3cbb --- /dev/null +++ b/scripts/dual_gpu_summary.txt @@ -0,0 +1,31 @@ +Dual-GPU Benchmark v2 — 2026-04-06T06:52:08.868Z +2x RTX 3060 12GB | 256K Context | 58 configs | 69.4 min + +======================================================= + RANKING +======================================================= + + 🥇 #1: Gemma4-26B Q4_K_M + AVG: 76.4 t/s | BEST: 76.75 t/s | Boot: 9s + ngl=999 t=6 ub=512 b=2048 ctk=f16 ctv=f16 + + 🥈 #2: Gemma4-26B MXFP4_MOE + AVG: 64.05 t/s | BEST: 64.29 t/s | Boot: 9s + ngl=999 t=2 ub=512 b=2048 ctk=q8_0 ctv=q8_0 + + 🥉 #3: Qwen3.5-35B Q4_K_M + AVG: 52.05 t/s | BEST: 54.48 t/s | Boot: 12.1s + ngl=999 t=4 ub=256 b=1024 ctk=q4_0 ctv=q4_0 + --n-cpu-moe 5 + + #4: Qwen3.5-35B MXFP4_MOE + AVG: 46.66 t/s | BEST: 47.09 t/s | Boot: 15s + ngl=999 t=6 ub=512 b=2048 ctk=q4_0 ctv=q4_0 + --n-cpu-moe 5 + +======================================================= + ★ CHAMPION: Gemma4-26B Q4_K_M — 76.4 t/s +======================================================= + + Recommended: + llama-server --model models\gemma-4-26B-A4B-it-Q4_K_M.gguf -ngl 999 -c 262144 -t 6 -tb 6 -ub 512 -b 2048 -fa on --cache-type-k f16 --cache-type-v f16 --prio 3 --poll 50 --mlock --port 8000 --host 0.0.0.0 \ No newline at end of file diff --git a/scripts/final_tune_122b.txt b/scripts/final_tune_122b.txt new file mode 100644 index 0000000000000000000000000000000000000000..6eb081670c5d4d758b9c04178aac4a3b4ed1293b GIT binary patch literal 30310 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 PcUX8dG~_+B7<~Q@WBDhy literal 0 HcmV?d00001 diff --git a/scripts/final_tune_122b_dual.txt b/scripts/final_tune_122b_dual.txt new file mode 100644 index 0000000000000000000000000000000000000000..cdaacb86845c42716d224564c6e096274c531019 GIT binary patch literal 48340 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 AwEzGB literal 0 HcmV?d00001 diff --git a/scripts/find_max_dense.mjs b/scripts/find_max_dense.mjs new file mode 100644 index 0000000..e3a8531 --- /dev/null +++ b/scripts/find_max_dense.mjs @@ -0,0 +1,101 @@ +import { spawn, exec } from 'child_process'; + +const delay = ms => new Promise(res => setTimeout(res, ms)); + +async function killServer() { + return new Promise(r => exec('taskkill /F /IM llama-server.exe', () => { setTimeout(r, 2000); })); +} + +async function testContextSize(modelPath, contextSize) { + console.log(`\nTesting ${modelPath} with -c ${contextSize}...`); + await killServer(); + + const args = [ + '--model', `models\\${modelPath}`, + '-ngl', '999', + '-c', contextSize.toString(), + '-fa', 'on', + '--cache-type-k', 'q4_0', + '--cache-type-v', 'q4_0', + '-ub', '512', + '-b', '2048', + '-t', '6', + '-tb', '6', + '--split-mode', 'row', + '--prio', '3', + '--fit', 'off', + '--port', '8000', + '--host', '0.0.0.0' + ]; + + const server = spawn('llama_bin_run\\llama-server.exe', args, { stdio: 'pipe' }); + + let booted = false; + let oomed = false; + + server.stderr.on('data', (d) => { + const text = d.toString(); + if (text.toLowerCase().includes('out of memory') || text.includes('failed to allocate')) { + oomed = true; + } + }); + + for (let i = 0; i < 20; i++) { + if (oomed) break; + try { + const res = await fetch('http://127.0.0.1:8000/health', { timeout: 2000 }); + if (res.status === 200) { + booted = true; + break; + } + } catch(e) {} + await delay(2000); + } + + if (oomed || !booted) { + console.log(`❌ Failed: Out of Memory at -c ${contextSize}`); + server.kill('SIGKILL'); + await killServer(); + return false; + } + + console.log(`✅ Booted! Running Benchmark...`); + + // Benchmark + const bench = await new Promise(r => exec('node scripts/quick_pptest.mjs', (err, stdout, stderr) => { + r(stdout || stderr); + })); + + console.log(bench); + await killServer(); + return true; +} + +async function findMaxContext(modelName) { + const contexts = [262144, 131072, 65536, 32768, 16384, 8192]; + + let maxFound = false; + for (const c of contexts) { + const success = await testContextSize(modelName, c); + if (success) { + maxFound = true; + console.log(`\n🎉 MAX STABLE CONTEXT FOR ${modelName}: ${c}`); + break; + } + } + + if (!maxFound) { + console.log(`\n❌ Failed to find any working context size for ${modelName}`); + } +} + +async function main() { + exec('set CUDA_VISIBLE_DEVICES='); + console.log("============= QWEN 27B Q4_K_M ============="); + await findMaxContext('Qwen3.5-27B-Q4_K_M.gguf'); + + console.log("\n============= GEMMA 4 31B Q4_K_M ============="); + await findMaxContext('gemma-4-31B-it-Q4_K_M.gguf'); +} + +main(); diff --git a/scripts/help_full.txt b/scripts/help_full.txt new file mode 100644 index 0000000..a5bc29f --- /dev/null +++ b/scripts/help_full.txt @@ -0,0 +1,562 @@ +----- common params ----- + +-h, --help, --usage print usage and exit +--version show version and build info +--license show source code license and dependencies +-cl, --cache-list show list of models in cache +--completion-bash print source-able bash completion script for llama.cpp +-t, --threads N number of CPU threads to use during generation (default: -1) + (env: LLAMA_ARG_THREADS) +-tb, --threads-batch N number of threads to use during batch and prompt processing (default: + same as --threads) +-C, --cpu-mask M CPU affinity mask: arbitrarily long hex. Complements cpu-range + (default: "") +-Cr, --cpu-range lo-hi range of CPUs for affinity. Complements --cpu-mask +--cpu-strict <0|1> use strict CPU placement (default: 0) +--prio N set process/thread priority : low(-1), normal(0), medium(1), high(2), + realtime(3) (default: 0) +--poll <0...100> use polling level to wait for work (0 - no polling, default: 50) +-Cb, --cpu-mask-batch M CPU affinity mask: arbitrarily long hex. Complements cpu-range-batch + (default: same as --cpu-mask) +-Crb, --cpu-range-batch lo-hi ranges of CPUs for affinity. Complements --cpu-mask-batch +--cpu-strict-batch <0|1> use strict CPU placement (default: same as --cpu-strict) +--prio-batch N set process/thread priority : 0-normal, 1-medium, 2-high, 3-realtime + (default: 0) +--poll-batch <0|1> use polling to wait for work (default: same as --poll) +-c, --ctx-size N size of the prompt context (default: 0, 0 = loaded from model) + (env: LLAMA_ARG_CTX_SIZE) +-n, --predict, --n-predict N number of tokens to predict (default: -1, -1 = infinity) + (env: LLAMA_ARG_N_PREDICT) +-b, --batch-size N logical maximum batch size (default: 2048) + (env: LLAMA_ARG_BATCH) +-ub, --ubatch-size N physical maximum batch size (default: 512) + (env: LLAMA_ARG_UBATCH) +--keep N number of tokens to keep from the initial prompt (default: 0, -1 = + all) +--swa-full use full-size SWA cache (default: false) + [(more + info)](https://github.com/ggml-org/llama.cpp/pull/13194#issuecomment-2868343055) + (env: LLAMA_ARG_SWA_FULL) +-fa, --flash-attn [on|off|auto] set Flash Attention use ('on', 'off', or 'auto', default: 'auto') + (env: LLAMA_ARG_FLASH_ATTN) +--perf, --no-perf whether to enable internal libllama performance timings (default: + false) + (env: LLAMA_ARG_PERF) +-e, --escape, --no-escape whether to process escapes sequences (\n, \r, \t, \', \", \\) + (default: true) +--rope-scaling {none,linear,yarn} RoPE frequency scaling method, defaults to linear unless specified by + the model + (env: LLAMA_ARG_ROPE_SCALING_TYPE) +--rope-scale N RoPE context scaling factor, expands context by a factor of N + (env: LLAMA_ARG_ROPE_SCALE) +--rope-freq-base N RoPE base frequency, used by NTK-aware scaling (default: loaded from + model) + (env: LLAMA_ARG_ROPE_FREQ_BASE) +--rope-freq-scale N RoPE frequency scaling factor, expands context by a factor of 1/N + (env: LLAMA_ARG_ROPE_FREQ_SCALE) +--yarn-orig-ctx N YaRN: original context size of model (default: 0 = model training + context size) + (env: LLAMA_ARG_YARN_ORIG_CTX) +--yarn-ext-factor N YaRN: extrapolation mix factor (default: -1.00, 0.0 = full + interpolation) + (env: LLAMA_ARG_YARN_EXT_FACTOR) +--yarn-attn-factor N YaRN: scale sqrt(t) or attention magnitude (default: -1.00) + (env: LLAMA_ARG_YARN_ATTN_FACTOR) +--yarn-beta-slow N YaRN: high correction dim or alpha (default: -1.00) + (env: LLAMA_ARG_YARN_BETA_SLOW) +--yarn-beta-fast N YaRN: low correction dim or beta (default: -1.00) + (env: LLAMA_ARG_YARN_BETA_FAST) +-kvo, --kv-offload, -nkvo, --no-kv-offload + whether to enable KV cache offloading (default: enabled) + (env: LLAMA_ARG_KV_OFFLOAD) +--repack, -nr, --no-repack whether to enable weight repacking (default: enabled) + (env: LLAMA_ARG_REPACK) +--no-host bypass host buffer allowing extra buffers to be used + (env: LLAMA_ARG_NO_HOST) +-ctk, --cache-type-k TYPE KV cache data type for K + allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1 + (default: f16) + (env: LLAMA_ARG_CACHE_TYPE_K) +-ctv, --cache-type-v TYPE KV cache data type for V + allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1 + (default: f16) + (env: LLAMA_ARG_CACHE_TYPE_V) +-dt, --defrag-thold N KV cache defragmentation threshold (DEPRECATED) + (env: LLAMA_ARG_DEFRAG_THOLD) +--rpc SERVERS comma separated list of RPC servers (host:port) + (env: LLAMA_ARG_RPC) +--mlock force system to keep model in RAM rather than swapping or compressing + (env: LLAMA_ARG_MLOCK) +--mmap, --no-mmap whether to memory-map model. (if mmap disabled, slower load but may + reduce pageouts if not using mlock) (default: enabled) + (env: LLAMA_ARG_MMAP) +-dio, --direct-io, -ndio, --no-direct-io + use DirectIO if available. (default: disabled) + (env: LLAMA_ARG_DIO) +--numa TYPE attempt optimizations that help on some NUMA systems + - distribute: spread execution evenly over all nodes + - isolate: only spawn threads on CPUs on the node that execution + started on + - numactl: use the CPU map provided by numactl + if run without this previously, it is recommended to drop the system + page cache before using this + see https://github.com/ggml-org/llama.cpp/issues/1437 + (env: LLAMA_ARG_NUMA) +-dev, --device <dev1,dev2,..> comma-separated list of devices to use for offloading (none = don't + offload) + use --list-devices to see a list of available devices + (env: LLAMA_ARG_DEVICE) +--list-devices print list of available devices and exit +-ot, --override-tensor <tensor name pattern>=<buffer type>,... + override tensor buffer type + (env: LLAMA_ARG_OVERRIDE_TENSOR) +-cmoe, --cpu-moe keep all Mixture of Experts (MoE) weights in the CPU + (env: LLAMA_ARG_CPU_MOE) +-ncmoe, --n-cpu-moe N keep the Mixture of Experts (MoE) weights of the first N layers in the + CPU + (env: LLAMA_ARG_N_CPU_MOE) +-ngl, --gpu-layers, --n-gpu-layers N max. number of layers to store in VRAM, either an exact number, + 'auto', or 'all' (default: auto) + (env: LLAMA_ARG_N_GPU_LAYERS) +-sm, --split-mode {none,layer,row} how to split the model across multiple GPUs, one of: + - none: use one GPU only + - layer (default): split layers and KV across GPUs + - row: split rows across GPUs + (env: LLAMA_ARG_SPLIT_MODE) +-ts, --tensor-split N0,N1,N2,... fraction of the model to offload to each GPU, comma-separated list of + proportions, e.g. 3,1 + (env: LLAMA_ARG_TENSOR_SPLIT) +-mg, --main-gpu INDEX the GPU to use for the model (with split-mode = none), or for + intermediate results and KV (with split-mode = row) (default: 0) + (env: LLAMA_ARG_MAIN_GPU) +-fit, --fit [on|off] whether to adjust unset arguments to fit in device memory ('on' or + 'off', default: 'on') + (env: LLAMA_ARG_FIT) +-fitt, --fit-target MiB0,MiB1,MiB2,... + target margin per device for --fit, comma-separated list of values, + single value is broadcast across all devices, default: 1024 + (env: LLAMA_ARG_FIT_TARGET) +-fitc, --fit-ctx N minimum ctx size that can be set by --fit option, default: 4096 + (env: LLAMA_ARG_FIT_CTX) +--check-tensors check model tensor data for invalid values (default: false) +--override-kv KEY=TYPE:VALUE,... advanced option to override model metadata by key. to specify multiple + overrides, either use comma-separated values. + types: int, float, bool, str. example: --override-kv + tokenizer.ggml.add_bos_token=bool:false,tokenizer.ggml.add_eos_token=bool:false +--op-offload, --no-op-offload whether to offload host tensor operations to device (default: true) +--lora FNAME path to LoRA adapter (use comma-separated values to load multiple + adapters) +--lora-scaled FNAME:SCALE,... path to LoRA adapter with user defined scaling (format: + FNAME:SCALE,...) + note: use comma-separated values +--control-vector FNAME add a control vector + note: use comma-separated values to add multiple control vectors +--control-vector-scaled FNAME:SCALE,... + add a control vector with user defined scaling SCALE + note: use comma-separated values (format: FNAME:SCALE,...) +--control-vector-layer-range START END + layer range to apply the control vector(s) to, start and end inclusive +-m, --model FNAME model path to load + (env: LLAMA_ARG_MODEL) +-mu, --model-url MODEL_URL model download url (default: unused) + (env: LLAMA_ARG_MODEL_URL) +-dr, --docker-repo [<repo>/]<model>[:quant] + Docker Hub model repository. repo is optional, default to ai/. quant + is optional, default to :latest. + example: gemma3 + (default: unused) + (env: LLAMA_ARG_DOCKER_REPO) +-hf, -hfr, --hf-repo <user>/<model>[:quant] + Hugging Face model repository; quant is optional, case-insensitive, + default to Q4_K_M, or falls back to the first file in the repo if + Q4_K_M doesn't exist. + mmproj is also downloaded automatically if available. to disable, add + --no-mmproj + example: ggml-org/GLM-4.7-Flash-GGUF:Q4_K_M + (default: unused) + (env: LLAMA_ARG_HF_REPO) +-hfd, -hfrd, --hf-repo-draft <user>/<model>[:quant] + Same as --hf-repo, but for the draft model (default: unused) + (env: LLAMA_ARG_HFD_REPO) +-hff, --hf-file FILE Hugging Face model file. If specified, it will override the quant in + --hf-repo (default: unused) + (env: LLAMA_ARG_HF_FILE) +-hfv, -hfrv, --hf-repo-v <user>/<model>[:quant] + Hugging Face model repository for the vocoder model (default: unused) + (env: LLAMA_ARG_HF_REPO_V) +-hffv, --hf-file-v FILE Hugging Face model file for the vocoder model (default: unused) + (env: LLAMA_ARG_HF_FILE_V) +-hft, --hf-token TOKEN Hugging Face access token (default: value from HF_TOKEN environment + variable) + (env: HF_TOKEN) +--log-disable Log disable +--log-file FNAME Log to file + (env: LLAMA_LOG_FILE) +--log-colors [on|off|auto] Set colored logging ('on', 'off', or 'auto', default: 'auto') + 'auto' enables colors when output is to a terminal + (env: LLAMA_LOG_COLORS) +-v, --verbose, --log-verbose Set verbosity level to infinity (i.e. log all messages, useful for + debugging) +--offline Offline mode: forces use of cache, prevents network access + (env: LLAMA_OFFLINE) +-lv, --verbosity, --log-verbosity N Set the verbosity threshold. Messages with a higher verbosity will be + ignored. Values: + - 0: generic output + - 1: error + - 2: warning + - 3: info + - 4: debug + (default: 3) + + (env: LLAMA_LOG_VERBOSITY) +--log-prefix Enable prefix in log messages + (env: LLAMA_LOG_PREFIX) +--log-timestamps Enable timestamps in log messages + (env: LLAMA_LOG_TIMESTAMPS) +-ctkd, --cache-type-k-draft TYPE KV cache data type for K for the draft model + allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1 + (default: f16) + (env: LLAMA_ARG_CACHE_TYPE_K_DRAFT) +-ctvd, --cache-type-v-draft TYPE KV cache data type for V for the draft model + allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1 + (default: f16) + (env: LLAMA_ARG_CACHE_TYPE_V_DRAFT) + + +----- sampling params ----- + +--samplers SAMPLERS samplers that will be used for generation in the order, separated by + ';' + (default: + penalties;dry;top_n_sigma;top_k;typ_p;top_p;min_p;xtc;temperature) +-s, --seed SEED RNG seed (default: -1, use random seed for -1) +--sampler-seq, --sampling-seq SEQUENCE + simplified sequence for samplers that will be used (default: + edskypmxt) +--ignore-eos ignore end of stream token and continue generating (implies + --logit-bias EOS-inf) +--temp, --temperature N temperature (default: 0.80) +--top-k N top-k sampling (default: 40, 0 = disabled) + (env: LLAMA_ARG_TOP_K) +--top-p N top-p sampling (default: 0.95, 1.0 = disabled) +--min-p N min-p sampling (default: 0.05, 0.0 = disabled) +--top-nsigma, --top-n-sigma N top-n-sigma sampling (default: -1.00, -1.0 = disabled) +--xtc-probability N xtc probability (default: 0.00, 0.0 = disabled) +--xtc-threshold N xtc threshold (default: 0.10, 1.0 = disabled) +--typical, --typical-p N locally typical sampling, parameter p (default: 1.00, 1.0 = disabled) +--repeat-last-n N last n tokens to consider for penalize (default: 64, 0 = disabled, -1 + = ctx_size) +--repeat-penalty N penalize repeat sequence of tokens (default: 1.00, 1.0 = disabled) +--presence-penalty N repeat alpha presence penalty (default: 0.00, 0.0 = disabled) +--frequency-penalty N repeat alpha frequency penalty (default: 0.00, 0.0 = disabled) +--dry-multiplier N set DRY sampling multiplier (default: 0.00, 0.0 = disabled) +--dry-base N set DRY sampling base value (default: 1.75) +--dry-allowed-length N set allowed length for DRY sampling (default: 2) +--dry-penalty-last-n N set DRY penalty for the last n tokens (default: -1, 0 = disable, -1 = + context size) +--dry-sequence-breaker STRING add sequence breaker for DRY sampling, clearing out default breakers + ('\n', ':', '"', '*') in the process; use "none" to not use any + sequence breakers +--adaptive-target N adaptive-p: select tokens near this probability (valid range 0.0 to + 1.0; negative = disabled) (default: -1.00) + [(more info)](https://github.com/ggml-org/llama.cpp/pull/17927) +--adaptive-decay N adaptive-p: decay rate for target adaptation over time. lower values + are more reactive, higher values are more stable. + (valid range 0.0 to 0.99) (default: 0.90) +--dynatemp-range N dynamic temperature range (default: 0.00, 0.0 = disabled) +--dynatemp-exp N dynamic temperature exponent (default: 1.00) +--mirostat N use Mirostat sampling. + Top K, Nucleus and Locally Typical samplers are ignored if used. + (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0) +--mirostat-lr N Mirostat learning rate, parameter eta (default: 0.10) +--mirostat-ent N Mirostat target entropy, parameter tau (default: 5.00) +-l, --logit-bias TOKEN_ID(+/-)BIAS modifies the likelihood of token appearing in the completion, + i.e. `--logit-bias 15043+1` to increase likelihood of token ' Hello', + or `--logit-bias 15043-1` to decrease likelihood of token ' Hello' +--grammar GRAMMAR BNF-like grammar to constrain generations (see samples in grammars/ + dir) +--grammar-file FNAME file to read grammar from +-j, --json-schema SCHEMA JSON schema to constrain generations (https://json-schema.org/), e.g. + `{}` for any JSON object + For schemas w/ external $refs, use --grammar + + example/json_schema_to_grammar.py instead +-jf, --json-schema-file FILE File containing a JSON schema to constrain generations + (https://json-schema.org/), e.g. `{}` for any JSON object + For schemas w/ external $refs, use --grammar + + example/json_schema_to_grammar.py instead +-bs, --backend-sampling enable backend sampling (experimental) (default: disabled) + (env: LLAMA_ARG_BACKEND_SAMPLING) + + +----- example-specific params ----- + +-lcs, --lookup-cache-static FNAME path to static lookup cache to use for lookup decoding (not updated by + generation) +-lcd, --lookup-cache-dynamic FNAME path to dynamic lookup cache to use for lookup decoding (updated by + generation) +-ctxcp, --ctx-checkpoints, --swa-checkpoints N + max number of context checkpoints to create per slot (default: + 32)[(more info)](https://github.com/ggml-org/llama.cpp/pull/15293) + (env: LLAMA_ARG_CTX_CHECKPOINTS) +-cpent, --checkpoint-every-n-tokens N create a checkpoint every n tokens during prefill (processing), -1 to + disable (default: 8192) + (env: LLAMA_ARG_CHECKPOINT_EVERY_NT) +-cram, --cache-ram N set the maximum cache size in MiB (default: 8192, -1 - no limit, 0 - + disable)[(more + info)](https://github.com/ggml-org/llama.cpp/pull/16391) + (env: LLAMA_ARG_CACHE_RAM) +-kvu, --kv-unified, -no-kvu, --no-kv-unified + use single unified KV buffer shared across all sequences (default: + enabled if number of slots is auto) + (env: LLAMA_ARG_KV_UNIFIED) +--clear-idle, --no-clear-idle save and clear idle slots on new task (default: enabled, requires + unified KV and cache-ram) + (env: LLAMA_ARG_CLEAR_IDLE) +--context-shift, --no-context-shift whether to use context shift on infinite text generation (default: + disabled) + (env: LLAMA_ARG_CONTEXT_SHIFT) +-r, --reverse-prompt PROMPT halt generation at PROMPT, return control in interactive mode +-sp, --special special tokens output enabled (default: false) +--warmup, --no-warmup whether to perform warmup with an empty run (default: enabled) +--spm-infill use Suffix/Prefix/Middle pattern for infill (instead of + Prefix/Suffix/Middle) as some models prefer this. (default: disabled) +--pooling {none,mean,cls,last,rank} pooling type for embeddings, use model default if unspecified + (env: LLAMA_ARG_POOLING) +-np, --parallel N number of server slots (default: -1, -1 = auto) + (env: LLAMA_ARG_N_PARALLEL) +-cb, --cont-batching, -nocb, --no-cont-batching + whether to enable continuous batching (a.k.a dynamic batching) + (default: enabled) + (env: LLAMA_ARG_CONT_BATCHING) +-mm, --mmproj FILE path to a multimodal projector file. see tools/mtmd/README.md + note: if -hf is used, this argument can be omitted + (env: LLAMA_ARG_MMPROJ) +-mmu, --mmproj-url URL URL to a multimodal projector file. see tools/mtmd/README.md + (env: LLAMA_ARG_MMPROJ_URL) +--mmproj-auto, --no-mmproj, --no-mmproj-auto + whether to use multimodal projector file (if available), useful when + using -hf (default: enabled) + (env: LLAMA_ARG_MMPROJ_AUTO) +--mmproj-offload, --no-mmproj-offload whether to enable GPU offloading for multimodal projector (default: + enabled) + (env: LLAMA_ARG_MMPROJ_OFFLOAD) +--image-min-tokens N minimum number of tokens each image can take, only used by vision + models with dynamic resolution (default: read from model) + (env: LLAMA_ARG_IMAGE_MIN_TOKENS) +--image-max-tokens N maximum number of tokens each image can take, only used by vision + models with dynamic resolution (default: read from model) + (env: LLAMA_ARG_IMAGE_MAX_TOKENS) +-otd, --override-tensor-draft <tensor name pattern>=<buffer type>,... + override tensor buffer type for draft model +-cmoed, --cpu-moe-draft keep all Mixture of Experts (MoE) weights in the CPU for the draft + model + (env: LLAMA_ARG_CPU_MOE_DRAFT) +-ncmoed, --n-cpu-moe-draft N keep the Mixture of Experts (MoE) weights of the first N layers in the + CPU for the draft model + (env: LLAMA_ARG_N_CPU_MOE_DRAFT) +-a, --alias STRING set model name aliases, comma-separated (to be used by API) + (env: LLAMA_ARG_ALIAS) +--tags STRING set model tags, comma-separated (informational, not used for routing) + (env: LLAMA_ARG_TAGS) +--host HOST ip address to listen, or bind to an UNIX socket if the address ends + with .sock (default: 127.0.0.1) + (env: LLAMA_ARG_HOST) +--port PORT port to listen (default: 8080) + (env: LLAMA_ARG_PORT) +--reuse-port allow multiple sockets to bind to the same port (default: disabled) + (env: LLAMA_ARG_REUSE_PORT) +--path PATH path to serve static files from (default: ) + (env: LLAMA_ARG_STATIC_PATH) +--api-prefix PREFIX prefix path the server serves from, without the trailing slash + (default: ) + (env: LLAMA_ARG_API_PREFIX) +--webui-config JSON JSON that provides default WebUI settings (overrides WebUI defaults) + (env: LLAMA_ARG_WEBUI_CONFIG) +--webui-config-file PATH JSON file that provides default WebUI settings (overrides WebUI + defaults) + (env: LLAMA_ARG_WEBUI_CONFIG_FILE) +--webui-mcp-proxy, --no-webui-mcp-proxy + experimental: whether to enable MCP CORS proxy - do not enable in + untrusted environments (default: disabled) + (env: LLAMA_ARG_WEBUI_MCP_PROXY) +--tools TOOL1,TOOL2,... experimental: whether to enable built-in tools for AI agents - do not + enable in untrusted environments (default: no tools) + specify "all" to enable all tools + available tools: read_file, file_glob_search, grep_search, + exec_shell_command, write_file, edit_file, apply_diff + (env: LLAMA_ARG_TOOLS) +--webui, --no-webui whether to enable the Web UI (default: enabled) + (env: LLAMA_ARG_WEBUI) +--embedding, --embeddings restrict to only support embedding use case; use only with dedicated + embedding models (default: disabled) + (env: LLAMA_ARG_EMBEDDINGS) +--rerank, --reranking enable reranking endpoint on server (default: disabled) + (env: LLAMA_ARG_RERANKING) +--api-key KEY API key to use for authentication, multiple keys can be provided as a + comma-separated list (default: none) + (env: LLAMA_API_KEY) +--api-key-file FNAME path to file containing API keys (default: none) +--ssl-key-file FNAME path to file a PEM-encoded SSL private key + (env: LLAMA_ARG_SSL_KEY_FILE) +--ssl-cert-file FNAME path to file a PEM-encoded SSL certificate + (env: LLAMA_ARG_SSL_CERT_FILE) +--chat-template-kwargs STRING sets additional params for the json template parser, must be a valid + json object string, e.g. '{"key1":"value1","key2":"value2"}' + (env: LLAMA_CHAT_TEMPLATE_KWARGS) +-to, --timeout N server read/write timeout in seconds (default: 600) + (env: LLAMA_ARG_TIMEOUT) +--threads-http N number of threads used to process HTTP requests (default: -1) + (env: LLAMA_ARG_THREADS_HTTP) +--cache-prompt, --no-cache-prompt whether to enable prompt caching (default: enabled) + (env: LLAMA_ARG_CACHE_PROMPT) +--cache-reuse N min chunk size to attempt reusing from the cache via KV shifting, + requires prompt caching to be enabled (default: 0) + [(card)](https://ggml.ai/f0.png) + (env: LLAMA_ARG_CACHE_REUSE) +--metrics enable prometheus compatible metrics endpoint (default: disabled) + (env: LLAMA_ARG_ENDPOINT_METRICS) +--props enable changing global properties via POST /props (default: disabled) + (env: LLAMA_ARG_ENDPOINT_PROPS) +--slots, --no-slots expose slots monitoring endpoint (default: enabled) + (env: LLAMA_ARG_ENDPOINT_SLOTS) +--slot-save-path PATH path to save slot kv cache (default: disabled) +--media-path PATH directory for loading local media files; files can be accessed via + file:// URLs using relative paths (default: disabled) +--models-dir PATH directory containing models for the router server (default: disabled) + (env: LLAMA_ARG_MODELS_DIR) +--models-preset PATH path to INI file containing model presets for the router server + (default: disabled) + (env: LLAMA_ARG_MODELS_PRESET) +--models-max N for router server, maximum number of models to load simultaneously + (default: 4, 0 = unlimited) + (env: LLAMA_ARG_MODELS_MAX) +--models-autoload, --no-models-autoload + for router server, whether to automatically load models (default: + enabled) + (env: LLAMA_ARG_MODELS_AUTOLOAD) +--jinja, --no-jinja whether to use jinja template engine for chat (default: enabled) + (env: LLAMA_ARG_JINJA) +--reasoning-format FORMAT controls whether thought tags are allowed and/or extracted from the + response, and in which format they're returned; one of: + - none: leaves thoughts unparsed in `message.content` + - deepseek: puts thoughts in `message.reasoning_content` + - deepseek-legacy: keeps `<think>` tags in `message.content` while + also populating `message.reasoning_content` + (default: auto) + (env: LLAMA_ARG_THINK) +-rea, --reasoning [on|off|auto] Use reasoning/thinking in the chat ('on', 'off', or 'auto', default: + 'auto' (detect from template)) + (env: LLAMA_ARG_REASONING) +--reasoning-budget N token budget for thinking: -1 for unrestricted, 0 for immediate end, + N>0 for token budget (default: -1) + (env: LLAMA_ARG_THINK_BUDGET) +--reasoning-budget-message MESSAGE message injected before the end-of-thinking tag when reasoning budget + is exhausted (default: none) + (env: LLAMA_ARG_THINK_BUDGET_MESSAGE) +--chat-template JINJA_TEMPLATE set custom jinja chat template (default: template taken from model's + metadata) + if suffix/prefix are specified, template will be disabled + only commonly used templates are accepted (unless --jinja is set + before this flag): + list of built-in templates: + bailing, bailing-think, bailing2, chatglm3, chatglm4, chatml, + command-r, deepseek, deepseek-ocr, deepseek2, deepseek3, exaone-moe, + exaone3, exaone4, falcon3, gemma, gigachat, glmedge, gpt-oss, granite, + granite-4.0, grok-2, hunyuan-dense, hunyuan-moe, kimi-k2, llama2, + llama2-sys, llama2-sys-bos, llama2-sys-strip, llama3, llama4, megrez, + minicpm, mistral-v1, mistral-v3, mistral-v3-tekken, mistral-v7, + mistral-v7-tekken, monarch, openchat, orion, pangu-embedded, phi3, + phi4, rwkv-world, seed_oss, smolvlm, solar-open, vicuna, vicuna-orca, + yandex, zephyr + (env: LLAMA_ARG_CHAT_TEMPLATE) +--chat-template-file JINJA_TEMPLATE_FILE + set custom jinja chat template file (default: template taken from + model's metadata) + if suffix/prefix are specified, template will be disabled + only commonly used templates are accepted (unless --jinja is set + before this flag): + list of built-in templates: + bailing, bailing-think, bailing2, chatglm3, chatglm4, chatml, + command-r, deepseek, deepseek-ocr, deepseek2, deepseek3, exaone-moe, + exaone3, exaone4, falcon3, gemma, gigachat, glmedge, gpt-oss, granite, + granite-4.0, grok-2, hunyuan-dense, hunyuan-moe, kimi-k2, llama2, + llama2-sys, llama2-sys-bos, llama2-sys-strip, llama3, llama4, megrez, + minicpm, mistral-v1, mistral-v3, mistral-v3-tekken, mistral-v7, + mistral-v7-tekken, monarch, openchat, orion, pangu-embedded, phi3, + phi4, rwkv-world, seed_oss, smolvlm, solar-open, vicuna, vicuna-orca, + yandex, zephyr + (env: LLAMA_ARG_CHAT_TEMPLATE_FILE) +--skip-chat-parsing, --no-skip-chat-parsing + force a pure content parser, even if a Jinja template is specified; + model will output everything in the content section, including any + reasoning and/or tool calls (default: disabled) + (env: LLAMA_ARG_SKIP_CHAT_PARSING) +--prefill-assistant, --no-prefill-assistant + whether to prefill the assistant's response if the last message is an + assistant message (default: prefill enabled) + when this flag is set, if the last message is an assistant message + then it will be treated as a full message and not prefilled + + (env: LLAMA_ARG_PREFILL_ASSISTANT) +-sps, --slot-prompt-similarity SIMILARITY + how much the prompt of a request must match the prompt of a slot in + order to use that slot (default: 0.10, 0.0 = disabled) +--lora-init-without-apply load LoRA adapters without applying them (apply later via POST + /lora-adapters) (default: disabled) +--sleep-idle-seconds SECONDS number of seconds of idleness after which the server will sleep + (default: -1; -1 = disabled) +-td, --threads-draft N number of threads to use during generation (default: same as + --threads) +-tbd, --threads-batch-draft N number of threads to use during batch and prompt processing (default: + same as --threads-draft) +--draft, --draft-n, --draft-max N number of tokens to draft for speculative decoding (default: 16) + (env: LLAMA_ARG_DRAFT_MAX) +--draft-min, --draft-n-min N minimum number of draft tokens to use for speculative decoding + (default: 0) + (env: LLAMA_ARG_DRAFT_MIN) +--draft-p-min P minimum speculative decoding probability (greedy) (default: 0.75) + (env: LLAMA_ARG_DRAFT_P_MIN) +-cd, --ctx-size-draft N size of the prompt context for the draft model (default: 0, 0 = loaded + from model) + (env: LLAMA_ARG_CTX_SIZE_DRAFT) +-devd, --device-draft <dev1,dev2,..> comma-separated list of devices to use for offloading the draft model + (none = don't offload) + use --list-devices to see a list of available devices +-ngld, --gpu-layers-draft, --n-gpu-layers-draft N + max. number of draft model layers to store in VRAM, either an exact + number, 'auto', or 'all' (default: auto) + (env: LLAMA_ARG_N_GPU_LAYERS_DRAFT) +-md, --model-draft FNAME draft model for speculative decoding (default: unused) + (env: LLAMA_ARG_MODEL_DRAFT) +--spec-replace TARGET DRAFT translate the string in TARGET into DRAFT if the draft model and main + model are not compatible +--spec-type [none|ngram-cache|ngram-simple|ngram-map-k|ngram-map-k4v|ngram-mod] + type of speculative decoding to use when no draft model is provided + (default: none) + + (env: LLAMA_ARG_SPEC_TYPE) +--spec-ngram-size-n N ngram size N for ngram-simple/ngram-map speculative decoding, length + of lookup n-gram (default: 12) +--spec-ngram-size-m N ngram size M for ngram-simple/ngram-map speculative decoding, length + of draft m-gram (default: 48) +--spec-ngram-min-hits N minimum hits for ngram-map speculative decoding (default: 1) +-mv, --model-vocoder FNAME vocoder model for audio generation (default: unused) +--tts-use-guide-tokens Use guide tokens to improve TTS word recall +--embd-gemma-default use default EmbeddingGemma model (note: can download weights from the + internet) +--fim-qwen-1.5b-default use default Qwen 2.5 Coder 1.5B (note: can download weights from the + internet) +--fim-qwen-3b-default use default Qwen 2.5 Coder 3B (note: can download weights from the + internet) +--fim-qwen-7b-default use default Qwen 2.5 Coder 7B (note: can download weights from the + internet) +--fim-qwen-7b-spec use Qwen 2.5 Coder 7B + 0.5B draft for speculative decoding (note: can + download weights from the internet) +--fim-qwen-14b-spec use Qwen 2.5 Coder 14B + 0.5B draft for speculative decoding (note: + can download weights from the internet) +--fim-qwen-30b-default use default Qwen 3 Coder 30B A3B Instruct (note: can download weights + from the internet) +--gpt-oss-20b-default use gpt-oss-20b (note: can download weights from the internet) +--gpt-oss-120b-default use gpt-oss-120b (note: can download weights from the internet) +--vision-gemma-4b-default use Gemma 3 4B QAT (note: can download weights from the internet) +--vision-gemma-12b-default use Gemma 3 12B QAT (note: can download weights from the internet) diff --git a/scripts/help_gpu_flags.txt b/scripts/help_gpu_flags.txt new file mode 100644 index 0000000..68565c7 --- /dev/null +++ b/scripts/help_gpu_flags.txt @@ -0,0 +1,31 @@ +ggml_cuda_init: found 2 CUDA devices (Total VRAM: 24575 MiB): + Device 0: NVIDIA GeForce RTX 3060, compute capability 8.6, VMM: yes, VRAM: 12287 MiB + Device 1: NVIDIA GeForce RTX 3060, compute capability 8.6, VMM: yes, VRAM: 12287 MiB +-dev, --device <dev1,dev2,..> comma-separated list of devices to use for offloading (none = don't + use --list-devices to see a list of available devices + (env: LLAMA_ARG_DEVICE) +--list-devices print list of available devices and exit +-ot, --override-tensor <tensor name pattern>=<buffer type>,... + override tensor buffer type + (env: LLAMA_ARG_OVERRIDE_TENSOR) +-cmoe, --cpu-moe keep all Mixture of Experts (MoE) weights in the CPU +-ncmoe, --n-cpu-moe N keep the Mixture of Experts (MoE) weights of the first N layers in the +-sm, --split-mode {none,layer,row} how to split the model across multiple GPUs, one of: + - layer (default): split layers and KV across GPUs + - row: split rows across GPUs + (env: LLAMA_ARG_SPLIT_MODE) +-ts, --tensor-split N0,N1,N2,... fraction of the model to offload to each GPU, comma-separated list of + (env: LLAMA_ARG_TENSOR_SPLIT) +-mg, --main-gpu INDEX the GPU to use for the model (with split-mode = none), or for + intermediate results and KV (with split-mode = row) (default: 0) +-fit, --fit [on|off] whether to adjust unset arguments to fit in device memory ('on' or + target margin per device for --fit, comma-separated list of values, + single value is broadcast across all devices, default: 1024 +--check-tensors check model tensor data for invalid values (default: false) +--op-offload, --no-op-offload whether to offload host tensor operations to device (default: true) +-otd, --override-tensor-draft <tensor name pattern>=<buffer type>,... + override tensor buffer type for draft model +-cmoed, --cpu-moe-draft keep all Mixture of Experts (MoE) weights in the CPU for the draft +-ncmoed, --n-cpu-moe-draft N keep the Mixture of Experts (MoE) weights of the first N layers in the +-devd, --device-draft <dev1,dev2,..> comma-separated list of devices to use for offloading the draft model + use --list-devices to see a list of available devices diff --git a/scripts/hf_search.py b/scripts/hf_search.py new file mode 100644 index 0000000..afecd79 --- /dev/null +++ b/scripts/hf_search.py @@ -0,0 +1,28 @@ +from huggingface_hub import HfApi +import sys + +api = HfApi() + +def search_gguf(query): + print(f"\n--- Searching for: {query} ---") + try: + models = api.list_models(search=query, limit=3) + found = list(models) + if not found: + print("No models found.") + return + for m in found: + print(f"Repo: {m.id}") + files = api.list_repo_files(repo_id=m.id) + ggufs = [f for f in files if "q4_k_m" in f.lower() and f.endswith(".gguf")] + if not ggufs: + ggufs = [f for f in files if f.endswith(".gguf")][:3] + print(f" GGUFs: {ggufs}") + except Exception as e: + print(f"Error: {e}") + +search_gguf("122b-a10b gguf") +search_gguf("Qwen3.5 122b gguf") +search_gguf("35b-a3b gguf") +search_gguf("gemma-4 26b gguf") +search_gguf("Qwen 122B") diff --git a/scripts/perf_test.py b/scripts/perf_test.py new file mode 100644 index 0000000..6aaaebd --- /dev/null +++ b/scripts/perf_test.py @@ -0,0 +1,123 @@ +import time +import json +import urllib.request +import sys + +try: + sys.stdout.reconfigure(encoding='utf-8') +except AttributeError: + pass + +BASE_URL = "http://127.0.0.1:8000" + +def check_server(): + """Check if server is up""" + try: + req = urllib.request.Request(f"{BASE_URL}/health") + with urllib.request.urlopen(req, timeout=5) as resp: + data = json.loads(resp.read()) + return data.get("status") == "ok" + except: + return False + +def run_benchmark(prompt, max_tokens=100, label="Test"): + """Run a single benchmark request and return results""" + payload = json.dumps({ + "model": "local-model", + "messages": [{"role": "user", "content": prompt}], + "max_tokens": max_tokens, + "temperature": 0.0 + }).encode("utf-8") + + req = urllib.request.Request( + f"{BASE_URL}/v1/chat/completions", + data=payload, + headers={"Content-Type": "application/json"} + ) + + start = time.time() + with urllib.request.urlopen(req, timeout=300) as resp: + result = json.loads(resp.read()) + elapsed = time.time() - start + + content = result["choices"][0]["message"].get("content", "") + usage = result.get("usage", {}) + prompt_tokens = usage.get("prompt_tokens", 0) + completion_tokens = usage.get("completion_tokens", 0) + + gen_tps = completion_tokens / elapsed if elapsed > 0 else 0 + + return { + "label": label, + "prompt_tokens": prompt_tokens, + "completion_tokens": completion_tokens, + "elapsed": elapsed, + "gen_tps_approx": gen_tps, + "content_preview": content[:100] + } + +def main(): + print("=" * 60) + print(" LLM Performance Benchmark Tool") + print("=" * 60) + print() + + # Wait for server + print("[1/3] Checking server health...") + for i in range(30): + if check_server(): + print(" -> Server is ready!") + break + print(f" -> Waiting for server... ({i+1}/30)") + time.sleep(2) + else: + print(" -> ERROR: Server not responding after 60s") + return + + # Warmup + print() + print("[2/3] Warmup run (short)...") + try: + warmup = run_benchmark("Say hello in 5 words.", max_tokens=20, label="Warmup") + print(f" -> Warmup done: {warmup['completion_tokens']} tokens in {warmup['elapsed']:.2f}s") + except Exception as e: + print(f" -> Warmup failed: {e}") + + # Main benchmark + print() + print("[3/3] Running main benchmark...") + print("-" * 60) + + test_prompt = "Count from 1 to 50, writing each number on a new line." + + results = [] + for i in range(3): + print(f" Run {i+1}/3...") + try: + r = run_benchmark(test_prompt, max_tokens=200, label=f"Run {i+1}") + results.append(r) + print(f" Tokens: {r['completion_tokens']} | " + f"Time: {r['elapsed']:.2f}s | " + f"Speed: {r['gen_tps_approx']:.2f} t/s (approx)") + except Exception as e: + print(f" ERROR: {e}") + + if results: + print() + print("=" * 60) + print(" RESULTS SUMMARY") + print("=" * 60) + avg_tps = sum(r["gen_tps_approx"] for r in results) / len(results) + max_tps = max(r["gen_tps_approx"] for r in results) + min_tps = min(r["gen_tps_approx"] for r in results) + print(f" Runs: {len(results)}") + print(f" Avg TPS: {avg_tps:.2f} t/s (approx, includes prompt eval)") + print(f" Min TPS: {min_tps:.2f} t/s") + print(f" Max TPS: {max_tps:.2f} t/s") + print() + print(" NOTE: Check server console for exact generation t/s") + print(" (the 'eval time' line shows pure token generation speed)") + print("=" * 60) + +if __name__ == "__main__": + main() diff --git a/scripts/perf_test_122b.py b/scripts/perf_test_122b.py new file mode 100644 index 0000000..0981587 --- /dev/null +++ b/scripts/perf_test_122b.py @@ -0,0 +1,169 @@ +import time +import json +import urllib.request +import sys +import os +import re + +try: + sys.stdout.reconfigure(encoding='utf-8') +except AttributeError: + pass + +BASE_URL = "http://127.0.0.1:8000" + +def check_server(): + """Check if server is up""" + try: + req = urllib.request.Request(f"{BASE_URL}/health") + with urllib.request.urlopen(req, timeout=5) as resp: + data = json.loads(resp.read()) + return data.get("status") == "ok" + except: + return False + +def check_slots(): + """Check server slot info for VRAM usage details""" + try: + req = urllib.request.Request(f"{BASE_URL}/slots") + with urllib.request.urlopen(req, timeout=5) as resp: + return json.loads(resp.read()) + except: + return None + +def run_benchmark(prompt, max_tokens=300, label="Test"): + """Run a single benchmark request and return results""" + payload = json.dumps({ + "model": "local-model", + "messages": [{"role": "user", "content": prompt}], + "max_tokens": max_tokens, + "temperature": 0.0 + }).encode("utf-8") + + req = urllib.request.Request( + f"{BASE_URL}/v1/chat/completions", + data=payload, + headers={"Content-Type": "application/json"} + ) + + start = time.time() + with urllib.request.urlopen(req, timeout=600) as resp: + result = json.loads(resp.read()) + elapsed = time.time() - start + + content = result["choices"][0]["message"].get("content", "") + usage = result.get("usage", {}) + prompt_tokens = usage.get("prompt_tokens", 0) + completion_tokens = usage.get("completion_tokens", 0) + + gen_tps = completion_tokens / elapsed if elapsed > 0 else 0 + + return { + "label": label, + "prompt_tokens": prompt_tokens, + "completion_tokens": completion_tokens, + "elapsed": elapsed, + "gen_tps_approx": gen_tps, + "content_preview": content[:150] + } + +def main(): + print("=" * 70) + print(" Qwen3.5 122B-A10B Performance Benchmark") + print(" Target: 10+ t/s generation speed") + print("=" * 70) + print() + + # Wait for server (model loading takes 3-5 min for 71 GB) + print("[1/4] Waiting for server (122B model load takes 3-5 min)...") + max_wait = 600 # 10 minutes max + for i in range(max_wait // 5): + if check_server(): + print(f" -> Server is ready! (waited {i*5}s)") + break + if i % 6 == 0: + print(f" -> Loading model... ({i*5}s / {max_wait}s)") + time.sleep(5) + else: + print(f" -> ERROR: Server not responding after {max_wait}s") + return + + # Check server info + print() + print("[2/4] Checking server status...") + slots = check_slots() + if slots: + print(f" -> Slots available: {len(slots)}") + + # Warmup + print() + print("[3/4] Warmup run (short, pre-heating GPU caches)...") + try: + warmup = run_benchmark("Say hello in 5 words.", max_tokens=20, label="Warmup") + print(f" -> Warmup done: {warmup['completion_tokens']} tokens in {warmup['elapsed']:.2f}s") + print(f" -> Warmup speed: {warmup['gen_tps_approx']:.2f} t/s (includes prompt eval)") + except Exception as e: + print(f" -> Warmup failed: {e}") + + # Main benchmark - 5 runs for statistical reliability + print() + print("[4/4] Running main benchmark (5 runs x 300 tokens)...") + print("-" * 70) + + test_prompts = [ + "Write a detailed explanation of how neural networks learn. Cover backpropagation, gradient descent, and loss functions.", + "Explain the history of the internet from ARPANET to modern day. Include key milestones and technological breakthroughs.", + "Describe the complete process of photosynthesis in plants. Include both light-dependent and light-independent reactions.", + "Write about the major differences between SQL and NoSQL databases, including use cases and performance characteristics.", + "Explain quantum computing concepts including qubits, superposition, and entanglement in simple terms.", + ] + + results = [] + for i in range(5): + prompt = test_prompts[i % len(test_prompts)] + print(f"\n Run {i+1}/5: {prompt[:50]}...") + try: + r = run_benchmark(prompt, max_tokens=300, label=f"Run {i+1}") + results.append(r) + print(f" Completion tokens: {r['completion_tokens']}") + print(f" Total time: {r['elapsed']:.2f}s") + print(f" Approx speed: {r['gen_tps_approx']:.2f} t/s (includes prompt eval)") + except Exception as e: + print(f" ERROR: {e}") + + if results: + print() + print("=" * 70) + print(" RESULTS SUMMARY - Qwen3.5 122B-A10B") + print("=" * 70) + avg_tps = sum(r["gen_tps_approx"] for r in results) / len(results) + max_tps = max(r["gen_tps_approx"] for r in results) + min_tps = min(r["gen_tps_approx"] for r in results) + total_tokens = sum(r["completion_tokens"] for r in results) + total_time = sum(r["elapsed"] for r in results) + + print(f" Runs completed: {len(results)}/5") + print(f" Total tokens: {total_tokens}") + print(f" Total time: {total_time:.1f}s") + print() + print(f" Approx TPS (avg): {avg_tps:.2f} t/s") + print(f" Approx TPS (min): {min_tps:.2f} t/s") + print(f" Approx TPS (max): {max_tps:.2f} t/s") + print() + + # Verdict + if avg_tps >= 10: + print(" ✅ TARGET ACHIEVED: 10+ t/s!") + elif avg_tps >= 8: + print(" ⚠️ CLOSE TO TARGET: Consider further tuning") + else: + print(f" ❌ BELOW TARGET: {avg_tps:.1f} t/s < 10 t/s") + + print() + print(" ⚡ IMPORTANT: The 'approx' speed includes prompt eval overhead.") + print(" ⚡ Check the server console/log for exact 'eval time' t/s value,") + print(" ⚡ which shows pure token generation speed (always higher).") + print("=" * 70) + +if __name__ == "__main__": + main() diff --git a/scripts/q4km_latest.txt b/scripts/q4km_latest.txt new file mode 100644 index 0000000..c4687b1 --- /dev/null +++ b/scripts/q4km_latest.txt @@ -0,0 +1,5 @@ +pure-GPU nommap small | 62.29 | GPU | VRAM:22975 | ub=128 b=512 t=4 +pure-GPU ts=0.5,0.5 | 63.89 | GPU | VRAM:23002 | ub=128 b=512 t=4 +tune t=2 | 64.1 | GPU | VRAM:22980 | ub=128 b=512 t=2 +tune t=6 | 64.18 | GPU | VRAM:22982 | ub=128 b=512 t=6 +tune t=8 | 63.11 | GPU | VRAM:22980 | ub=128 b=512 t=8 \ No newline at end of file diff --git a/scripts/quick_pptest.mjs b/scripts/quick_pptest.mjs new file mode 100644 index 0000000..69d923f --- /dev/null +++ b/scripts/quick_pptest.mjs @@ -0,0 +1,31 @@ +// Quick PP+TG speed test +const BASE = "http://127.0.0.1:8000"; + +async function test(label, prompt, maxTok) { + const t0 = Date.now(); + const r = await fetch(`${BASE}/v1/chat/completions`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ model: "m", messages: [{ role: "user", content: prompt }], max_tokens: maxTok, temperature: 0 }), + signal: AbortSignal.timeout(600000), + }); + const d = await r.json(); + const dt = (Date.now() - t0) / 1000; + const u = d.usage || {}; + const pp = u.prompt_tokens || 0; + const tg = u.completion_tokens || 0; + const ppSpeed = pp > 0 ? (pp / dt).toFixed(1) : "?"; + const tgSpeed = tg > 0 ? (tg / dt).toFixed(1) : "?"; + console.log(`${label} | PP:${pp}tok ${ppSpeed}t/s | TG:${tg}tok ${tgSpeed}t/s | ${dt.toFixed(1)}s`); +} + +const short = "Count 1 to 20."; +const long = "x".repeat(3000) + " Summarize above in 3 words."; +const code = Array(200).fill("function foo(x) { return x * 2 + Math.random(); }").join("\n") + "\n\nRefactor above to arrow functions. Show first 5 lines."; + +await test("warmup", short, 20); +await test("SHORT", short, 200); +await test("3K-PP", long, 100); +await test("10K-CODE", code, 100); +await test("TG-500", short, 500); +console.log("DONE"); diff --git a/scripts/qwen_fullgpu_challenge.mjs b/scripts/qwen_fullgpu_challenge.mjs new file mode 100644 index 0000000..209488d --- /dev/null +++ b/scripts/qwen_fullgpu_challenge.mjs @@ -0,0 +1,345 @@ +/** + * Qwen3.5 Full-GPU Challenge — VRAM 극한 최적화 벤치마크 + * ===================================================== + * 목표: Qwen3.5-35B-A3B를 24GB 듀얼 3060에 100% GPU로 올리기 + * + * 테스트 모델: + * 1. UD-IQ4_NL (16.6 GB) — 확실히 올라감, 기준선 + * 2. MXFP4_MOE (20.1 GB) — 도전! VRAM 극한 최적화 + * 3. Q4_K_M (20.5 GB) — 대조군 (n-cpu-moe=5) + * + * VRAM 절감 전략: + * A. 배치 최소화: -ub 64 -b 256 (computation buffer 축소) + * B. split-mode row (GPU간 더 균등한 분배) + * C. tensor-split 수동 밸런싱 + * D. no-mmap (메모리 관리 최적화) + * E. defrag-thold (KV 캐시 파편화 방지) + * + * Run: node scripts/qwen_fullgpu_challenge.mjs + */ + +import { spawn, execSync } from "child_process"; +import { writeFileSync, existsSync, statSync } from "fs"; + +const BASE_URL = "http://127.0.0.1:8000"; +const LLAMA = String.raw`llama_bin_run\llama-server.exe`; +const CTX = 262144; +const RUNS = 3; +const TOKENS = 200; +const BOOT_TIMEOUT = 300_000; + +const MODELS = [ + { + name: "Qwen3.5 UD-IQ4_NL", + path: String.raw`models\Qwen3.5-35B-A3B-UD-IQ4_NL.gguf`, + sizeGB: 16.6, + }, + { + name: "Qwen3.5 MXFP4_MOE", + path: String.raw`models\Qwen3.5-35B-A3B-MXFP4_MOE.gguf`, + sizeGB: 20.11, + }, + { + name: "Qwen3.5 Q4_K_M", + path: String.raw`models\Qwen3.5-35B-A3B-Q4_K_M.gguf`, + sizeGB: 20.5, + }, +]; + +const ALL = []; +let proc = null; +const log = (m) => console.log(`[${new Date().toLocaleTimeString("ko-KR",{hour12:false})}] ${m}`); +const sleep = (ms) => new Promise(r => setTimeout(r, ms)); + +async function kill() { + if (proc) { try { proc.kill("SIGKILL"); } catch {} proc = null; } + try { execSync("taskkill /F /IM llama-server.exe", { stdio: "ignore" }); } catch {} + await sleep(5000); +} + +function vram() { + try { + return execSync('nvidia-smi --query-gpu=index,memory.used,memory.total --format=csv,noheader,nounits', + { encoding: "utf-8", timeout: 5000 }).trim().split("\n").map(l => { + const [g, u, t] = l.split(",").map(s => parseInt(s)); + return { gpu: g, used: u, total: t }; + }); + } catch { return []; } +} + +function startServer(modelPath, p) { + const args = [ + "--model", modelPath, "-ngl", "999", + "-c", String(CTX), "-np", "1", "-fa", "on", + "--cache-type-k", p.ctk || "q4_0", + "--cache-type-v", p.ctv || "q4_0", + "-ub", String(p.ub || 512), "-b", String(p.b || 2048), + "-t", String(p.t || 4), "-tb", String(p.t || 4), + "--prio", "3", "--poll", "50", "--mlock", + "--port", "8000", "--host", "0.0.0.0", + ]; + + // GPU offload strategy + if (p.cpuMoe) args.push("--cpu-moe"); + else if (p.nCpuMoe) args.push("--n-cpu-moe", String(p.nCpuMoe)); + + // VRAM saving options + if (p.splitMode) args.push("--split-mode", p.splitMode); + if (p.tensorSplit) args.push("--tensor-split", p.tensorSplit); + if (p.noMmap) args.push("--no-mmap"); + if (p.defragThold) args.push("--defrag-thold", String(p.defragThold)); + if (p.noKvOffload) args.push("--no-kv-offload"); + + const cmdStr = args.join(" "); + log(` CMD: ...${cmdStr.slice(-80)}`); + proc = spawn(LLAMA, args, { cwd: process.cwd(), stdio: ["ignore", "pipe", "pipe"] }); + return proc; +} + +async function waitReady(timeout = BOOT_TIMEOUT) { + const t0 = Date.now(); + while (Date.now() - t0 < timeout) { + try { + const r = await fetch(`${BASE_URL}/health`, { signal: AbortSignal.timeout(3000) }); + const d = await r.json(); + if (d.status === "ok") return { ok: true, boot: (Date.now() - t0) / 1000 }; + } catch {} + await sleep(3000); + } + return { ok: false, boot: timeout / 1000 }; +} + +async function bench(n = TOKENS) { + const t0 = Date.now(); + const r = await fetch(`${BASE_URL}/v1/chat/completions`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: "m", + messages: [{ role: "user", content: "Count from 1 to 50, each on new line." }], + max_tokens: n, temperature: 0, + }), + signal: AbortSignal.timeout(600_000), + }); + const d = await r.json(); + const dt = (Date.now() - t0) / 1000; + const ct = d.usage?.completion_tokens || 0; + return { tps: ct / dt, ct, dt }; +} + +async function testConfig(model, label, params) { + await kill(); + log(` [${label}] Starting...`); + startServer(model.path, params); + const { ok, boot } = await waitReady(); + if (!ok) { + log(` [${label}] ✗ FAILED (timeout ${BOOT_TIMEOUT/1000}s)`); + await kill(); + return null; + } + + const v = vram(); + const totalUsed = v.reduce((a, g) => a + g.used, 0); + const vs = v.map(g => `GPU${g.gpu}:${g.used}/${g.total}`).join(" | "); + log(` [${label}] ✓ Boot:${boot.toFixed(0)}s | VRAM: ${vs} (total: ${totalUsed} MiB)`); + + try { await bench(20); } catch {} // warmup + + const speeds = []; + for (let i = 0; i < RUNS; i++) { + try { + const r = await bench(); + speeds.push(r.tps); + log(` Run${i+1}: ${r.tps.toFixed(2)} t/s`); + } catch (e) { + log(` Run${i+1}: ERR ${e.message}`); + } + } + await kill(); + + if (!speeds.length) return null; + const avg = speeds.reduce((a,b)=>a+b) / speeds.length; + const best = Math.max(...speeds); + log(` [${label}] ⇒ AVG:${avg.toFixed(2)} BEST:${best.toFixed(2)} t/s`); + + const res = { + model: model.name, label, + avg_tps: +avg.toFixed(2), best_tps: +best.toFixed(2), + boot: +boot.toFixed(1), + vram_total: totalUsed, vram: v, + params: { ...params, ngl: 999, ctk: params.ctk||"q4_0", ctv: params.ctv||"q4_0" }, + gpu_only: !params.cpuMoe && !params.nCpuMoe, + }; + ALL.push(res); + writeFileSync("scripts/qwen_fullgpu_results.json", JSON.stringify(ALL, null, 2)); + return res; +} + +// ─── Test Strategies ─────────────────────────────────────────── + +async function testModel(model) { + log(`\n${"#".repeat(65)}`); + log(` ${model.name} (${model.sizeGB} GB)`); + if (!existsSync(model.path)) { log(" ✗ File not found!"); return null; } + log(`${"#".repeat(65)}`); + + let best = null; + const update = (r) => { if (r && (!best || r.avg_tps > best.avg_tps)) best = r; }; + + // ── Strategy 1: Pure GPU, default settings ── + log(`\n ── Strategy 1: Pure GPU (default) ──`); + update(await testConfig(model, "pure-GPU default", { + t: 4, ub: 512, b: 2048 + })); + + // ── Strategy 2: Pure GPU, minimal batch (VRAM saver) ── + log(`\n ── Strategy 2: Pure GPU, minimal batch ──`); + update(await testConfig(model, "pure-GPU minbatch", { + t: 4, ub: 64, b: 256 + })); + + // ── Strategy 3: Pure GPU, small batch + no-mmap ── + log(`\n ── Strategy 3: Pure GPU + no-mmap + small batch ──`); + update(await testConfig(model, "pure-GPU nommap small", { + t: 4, ub: 128, b: 512, noMmap: true + })); + + // ── Strategy 4: Pure GPU, split-mode row ── + log(`\n ── Strategy 4: Pure GPU + split-mode row ──`); + update(await testConfig(model, "pure-GPU row-split", { + t: 4, ub: 128, b: 512, splitMode: "row" + })); + + // ── Strategy 5: Pure GPU, tensor-split manual balance ── + log(`\n ── Strategy 5: Pure GPU + tensor-split 0.5,0.5 ──`); + update(await testConfig(model, "pure-GPU ts=0.5,0.5", { + t: 4, ub: 128, b: 512, tensorSplit: "0.5,0.5" + })); + + // ── Strategy 6: Pure GPU, defrag + all tricks ── + log(`\n ── Strategy 6: Pure GPU ALL tricks ──`); + update(await testConfig(model, "pure-GPU all-tricks", { + t: 4, ub: 64, b: 256, noMmap: true, defragThold: 0.1 + })); + + // ── Fallback: n-cpu-moe=5 baseline ── + if (!best || !best.gpu_only) { + log(`\n ── Fallback: n-cpu-moe=5 ──`); + update(await testConfig(model, "n-cpu-moe=5 baseline", { + t: 4, ub: 256, b: 1024, nCpuMoe: 5 + })); + } + + // ── If pure GPU worked, tune batch/thread/kv ── + if (best && best.gpu_only) { + log(`\n ── Pure GPU succeeded! Fine-tuning... ──`); + const bp = best.params; + + // Thread sweep + for (const t of [2, 6, 8]) { + if (t === bp.t) continue; + update(await testConfig(model, `tune t=${t}`, { ...bp, t })); + } + + // Batch sweep + for (const [ub, b] of [[256, 1024], [512, 2048], [256, 2048]]) { + if (ub === bp.ub && b === bp.b) continue; + update(await testConfig(model, `tune ub=${ub} b=${b}`, { ...bp, ub, b })); + } + + // KV cache upgrade (extra VRAM available?) + for (const [ctk, ctv] of [["q8_0","q8_0"], ["f16","f16"]]) { + update(await testConfig(model, `tune kv=${ctk}/${ctv}`, { ...bp, ctk, ctv })); + } + } + + // ── Final verification ── + if (best) { + log(`\n ── Final verification (5 runs) ──`); + await kill(); + startServer(model.path, best.params); + const { ok, boot } = await waitReady(); + if (ok) { + const v = vram(); + try { await bench(20); } catch {} + const finals = []; + for (let i = 0; i < 5; i++) { + try { const r = await bench(); finals.push(r.tps); log(` Final ${i+1}: ${r.tps.toFixed(2)} t/s`); + } catch (e) { log(` Final ${i+1}: ERR`); } + } + await kill(); + if (finals.length > 0) { + const avg = finals.reduce((a,b)=>a+b) / finals.length; + const bst = Math.max(...finals); + log(` ★ FINAL: AVG ${avg.toFixed(2)} | BEST ${bst.toFixed(2)} t/s`); + const final = { model: model.name, label: "FINAL", + avg_tps: +avg.toFixed(2), best_tps: +bst.toFixed(2), + boot: +boot.toFixed(1), vram_total: v.reduce((a,g)=>a+g.used,0), + vram: v, params: best.params, gpu_only: best.gpu_only }; + ALL.push(final); + writeFileSync("scripts/qwen_fullgpu_results.json", JSON.stringify(ALL, null, 2)); + return final; + } + } + await kill(); + } + return best; +} + +// ─── Main ────────────────────────────────────────────────────── + +async function main() { + const t0 = Date.now(); + log("=".repeat(65)); + log(" QWEN3.5 FULL-GPU CHALLENGE — 70 t/s TARGET"); + log(" 2x RTX 3060 (24GB) | 256K Context"); + log(" " + new Date().toISOString()); + log("=".repeat(65)); + vram().forEach(g => log(` GPU${g.gpu}: ${g.used}/${g.total} MiB`)); + + const winners = []; + for (const model of MODELS) { + const w = await testModel(model); + if (w) winners.push(w); + } + + // ─── Summary ────────────────────────────────────────────── + const elapsed = ((Date.now() - t0) / 60000).toFixed(1); + winners.sort((a, b) => b.avg_tps - a.avg_tps); + + const lines = [ + `Qwen3.5 Full-GPU Challenge — ${new Date().toISOString()}`, + `2x RTX 3060 12GB | 256K Context | ${ALL.length} configs | ${elapsed} min`, + "", "=".repeat(55), " RANKING", "=".repeat(55), + ]; + for (let i = 0; i < winners.length; i++) { + const w = winners[i], p = w.params; + const gpu = w.gpu_only ? "★ FULL GPU" : "⚠ CPU offload"; + lines.push("", ` #${i+1}: ${w.model} [${gpu}]`); + lines.push(` AVG: ${w.avg_tps} t/s | BEST: ${w.best_tps} t/s | Boot: ${w.boot}s`); + lines.push(` VRAM: ${w.vram_total} MiB total`); + const flags = []; + if (p.splitMode) flags.push(`split=${p.splitMode}`); + if (p.tensorSplit) flags.push(`ts=${p.tensorSplit}`); + if (p.noMmap) flags.push("no-mmap"); + if (p.nCpuMoe) flags.push(`n-cpu-moe=${p.nCpuMoe}`); + lines.push(` t=${p.t} ub=${p.ub} b=${p.b} kv=${p.ctk}/${p.ctv} ${flags.join(" ")}`); + } + + if (winners.length > 0) { + const c = winners[0]; + lines.push("", "=".repeat(55)); + lines.push(` ★ CHAMPION: ${c.model} — ${c.avg_tps} t/s [${c.gpu_only?"FULL GPU":"CPU offload"}]`); + lines.push("=".repeat(55)); + } + + const summary = lines.join("\n"); + console.log("\n" + summary); + writeFileSync("scripts/qwen_fullgpu_summary.txt", summary, "utf-8"); + writeFileSync("scripts/qwen_fullgpu_results.json", JSON.stringify(ALL, null, 2)); + log(`\n Saved: qwen_fullgpu_results.json + qwen_fullgpu_summary.txt`); + log(" DONE!"); + await kill(); +} + +main().catch(e => { console.error("FATAL:", e); process.exit(1); }); diff --git a/scripts/qwen_fullgpu_results.json b/scripts/qwen_fullgpu_results.json new file mode 100644 index 0000000..3515bda --- /dev/null +++ b/scripts/qwen_fullgpu_results.json @@ -0,0 +1,834 @@ +[ + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "pure-GPU minbatch", + "avg_tps": 65.11, + "best_tps": 65.49, + "boot": 9, + "vram_total": 19177, + "vram": [ + { + "gpu": 0, + "used": 10039, + "total": 12288 + }, + { + "gpu": 1, + "used": 9138, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 64, + "b": 256, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "pure-GPU nommap small", + "avg_tps": 65.01, + "best_tps": 65.36, + "boot": 6, + "vram_total": 19672, + "vram": [ + { + "gpu": 0, + "used": 10342, + "total": 12288 + }, + { + "gpu": 1, + "used": 9330, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 128, + "b": 512, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "pure-GPU row-split", + "avg_tps": 13.65, + "best_tps": 14.82, + "boot": 9, + "vram_total": 19427, + "vram": [ + { + "gpu": 0, + "used": 10311, + "total": 12288 + }, + { + "gpu": 1, + "used": 9116, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 128, + "b": 512, + "splitMode": "row", + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "pure-GPU ts=0.5,0.5", + "avg_tps": 64.92, + "best_tps": 65.23, + "boot": 9, + "vram_total": 19664, + "vram": [ + { + "gpu": 0, + "used": 10334, + "total": 12288 + }, + { + "gpu": 1, + "used": 9330, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 128, + "b": 512, + "tensorSplit": "0.5,0.5", + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "pure-GPU all-tricks", + "avg_tps": 64.72, + "best_tps": 64.89, + "boot": 6, + "vram_total": 19171, + "vram": [ + { + "gpu": 0, + "used": 10033, + "total": 12288 + }, + { + "gpu": 1, + "used": 9138, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 64, + "b": 256, + "noMmap": true, + "defragThold": 0.1, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "tune t=2", + "avg_tps": 64.87, + "best_tps": 65.13, + "boot": 9, + "vram_total": 19170, + "vram": [ + { + "gpu": 0, + "used": 10032, + "total": 12288 + }, + { + "gpu": 1, + "used": 9138, + "total": 12288 + } + ], + "params": { + "t": 2, + "ub": 64, + "b": 256, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "tune t=6", + "avg_tps": 64.88, + "best_tps": 65.17, + "boot": 9, + "vram_total": 19168, + "vram": [ + { + "gpu": 0, + "used": 10030, + "total": 12288 + }, + { + "gpu": 1, + "used": 9138, + "total": 12288 + } + ], + "params": { + "t": 6, + "ub": 64, + "b": 256, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "tune t=8", + "avg_tps": 64.5, + "best_tps": 64.77, + "boot": 9, + "vram_total": 19168, + "vram": [ + { + "gpu": 0, + "used": 10030, + "total": 12288 + }, + { + "gpu": 1, + "used": 9138, + "total": 12288 + } + ], + "params": { + "t": 8, + "ub": 64, + "b": 256, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "tune ub=256 b=1024", + "avg_tps": 64.73, + "best_tps": 64.98, + "boot": 9, + "vram_total": 20640, + "vram": [ + { + "gpu": 0, + "used": 10928, + "total": 12288 + }, + { + "gpu": 1, + "used": 9712, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 256, + "b": 1024, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "tune ub=256 b=2048", + "avg_tps": 63.69, + "best_tps": 64.94, + "boot": 12, + "vram_total": 20614, + "vram": [ + { + "gpu": 0, + "used": 10902, + "total": 12288 + }, + { + "gpu": 1, + "used": 9712, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 256, + "b": 2048, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "tune kv=q8_0/q8_0", + "avg_tps": 64.78, + "best_tps": 65.08, + "boot": 9, + "vram_total": 20422, + "vram": [ + { + "gpu": 0, + "used": 10644, + "total": 12288 + }, + { + "gpu": 1, + "used": 9778, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 64, + "b": 256, + "ngl": 999, + "ctk": "q8_0", + "ctv": "q8_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "tune kv=f16/f16", + "avg_tps": 65.53, + "best_tps": 65.81, + "boot": 9, + "vram_total": 22812, + "vram": [ + { + "gpu": 0, + "used": 11846, + "total": 12288 + }, + { + "gpu": 1, + "used": 10966, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 64, + "b": 256, + "ngl": 999, + "ctk": "f16", + "ctv": "f16" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 UD-IQ4_NL", + "label": "FINAL", + "avg_tps": 66.31, + "best_tps": 66.53, + "boot": 9, + "vram_total": 22811, + "vram": [ + { + "gpu": 0, + "used": 11845, + "total": 12288 + }, + { + "gpu": 1, + "used": 10966, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 64, + "b": 256, + "ngl": 999, + "ctk": "f16", + "ctv": "f16" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "pure-GPU minbatch", + "avg_tps": 63.06, + "best_tps": 64.16, + "boot": 12, + "vram_total": 22747, + "vram": [ + { + "gpu": 0, + "used": 11895, + "total": 12288 + }, + { + "gpu": 1, + "used": 10852, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 64, + "b": 256, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "pure-GPU nommap small", + "avg_tps": 63.75, + "best_tps": 63.98, + "boot": 9, + "vram_total": 22579, + "vram": [ + { + "gpu": 0, + "used": 11797, + "total": 12288 + }, + { + "gpu": 1, + "used": 10782, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 128, + "b": 512, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "pure-GPU ts=0.5,0.5", + "avg_tps": 62.88, + "best_tps": 63.9, + "boot": 12, + "vram_total": 22578, + "vram": [ + { + "gpu": 0, + "used": 11796, + "total": 12288 + }, + { + "gpu": 1, + "used": 10782, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 128, + "b": 512, + "tensorSplit": "0.5,0.5", + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "pure-GPU all-tricks", + "avg_tps": 62.55, + "best_tps": 63.71, + "boot": 9, + "vram_total": 22743, + "vram": [ + { + "gpu": 0, + "used": 11891, + "total": 12288 + }, + { + "gpu": 1, + "used": 10852, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 64, + "b": 256, + "noMmap": true, + "defragThold": 0.1, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "tune t=2", + "avg_tps": 63.07, + "best_tps": 64.08, + "boot": 9, + "vram_total": 22601, + "vram": [ + { + "gpu": 0, + "used": 11819, + "total": 12288 + }, + { + "gpu": 1, + "used": 10782, + "total": 12288 + } + ], + "params": { + "t": 2, + "ub": 128, + "b": 512, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "tune t=6", + "avg_tps": 63.58, + "best_tps": 64.04, + "boot": 9, + "vram_total": 22583, + "vram": [ + { + "gpu": 0, + "used": 11801, + "total": 12288 + }, + { + "gpu": 1, + "used": 10782, + "total": 12288 + } + ], + "params": { + "t": 6, + "ub": 128, + "b": 512, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "tune t=8", + "avg_tps": 62.92, + "best_tps": 63.73, + "boot": 9, + "vram_total": 22536, + "vram": [ + { + "gpu": 0, + "used": 11754, + "total": 12288 + }, + { + "gpu": 1, + "used": 10782, + "total": 12288 + } + ], + "params": { + "t": 8, + "ub": 128, + "b": 512, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "tune ub=256 b=1024", + "avg_tps": 62.76, + "best_tps": 63.86, + "boot": 9, + "vram_total": 22874, + "vram": [ + { + "gpu": 0, + "used": 11968, + "total": 12288 + }, + { + "gpu": 1, + "used": 10906, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 256, + "b": 1024, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "tune ub=256 b=2048", + "avg_tps": 62.74, + "best_tps": 63.9, + "boot": 9, + "vram_total": 22912, + "vram": [ + { + "gpu": 0, + "used": 12006, + "total": 12288 + }, + { + "gpu": 1, + "used": 10906, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 256, + "b": 2048, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 MXFP4_MOE", + "label": "FINAL", + "avg_tps": 63.71, + "best_tps": 64.39, + "boot": 9, + "vram_total": 22566, + "vram": [ + { + "gpu": 0, + "used": 11784, + "total": 12288 + }, + { + "gpu": 1, + "used": 10782, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 128, + "b": 512, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 Q4_K_M", + "label": "pure-GPU nommap small", + "avg_tps": 62.29, + "best_tps": 63.03, + "boot": 9, + "vram_total": 22975, + "vram": [ + { + "gpu": 0, + "used": 12007, + "total": 12288 + }, + { + "gpu": 1, + "used": 10968, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 128, + "b": 512, + "noMmap": true, + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 Q4_K_M", + "label": "pure-GPU ts=0.5,0.5", + "avg_tps": 63.89, + "best_tps": 64.91, + "boot": 12, + "vram_total": 23002, + "vram": [ + { + "gpu": 0, + "used": 12034, + "total": 12288 + }, + { + "gpu": 1, + "used": 10968, + "total": 12288 + } + ], + "params": { + "t": 4, + "ub": 128, + "b": 512, + "tensorSplit": "0.5,0.5", + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 Q4_K_M", + "label": "tune t=2", + "avg_tps": 64.1, + "best_tps": 64.54, + "boot": 12, + "vram_total": 22980, + "vram": [ + { + "gpu": 0, + "used": 12012, + "total": 12288 + }, + { + "gpu": 1, + "used": 10968, + "total": 12288 + } + ], + "params": { + "t": 2, + "ub": 128, + "b": 512, + "tensorSplit": "0.5,0.5", + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 Q4_K_M", + "label": "tune t=6", + "avg_tps": 64.18, + "best_tps": 64.72, + "boot": 12, + "vram_total": 22982, + "vram": [ + { + "gpu": 0, + "used": 12014, + "total": 12288 + }, + { + "gpu": 1, + "used": 10968, + "total": 12288 + } + ], + "params": { + "t": 6, + "ub": 128, + "b": 512, + "tensorSplit": "0.5,0.5", + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + }, + { + "model": "Qwen3.5 Q4_K_M", + "label": "tune t=8", + "avg_tps": 63.11, + "best_tps": 64.02, + "boot": 12, + "vram_total": 22980, + "vram": [ + { + "gpu": 0, + "used": 12012, + "total": 12288 + }, + { + "gpu": 1, + "used": 10968, + "total": 12288 + } + ], + "params": { + "t": 8, + "ub": 128, + "b": 512, + "tensorSplit": "0.5,0.5", + "ngl": 999, + "ctk": "q4_0", + "ctv": "q4_0" + }, + "gpu_only": true + } +] \ No newline at end of file diff --git a/scripts/qwen_intermediate.csv b/scripts/qwen_intermediate.csv new file mode 100644 index 0000000..e94b673 --- /dev/null +++ b/scripts/qwen_intermediate.csv @@ -0,0 +1,12 @@ +model,label,avg,best,mode,vram,t,ub,b,kv,split,mmap +UD-IQ4_NL,pure-GPU minbatch,65.11,65.49,GPU,19177,t4,ub64,b256,q4_0/q4_0,, +UD-IQ4_NL,pure-GPU nommap small,65.01,65.36,GPU,19672,t4,ub128,b512,q4_0/q4_0,,nommap +UD-IQ4_NL,pure-GPU row-split,13.65,14.82,GPU,19427,t4,ub128,b512,q4_0/q4_0,row, +UD-IQ4_NL,pure-GPU ts=0.5,0.5,64.92,65.23,GPU,19664,t4,ub128,b512,q4_0/q4_0,, +UD-IQ4_NL,pure-GPU all-tricks,64.72,64.89,GPU,19171,t4,ub64,b256,q4_0/q4_0,,nommap +UD-IQ4_NL,tune t=2,64.87,65.13,GPU,19170,t2,ub64,b256,q4_0/q4_0,, +UD-IQ4_NL,tune t=6,64.88,65.17,GPU,19168,t6,ub64,b256,q4_0/q4_0,, +UD-IQ4_NL,tune t=8,64.5,64.77,GPU,19168,t8,ub64,b256,q4_0/q4_0,, +UD-IQ4_NL,tune ub=256 b=1024,64.73,64.98,GPU,20640,t4,ub256,b1024,q4_0/q4_0,, +UD-IQ4_NL,tune ub=256 b=2048,63.69,64.94,GPU,20614,t4,ub256,b2048,q4_0/q4_0,, +UD-IQ4_NL,tune kv=q8_0/q8_0,64.78,65.08,GPU,20422,t4,ub64,b256,q8_0/q8_0,, \ No newline at end of file diff --git a/scripts/qwen_latest.txt b/scripts/qwen_latest.txt new file mode 100644 index 0000000..5c3446e --- /dev/null +++ b/scripts/qwen_latest.txt @@ -0,0 +1,24 @@ +UD-IQ4_NL | pure-GPU minbatch | 65.11 | GPU | 19177 +UD-IQ4_NL | pure-GPU nommap small | 65.01 | GPU | 19672 +UD-IQ4_NL | pure-GPU row-split | 13.65 | GPU | 19427 +UD-IQ4_NL | pure-GPU ts=0.5,0.5 | 64.92 | GPU | 19664 +UD-IQ4_NL | pure-GPU all-tricks | 64.72 | GPU | 19171 +UD-IQ4_NL | tune t=2 | 64.87 | GPU | 19170 +UD-IQ4_NL | tune t=6 | 64.88 | GPU | 19168 +UD-IQ4_NL | tune t=8 | 64.5 | GPU | 19168 +UD-IQ4_NL | tune ub=256 b=1024 | 64.73 | GPU | 20640 +UD-IQ4_NL | tune ub=256 b=2048 | 63.69 | GPU | 20614 +UD-IQ4_NL | tune kv=q8_0/q8_0 | 64.78 | GPU | 20422 +UD-IQ4_NL | tune kv=f16/f16 | 65.53 | GPU | 22812 +UD-IQ4_NL | FINAL | 66.31 | GPU | 22811 +MXFP4_MOE | pure-GPU minbatch | 63.06 | GPU | 22747 +MXFP4_MOE | pure-GPU nommap small | 63.75 | GPU | 22579 +MXFP4_MOE | pure-GPU ts=0.5,0.5 | 62.88 | GPU | 22578 +MXFP4_MOE | pure-GPU all-tricks | 62.55 | GPU | 22743 +MXFP4_MOE | tune t=2 | 63.07 | GPU | 22601 +MXFP4_MOE | tune t=6 | 63.58 | GPU | 22583 +MXFP4_MOE | tune t=8 | 62.92 | GPU | 22536 +MXFP4_MOE | tune ub=256 b=1024 | 62.76 | GPU | 22874 +MXFP4_MOE | tune ub=256 b=2048 | 62.74 | GPU | 22912 +MXFP4_MOE | FINAL | 63.71 | GPU | 22566 +Q4_K_M | pure-GPU nommap small | 62.29 | GPU | 22975 \ No newline at end of file diff --git a/scripts/test_20ts.txt b/scripts/test_20ts.txt new file mode 100644 index 0000000000000000000000000000000000000000..90f3e9ebaed708d123463b12f3a956e964ff6b68 GIT binary patch literal 1384 zcmchX&2G~`6ot>CD<s}wR<sBR4JtIsqMMLPY|2kTAVsB$;-obsZg89mgy0!?A0D7$ z!y^IT8N0-BRk1^sJ$GjA+;i@^*Yo>VrZXiPE2ztRIqNFLE?CbQhnniXX3=wrJ=TJG zt|fcNtPi7iS8Z);S2uM-8y9bW25tQCT2SZvw+ma|xl=<4#2FDM)f_Cx{6Nf<wfDrG z$5mq$YJu_&D`=0mdCENPb*u+4aHJOBkGz4EJ>9Bdf7SngoOh@m=ifsyb3Q@QT%Ys= z9VghAKK0R6Ibg5QA$p9_CdD_!N@B&fI9pcmx|)Tkdl@r9-+$Gw3sMoQlELsc_yZm3 zF5?SO-38{}N8b^nE0?mC)lfpL@_xjclHYSI_t1)YpZGbaa&k|ITxMxiQk@gE$KEV< zR>uCyyHv8SXUX&grtZ~O@EC!$WPVDtt1P$Hc)pD@#~bJ+Os((=&8#M@6vRA@wmaSW zB`6Z~`ERU2g}PwxC}y)BWfT?4*c}lqfYH-a>Uw~teeJ{hknu4)Z8~<B{@t$LHqkoF z{M7SyJ*`)OcTdlvVj0<&2(Qs%NH#6L6TGaAHC=hQs>Aq4dDZ-8_&YayJG**kIc^d0 z{U1Nljvm6xmiuF+gIO8Nx5io2;U2!e6*_cKoxc%%?^xr_YxUS{oLW#L|NZW!X?1p8 z+vbTOC<%OgLoBKjS2b>)n!_$9W3y|j|0U*8ew~s3PG{1pZm4rAxWlYneeLrHS<T&H literal 0 HcmV?d00001 diff --git a/scripts/tune_122b_20ts.mjs b/scripts/tune_122b_20ts.mjs new file mode 100644 index 0000000..f700592 --- /dev/null +++ b/scripts/tune_122b_20ts.mjs @@ -0,0 +1,64 @@ +import { exec, spawn } from 'child_process'; + +const delay = ms => new Promise(res => setTimeout(res, ms)); + +async function runTest(modelArgs, envVars, name) { + console.log(`\n===========================================`); + console.log(`Testing: ${name}`); + console.log(`Args: ${modelArgs}`); + + return new Promise(async (resolve) => { + await new Promise(r => exec('taskkill /F /IM llama-server.exe', r)); + await delay(2000); + + const env = { ...process.env, ...envVars }; + const server = spawn('llama_bin_run\\llama-server.exe', modelArgs.split(' '), { + detached: true, + stdio: 'ignore', + env + }); + + let ready = false; + for (let i = 0; i < 40; i++) { + try { + const res = await fetch('http://127.0.0.1:8000/health', { timeout: 2000 }); + if (res.status === 200) { + ready = true; + break; + } + } catch (e) {} + await delay(3000); + } + + if (!ready) { + console.log(`[${name}] FAILED TO BOOT`); + exec('taskkill /F /IM llama-server.exe'); + resolve({ success: false }); + return; + } + + console.log(`[${name}] Server Ready! Running benchmark...`); + exec('node scripts/quick_pptest.mjs', (err, stdout, stderr) => { + console.log(stdout || stderr); + exec('taskkill /F /IM llama-server.exe'); + resolve({ success: true }); + }); + }); +} + +async function main() { + const baseLineArgs = `--model models\\Q4_K_M\\Qwen3.5-122B-A10B-Q4_K_M-00001-of-00003.gguf -ngl 999 -c 8192 -np 1 -fa on --cache-type-k q4_0 --cache-type-v q4_0 -ub 256 -b 1024 -t 6 -tb 6 --prio 3 --fit off --port 8000 --host 0.0.0.0`; + + // 1. Dual GPU, Split Mode Layer, Maximize VRAM usage (estimate 32 layers offloaded out of 48) + await runTest(`${baseLineArgs} --split-mode layer --n-cpu-moe 32`, {}, "122B: n-cpu-moe 32 + sm layer"); + + // 2. Dual GPU, Split Mode Layer, even more aggressive GPU usage + await runTest(`${baseLineArgs} --split-mode layer --n-cpu-moe 28`, {}, "122B: n-cpu-moe 28 + sm layer"); + + // 3. Fallback to 36 if OOM happens on 32/28 + await runTest(`${baseLineArgs} --split-mode layer --n-cpu-moe 36`, {}, "122B: n-cpu-moe 36 + sm layer"); + + console.log("\nALL TESTS COMPLETED"); +} + +main(); diff --git a/scripts/tune_exact.mjs b/scripts/tune_exact.mjs new file mode 100644 index 0000000..52e4367 --- /dev/null +++ b/scripts/tune_exact.mjs @@ -0,0 +1,72 @@ +import { exec, spawn } from 'child_process'; + +const delay = ms => new Promise(res => setTimeout(res, ms)); + +async function runTest(modelArgs, envVars, name) { + console.log(`\n===========================================`); + console.log(`Testing: ${name}`); + console.log(`Env: ${JSON.stringify(envVars)}`); + console.log(`Args: ${modelArgs}`); + + return new Promise(async (resolve) => { + await new Promise(r => exec('taskkill /F /IM llama-server.exe', r)); + await delay(2000); + + const env = { ...process.env, ...envVars }; + const server = spawn('llama_bin_run\\llama-server.exe', modelArgs.split(' '), { + detached: true, + stdio: 'ignore', + env + }); + + let ready = false; + + for (let i = 0; i < 40; i++) { + try { + const res = await fetch('http://127.0.0.1:8000/health', { timeout: 2000 }); + if (res.status === 200) { + ready = true; + break; + } + } catch (e) {} + await delay(3000); + } + + if (!ready) { + console.log(`[${name}] FAILED TO BOOT`); + exec('taskkill /F /IM llama-server.exe'); + resolve({ success: false }); + return; + } + + console.log(`[${name}] Server Ready! Running speed test...`); + exec('node scripts/quick_pptest.mjs', (err, stdout, stderr) => { + console.log(stdout || stderr); + exec('taskkill /F /IM llama-server.exe'); + resolve({ success: true }); + }); + }); +} + +async function main() { + // 1. 122B-A10B: Pure GPU offload (No n-cpu-moe at all) + // -ngl 999 will offload all 48 layers to the NVIDIA driver (triggering Shared VRAM fallback on Windows) + const args122B = `--model models\\Q4_K_M\\Qwen3.5-122B-A10B-Q4_K_M-00001-of-00003.gguf -ngl 999 -c 32768 -np 1 -fa on --cache-type-k q4_0 --cache-type-v q4_0 -ub 512 -b 2048 -t 6 -tb 6 --prio 3 --fit off --port 8000 --host 0.0.0.0`; + await runTest(args122B, {}, "122B-A10B: Pure GPU (NVIDIA Shared Memory Fallback)"); + + // 2. 35B-A3B: Pure GPU tuning to hit 70 t/s + // Base configuration from previous full-gpu run: + const args35B = `--model models\\Qwen3.5-35B-A3B-Q4_K_M.gguf -ngl 999 -c 262144 -np 1 -fa on --cache-type-k q4_0 --cache-type-v q4_0 -ub 128 -b 512 -t 6 -tb 6 --prio 3 --fit off --port 8000 --host 0.0.0.0`; + + // We already got ~64 t/s basically. + // Let's try MMQ for custom matrix multiplication which is often faster on Ampere for low batch generation + await runTest(args35B, { GGML_CUDA_FORCE_MMQ: "1" }, "35B-A3B: Force MMQ = 1"); + + // Try increasing threads to 12 just in case + const args35B_t12 = args35B.replace("-t 6 -tb 6", "-t 12 -tb 12"); + await runTest(args35B_t12, { GGML_CUDA_FORCE_MMQ: "1" }, "35B-A3B: Threads 12 + MMQ"); + + console.log("\nALL TESTS COMPLETED"); +} + +main(); diff --git a/scripts/tune_models.mjs b/scripts/tune_models.mjs new file mode 100644 index 0000000..1726f1f --- /dev/null +++ b/scripts/tune_models.mjs @@ -0,0 +1,84 @@ +import { exec, spawn } from 'child_process'; + +const delay = ms => new Promise(res => setTimeout(res, ms)); + +async function runTest(modelArgs, name) { + console.log(`\n===========================================`); + console.log(`Testing: ${name}`); + console.log(`Args: ${modelArgs}`); + + return new Promise(async (resolve) => { + // Kill existing + await new Promise(r => exec('taskkill /F /IM llama-server.exe', r)); + await delay(2000); + + const server = spawn('llama_bin_run\\llama-server.exe', modelArgs.split(' '), { + detached: true, + stdio: 'ignore' + }); + + let ready = false; + let oom = false; + + for (let i = 0; i < 40; i++) { + try { + const res = await fetch('http://127.0.0.1:8000/health', { timeout: 2000 }); + if (res.status === 200) { + ready = true; + break; + } + } catch (e) {} + await delay(3000); + } + + if (!ready) { + console.log(`[${name}] FAILED TO BOOT (Likely OOM)`); + exec('taskkill /F /IM llama-server.exe'); + resolve({ success: false }); + return; + } + + console.log(`[${name}] Server Ready! Running benchmark...`); + // Run pptest + exec('node scripts/quick_pptest.mjs', (err, stdout, stderr) => { + console.log(stdout || stderr); + + // Extract TG and PP from TG-500 + const tgMatch = stdout.match(/TG-500 \| PP:\d+tok \d+\.\dt\/s \| TG:\d+tok (\d+\.\d+)t\/s/); + const ppMatch = stdout.match(/10K-CODE \| PP:\d+tok (\d+\.\d+)t\/s/); + + const tg = tgMatch ? parseFloat(tgMatch[1]) : 0; + const pp = ppMatch ? parseFloat(ppMatch[1]) : 0; + + exec('taskkill /F /IM llama-server.exe'); + resolve({ success: true, tg, pp }); + }); + }); +} + +async function main() { + // 1. Qwen 35B Tuning: We need 70 t/s. Let's try 1-3 layers of n-cpu-moe to unlock ub=512 + const args35B_base = `--model models\\Qwen3.5-35B-A3B-Q4_K_M.gguf -ngl 999 -c 262144 -np 1 -fa on --cache-type-k q4_0 --cache-type-v q4_0 -b 512 -t 6 -tb 6 --prio 3 --fit off --port 8000 --host 0.0.0.0`; + + // Test 1: n-cpu-moe 1, ub 512 + await runTest(`${args35B_base} -ub 512 --n-cpu-moe 1`, "Qwen-35B: moe=1, ub=512"); + + // Test 2: n-cpu-moe 2, ub 512 + await runTest(`${args35B_base} -ub 512 --n-cpu-moe 2`, "Qwen-35B: moe=2, ub=512"); + + // Test 3: n-cpu-moe 4, ub 512 + await runTest(`${args35B_base} -ub 512 --n-cpu-moe 4`, "Qwen-35B: moe=4, ub=512"); + + // 2. 122B Tuning: Find optimal n-cpu-moe + const args122B_base = `--model models\\Q4_K_M\\Qwen3.5-122B-A10B-Q4_K_M-00001-of-00003.gguf -ngl 999 -c 32768 -np 1 -fa on --cache-type-k q4_0 --cache-type-v q4_0 -ub 512 -b 2048 -t 6 -tb 6 --prio 3 --fit off --port 8000 --host 0.0.0.0`; + + // Since 48 leaves 16GB free, each layer is ~1.5GB total, meaning ~0.75GB per GPU. + // Let's try 38, 35, 30 + await runTest(`${args122B_base} --n-cpu-moe 38`, "Qwen-122B: moe=38"); + await runTest(`${args122B_base} --n-cpu-moe 30`, "Qwen-122B: moe=30"); + await runTest(`${args122B_base} --n-cpu-moe 22`, "Qwen-122B: moe=22"); + + console.log("Tuning finished."); +} + +main(); diff --git a/scripts/tune_results_gemma4_256k.json b/scripts/tune_results_gemma4_256k.json new file mode 100644 index 0000000..d6933c0 --- /dev/null +++ b/scripts/tune_results_gemma4_256k.json @@ -0,0 +1,591 @@ +[ + { + "ngl": 22, + "t": 8, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.22049935826915, + "best_tps": 25.971732307567606, + "vram_used": 11953, + "vram_total": 12288, + "label": "ngl=22" + }, + { + "ngl": 21, + "t": 8, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.805518952775174, + "best_tps": 25.953896683689454, + "vram_used": 11942, + "vram_total": 12288, + "label": "ngl=21" + }, + { + "ngl": 20, + "t": 8, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 23.537353232262834, + "best_tps": 24.32109262330477, + "vram_used": 11972, + "vram_total": 12288, + "label": "ngl=20" + }, + { + "ngl": 21, + "t": 2, + "tb": 2, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 20.167581352340264, + "best_tps": 20.701192443418005, + "vram_used": 11969, + "vram_total": 12288, + "label": "t=2 | tb=2" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.689104997668554, + "best_tps": 26.328541632880874, + "vram_used": 11975, + "vram_total": 12288, + "label": "t=4 | tb=4" + }, + { + "ngl": 21, + "t": 4, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.294470150452725, + "best_tps": 26.541251363470614, + "vram_used": 11984, + "vram_total": 12288, + "label": "t=4 | tb=8" + }, + { + "ngl": 21, + "t": 6, + "tb": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.307859289404675, + "best_tps": 26.292208504543133, + "vram_used": 11984, + "vram_total": 12288, + "label": "t=6 | tb=6" + }, + { + "ngl": 21, + "t": 6, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.230599923243314, + "best_tps": 26.366065850165732, + "vram_used": 11983, + "vram_total": 12288, + "label": "t=6 | tb=8" + }, + { + "ngl": 21, + "t": 8, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.113108026759278, + "best_tps": 26.123872617669583, + "vram_used": 11984, + "vram_total": 12288, + "label": "t=8 | tb=8" + }, + { + "ngl": 21, + "t": 8, + "tb": 12, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.05545428888364, + "best_tps": 26.06377500079152, + "vram_used": 11983, + "vram_total": 12288, + "label": "t=8 | tb=12" + }, + { + "ngl": 21, + "t": 10, + "tb": 10, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 24.706926870374986, + "best_tps": 25.03033604251865, + "vram_used": 11984, + "vram_total": 12288, + "label": "t=10 | tb=10" + }, + { + "ngl": 21, + "t": 12, + "tb": 12, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 22.468055564001904, + "best_tps": 23.425983251691825, + "vram_used": 11989, + "vram_total": 12288, + "label": "t=12 | tb=12" + }, + { + "ngl": 21, + "t": 16, + "tb": 16, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 21.176973905195442, + "best_tps": 21.482429642395456, + "vram_used": 12021, + "vram_total": 12288, + "label": "t=16 | tb=16" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 128, + "b": 512, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.545748810106186, + "best_tps": 26.344547829145817, + "vram_used": 11986, + "vram_total": 12288, + "label": "ub=128 | b=512" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 256, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.503875205368377, + "best_tps": 26.393548686102108, + "vram_used": 11981, + "vram_total": 12288, + "label": "ub=256 | b=1024" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 256, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.46500292415627, + "best_tps": 26.2726382287537, + "vram_used": 11981, + "vram_total": 12288, + "label": "ub=256 | b=2048" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 512, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.50982209452459, + "best_tps": 26.292282671074723, + "vram_used": 12020, + "vram_total": 12288, + "label": "ub=512 | b=1024" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.39646674356899, + "best_tps": 26.28106356028714, + "vram_used": 12020, + "vram_total": 12288, + "label": "ub=512 | b=2048" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.471945933724726, + "best_tps": 26.268422652962233, + "vram_used": 12021, + "vram_total": 12288, + "label": "ub=512 | b=4096" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.722119623856702, + "best_tps": 26.497264927416403, + "vram_used": 12019, + "vram_total": 12288, + "label": "ub=1024 | b=2048" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.665819493145943, + "best_tps": 26.301163428594148, + "vram_used": 12019, + "vram_total": 12288, + "label": "ub=1024 | b=4096" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.464915272955533, + "best_tps": 26.40667691713752, + "vram_used": 12019, + "vram_total": 12288, + "label": "ctk=q4_0 | ctv=q4_0" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.489715990281564, + "best_tps": 25.884133821146627, + "vram_used": 12011, + "vram_total": 12288, + "label": "ctk=q8_0 | ctv=q8_0" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q4_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 22.751034104721082, + "best_tps": 22.91250972782414, + "vram_used": 12017, + "vram_total": 12288, + "label": "ctk=q4_0 | ctv=q8_0" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "f16", + "ctv": "f16", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 24.745831571513975, + "best_tps": 25.53926086004382, + "vram_used": 11985, + "vram_total": 12288, + "label": "ctk=f16 | ctv=f16" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.21575943186602, + "best_tps": 25.796865637378264, + "vram_used": 12013, + "vram_total": 12288, + "label": "mmap=True | poll=50 | prio=2" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": false, + "prio": 2, + "poll": 50, + "avg_tps": 23.88172807693179, + "best_tps": 24.803356430302312, + "vram_used": 12016, + "vram_total": 12288, + "label": "mmap=False | poll=50 | prio=2" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 0, + "avg_tps": 25.041321207287698, + "best_tps": 25.88479834694897, + "vram_used": 12017, + "vram_total": 12288, + "label": "mmap=True | poll=0 | prio=2" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 100, + "avg_tps": 25.27990666474703, + "best_tps": 26.034861156695197, + "vram_used": 12017, + "vram_total": 12288, + "label": "mmap=True | poll=100 | prio=2" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 3, + "poll": 50, + "avg_tps": 25.360977804679788, + "best_tps": 26.0705565191107, + "vram_used": 12022, + "vram_total": 12288, + "label": "mmap=True | poll=50 | prio=3" + }, + { + "ngl": 21, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q8_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": false, + "prio": 3, + "poll": 0, + "avg_tps": 24.156893523381967, + "best_tps": 24.840307911026144, + "vram_used": 12021, + "vram_total": 12288, + "label": "mmap=False | poll=0 | prio=3" + } +] \ No newline at end of file diff --git a/scripts/tune_results_gemma4_ncpumoe.json b/scripts/tune_results_gemma4_ncpumoe.json new file mode 100644 index 0000000..e6bf6fb --- /dev/null +++ b/scripts/tune_results_gemma4_ncpumoe.json @@ -0,0 +1,201 @@ +[ + { + "label": "ncpumoe=0", + "ncpumoe": 0, + "avg": 15.396949591766335, + "best": 20.220093309883133, + "vram": 12011, + "nommap": false + }, + { + "label": "ncpumoe=5", + "ncpumoe": 5, + "avg": 4.853957926040404, + "best": 4.9029479257524216, + "vram": 11945, + "nommap": false + }, + { + "label": "ncpumoe=10", + "ncpumoe": 10, + "avg": 20.64137159193706, + "best": 26.474940718957154, + "vram": 12020, + "nommap": false + }, + { + "label": "ncpumoe=15", + "ncpumoe": 15, + "avg": 13.424368433101165, + "best": 13.698684361880598, + "vram": 12018, + "nommap": false + }, + { + "label": "ncpumoe=20", + "ncpumoe": 20, + "avg": 10.338449574838693, + "best": 13.495275411319872, + "vram": 11530, + "nommap": true + }, + { + "label": "ncpumoe=25", + "ncpumoe": 25, + "avg": 12.920348175328435, + "best": 12.99923042323437, + "vram": 11625, + "nommap": true + }, + { + "label": "ncpumoe=30", + "ncpumoe": 30, + "avg": 13.251690836275145, + "best": 13.253697466971921, + "vram": 9064, + "nommap": true + }, + { + "label": "ncpumoe=7", + "ncpumoe": 7, + "avg": 16.31796299658782, + "best": 23.160760806218782, + "vram": 11994, + "nommap": false + }, + { + "label": "ncpumoe=9", + "ncpumoe": 9, + "avg": 7.469651892205037, + "best": 10.875064047449284, + "vram": 11941, + "nommap": false + }, + { + "label": "ncpumoe=11", + "ncpumoe": 11, + "avg": 14.814740144776437, + "best": 15.199641279675724, + "vram": 11984, + "nommap": false + }, + { + "label": "ncpumoe=13", + "ncpumoe": 13, + "avg": 14.183175252947136, + "best": 14.427257794639086, + "vram": 12003, + "nommap": false + }, + { + "label": "t=2", + "ncpumoe": 10, + "avg": 28.551811207068425, + "best": 28.688565545389164, + "vram": 11968, + "t": 2, + "nommap": false + }, + { + "label": "t=4", + "ncpumoe": 10, + "avg": 30.8619310622166, + "best": 31.17677746690393, + "vram": 11972, + "t": 4, + "nommap": false + }, + { + "label": "t=6", + "ncpumoe": 10, + "avg": 30.578454576249854, + "best": 30.971792125516313, + "vram": 11983, + "t": 6, + "nommap": false + }, + { + "label": "t=8", + "ncpumoe": 10, + "avg": 30.529393512116172, + "best": 30.954830478128166, + "vram": 11982, + "t": 8, + "nommap": false + }, + { + "label": "t=10", + "ncpumoe": 10, + "avg": 30.773041112229503, + "best": 31.00899077264753, + "vram": 11972, + "t": 10, + "nommap": false + }, + { + "label": "ub=256,b=1024", + "ncpumoe": 10, + "avg": 30.49319055490045, + "best": 30.691055921541377, + "vram": 11993, + "t": 4, + "ub": 256, + "b": 1024, + "nommap": false + }, + { + "label": "ub=512,b=2048", + "ncpumoe": 10, + "avg": 30.923573731331718, + "best": 31.902272031660825, + "vram": 11995, + "t": 4, + "ub": 512, + "b": 2048, + "nommap": false + }, + { + "label": "ub=512,b=4096", + "ncpumoe": 10, + "avg": 30.723820162954862, + "best": 31.065476003548053, + "vram": 11966, + "t": 4, + "ub": 512, + "b": 4096, + "nommap": false + }, + { + "label": "ub=1024,b=2048", + "ncpumoe": 10, + "avg": 30.489888387093156, + "best": 30.982074615885946, + "vram": 11964, + "t": 4, + "ub": 1024, + "b": 2048, + "nommap": false + }, + { + "label": "kv=q4_0", + "ncpumoe": 10, + "avg": 30.63156129571348, + "best": 31.088674795634944, + "vram": 11988, + "t": 4, + "ctk": "q4_0", + "ctv": "q4_0", + "nommap": false + }, + { + "label": "kv=q8_0", + "ncpumoe": 10, + "avg": 29.6114222576863, + "best": 30.580427895917573, + "vram": 11980, + "t": 4, + "ctk": "q8_0", + "ctv": "q8_0", + "nommap": false + } +] \ No newline at end of file diff --git a/scripts/tune_results_qwen35b_256k.json b/scripts/tune_results_qwen35b_256k.json new file mode 100644 index 0000000..e82d0be --- /dev/null +++ b/scripts/tune_results_qwen35b_256k.json @@ -0,0 +1,522 @@ +[ + { + "ngl": 999, + "cpu_moe": true, + "t": 6, + "tb": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.169961832638464, + "best_tps": 26.533887071573073, + "vram_used": 4994, + "vram_total": 12288, + "label": "cpu_moe=True" + }, + { + "ngl": 999, + "cpu_moe": false, + "t": 6, + "tb": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 11.065030380022206, + "best_tps": 11.083028272674314, + "vram_used": 11949, + "vram_total": 12288, + "label": "cpu_moe=False" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 2, + "tb": 2, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 21.473286428302767, + "best_tps": 21.746637577851104, + "vram_used": 4994, + "vram_total": 12288, + "label": "t=2 | tb=2" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.552358479030676, + "best_tps": 27.314237654089343, + "vram_used": 4991, + "vram_total": 12288, + "label": "t=4 | tb=4" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.347068485327956, + "best_tps": 26.87924726131441, + "vram_used": 4993, + "vram_total": 12288, + "label": "t=4 | tb=6" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 6, + "tb": 6, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.331286039513458, + "best_tps": 26.81427299445741, + "vram_used": 5001, + "vram_total": 12288, + "label": "t=6 | tb=6" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 6, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.391160513711274, + "best_tps": 26.735573238878736, + "vram_used": 5001, + "vram_total": 12288, + "label": "t=6 | tb=8" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 8, + "tb": 8, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 25.32340666199144, + "best_tps": 25.87949347494079, + "vram_used": 4995, + "vram_total": 12288, + "label": "t=8 | tb=8" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 10, + "tb": 10, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 23.752277317850815, + "best_tps": 24.98242898809555, + "vram_used": 5011, + "vram_total": 12288, + "label": "t=10 | tb=10" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 12, + "tb": 12, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 21.75032196383532, + "best_tps": 23.18963400077116, + "vram_used": 5104, + "vram_total": 12288, + "label": "t=12 | tb=12" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 128, + "b": 512, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 13.27593572827031, + "best_tps": 13.337407402920235, + "vram_used": 4391, + "vram_total": 12288, + "label": "ub=128 | b=512" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 256, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.638687188233188, + "best_tps": 27.361082444434413, + "vram_used": 4495, + "vram_total": 12288, + "label": "ub=256 | b=1024" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 256, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.29069503392877, + "best_tps": 26.63368832924803, + "vram_used": 4490, + "vram_total": 12288, + "label": "ub=256 | b=2048" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 1024, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.518331831441134, + "best_tps": 26.972021321271527, + "vram_used": 4984, + "vram_total": 12288, + "label": "ub=512 | b=1024" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.401541912276873, + "best_tps": 26.46530849236633, + "vram_used": 4990, + "vram_total": 12288, + "label": "ub=512 | b=2048" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 26.892711500590455, + "best_tps": 26.892711500590455, + "vram_used": 5006, + "vram_total": 12288, + "label": "ub=512 | b=4096" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 2048, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 12.600209659679201, + "best_tps": 12.759356030807627, + "vram_used": 12020, + "vram_total": 12288, + "label": "ub=1024 | b=2048" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 1024, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 6.023959262370547, + "best_tps": 8.284882268188156, + "vram_used": 11931, + "vram_total": 12288, + "label": "ub=1024 | b=4096" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 12.96992950856374, + "best_tps": 12.96992950856374, + "vram_used": 12022, + "vram_total": 12288, + "label": "ctk=q4_0 | ctv=q4_0" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q8_0", + "ctv": "q8_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 11.420078920350697, + "best_tps": 13.524778595767653, + "vram_used": 12030, + "vram_total": 12288, + "label": "ctk=q8_0 | ctv=q8_0" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "f16", + "ctv": "f16", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 11.978106511464183, + "best_tps": 13.729190013094977, + "vram_used": 11518, + "vram_total": 12288, + "label": "ctk=f16 | ctv=f16" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 50, + "avg_tps": 16.164278220452957, + "best_tps": 22.645890325274323, + "vram_used": 11623, + "vram_total": 12288, + "label": "mmap=True | poll=50 | prio=2" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": false, + "prio": 2, + "poll": 50, + "avg_tps": 16.555542780023114, + "best_tps": 23.333815015033892, + "vram_used": 9062, + "vram_total": 12288, + "label": "mmap=False | poll=50 | prio=2" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 0, + "avg_tps": 13.003619379106329, + "best_tps": 13.031594557134142, + "vram_used": 11994, + "vram_total": 12288, + "label": "mmap=True | poll=0 | prio=2" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 2, + "poll": 100, + "avg_tps": 5.7762452690702935, + "best_tps": 5.795560155803046, + "vram_used": 11953, + "vram_total": 12288, + "label": "mmap=True | poll=100 | prio=2" + }, + { + "ngl": 999, + "cpu_moe": true, + "t": 4, + "tb": 4, + "ub": 512, + "b": 4096, + "ctk": "q4_0", + "ctv": "q4_0", + "fa": "on", + "mlock": true, + "mmap": true, + "prio": 3, + "poll": 50, + "avg_tps": 12.59406799687573, + "best_tps": 14.966737641114795, + "vram_used": 11996, + "vram_total": 12288, + "label": "mmap=True | poll=50 | prio=3" + } +] \ No newline at end of file