Instructions to use maxwelhelp/llama.cpp-DFlash2-pascal6-optimized with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use maxwelhelp/llama.cpp-DFlash2-pascal6-optimized with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf maxwelhelp/llama.cpp-DFlash2-pascal6-optimized # Run inference directly in the terminal: llama cli -hf maxwelhelp/llama.cpp-DFlash2-pascal6-optimized
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf maxwelhelp/llama.cpp-DFlash2-pascal6-optimized # Run inference directly in the terminal: llama cli -hf maxwelhelp/llama.cpp-DFlash2-pascal6-optimized
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf maxwelhelp/llama.cpp-DFlash2-pascal6-optimized # Run inference directly in the terminal: ./llama-cli -hf maxwelhelp/llama.cpp-DFlash2-pascal6-optimized
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf maxwelhelp/llama.cpp-DFlash2-pascal6-optimized # Run inference directly in the terminal: ./build/bin/llama-cli -hf maxwelhelp/llama.cpp-DFlash2-pascal6-optimized
Use Docker
docker model run hf.co/maxwelhelp/llama.cpp-DFlash2-pascal6-optimized
- LM Studio
- Jan
- Ollama
How to use maxwelhelp/llama.cpp-DFlash2-pascal6-optimized with Ollama:
ollama run hf.co/maxwelhelp/llama.cpp-DFlash2-pascal6-optimized
- Unsloth Desktop
- Docker Model Runner
How to use maxwelhelp/llama.cpp-DFlash2-pascal6-optimized with Docker Model Runner:
docker model run hf.co/maxwelhelp/llama.cpp-DFlash2-pascal6-optimized
- Lemonade
How to use maxwelhelp/llama.cpp-DFlash2-pascal6-optimized with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull maxwelhelp/llama.cpp-DFlash2-pascal6-optimized
Run and chat with the model
lemonade run user.llama.cpp-DFlash2-pascal6-optimized-{{QUANT_TAG}}List all available models
lemonade list
- Atomic Chat
maxwelhelp commited on
Commit ·
7c97475
1
Parent(s): 72ab2b3
DFlash2 q4-mix draft for llama.cpp on Tesla P40 (Pascal)
Browse files- .gitattributes +3 -0
- DFLASH2_RING_PORT.md +1183 -0
- Qwen3.8-27B-DFlash2-q4mix-self.gguf +3 -0
- README.md +155 -0
- run_best.sh +37 -0
- scripts/convert_dflash_to_gguf.py +3 -0
- scripts/quantize_dflash_draft.py +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
|
| 37 |
+
*.gguf filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.py filter=lfs diff=lfs merge=lfs -text
|
DFLASH2_RING_PORT.md
ADDED
|
@@ -0,0 +1,1183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DFlash2 draft KV-ring port to llama.cpp (рабочий проект llama-dflash3-pr)
|
| 2 |
+
|
| 3 |
+
> Цель: сжать draft-KV с ~1 ГБ до ~48 МБ БЕЗ потери acceptance (держать 79-80%),
|
| 4 |
+
> не повторив ошибку предыдущего агента (наивен seq_rm/урезание n_ctx -> 30% acceptance).
|
| 5 |
+
>
|
| 6 |
+
> Эталон для переноса: Luce `server/src/common/dflash_draft_kv.{h,cpp}` +
|
| 7 |
+
> `server/src/common/dflash_feature_ring.h` + драйвер `qwen35_backend.cpp`.
|
| 8 |
+
> База в llama.cpp: рабочий проект `llama-dflash3-pr` (acceptance 79.889%).
|
| 9 |
+
> Сломанная dflash2 удалена пользователем; работаем только в dflash3-pr.
|
| 10 |
+
|
| 11 |
+
## TL;DR (для чтения перед правками)
|
| 12 |
+
|
| 13 |
+
Проблема не в "окне 4096" как таковом, а в том, КАК его реализовали.
|
| 14 |
+
Luce использует честный **mod-cap ring** (`slot = pos % cap`) + **явные attention-маски**
|
| 15 |
+
(вне-окна = -inf, в-окна = 0) + **абсолютные RoPE-позиции** + **пересчёт K/V только
|
| 16 |
+
для новых строк**. Это математически эквивалентно "полному кешу", поэтому acceptance
|
| 17 |
+
не падает. Предыдущий агент вместо этого урезал `n_ctx` и резал кеш через
|
| 18 |
+
`llama_memory_seq_rm`, что создаёт дырки/неконсистентные окна и ломает selector-lattice.
|
| 19 |
+
|
| 20 |
+
Колесо НЕ даёт >80% (80% это потолок самой пары модели). Оно даёт тот же 80% при
|
| 21 |
+
меньшей памяти. Если не нужно экономить ~1 ГБ - НЕ переносим ничего, просто
|
| 22 |
+
собираем рабочую dflash3 и живём с полным контекстом.
|
| 23 |
+
|
| 24 |
+
---
|
| 25 |
+
|
| 26 |
+
## 0. Карта изменений (файлы, которые отличают рабочую dflash3 от сломанной dflash2)
|
| 27 |
+
|
| 28 |
+
Единственное отличие между worktree dflash3 (рабочий) и dflash2 (сломанный) - это
|
| 29 |
+
последняя правка агента "draft-only KV window". Оно затрагивает ровно 3 файла:
|
| 30 |
+
|
| 31 |
+
| файл | что добавил сломанный агент | вердикт |
|
| 32 |
+
|------|------------------------------|---------|
|
| 33 |
+
| `common/arg.cpp` | флаг `--spec-draft-ctx N` (добавляет `n_ctx` в `params.speculative.draft`) | НЕ решил проблему; меняет конфиг без правильного ring |
|
| 34 |
+
| `common/common.h` | поле `int32_t n_ctx = 4096` в `common_params_speculative_draft` | см. выше |
|
| 35 |
+
| `common/speculative.cpp` | `draft_ctx_window=4096`, `llama_memory_seq_rm`-ролл в `process()`, урезание `cparams.n_ctx` в common_speculative_init_result | **корень бага** |
|
| 36 |
+
|
| 37 |
+
Всё остальное (dflash.cpp, qwen35.cpp, llama-model-loader, server-context и т.д.)
|
| 38 |
+
между worktree ИДЕНТИЧНО - это рабочая часть порта, её не трогаем.
|
| 39 |
+
|
| 40 |
+
### Вывод по эталону
|
| 41 |
+
- Рабочая версия = **БЕЗ** `seq_rm`-ролла, БЕЗ урезания `n_ctx`, с полным контекстом.
|
| 42 |
+
- Сломанная версия = добавила только вышеуказанное и сломала acceptance.
|
| 43 |
+
- Правильный путь = перенести НАСТОЯЩИЙ ring (mod-cap + маски), а не наивный роллинг.
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
## 1. Что делает ring в Luce (механизм, код, математика)
|
| 48 |
+
|
| 49 |
+
Файлы-эталоны:
|
| 50 |
+
- `lucebox/server/src/common/dflash_draft_kv.h` (DraftKvState)
|
| 51 |
+
- `lucebox/server/src/common/dflash_draft_kv.cpp` (init / begin_step / masks)
|
| 52 |
+
- `lucebox/server/src/common/dflash_feature_ring.h` (DraftFeatureMirror - зеркало фич target)
|
| 53 |
+
- `lucebox/server/src/qwen35/qwen35_backend.cpp` (драйвер decode: строки ~2690-2785)
|
| 54 |
+
|
| 55 |
+
### 1.1 Геометрия (draft_kv_init, dflash_draft_kv.cpp:16-130)
|
| 56 |
+
|
| 57 |
+
```
|
| 58 |
+
cap = ring-вместимость контекстных слотов (последние `cap` позиций)
|
| 59 |
+
q_len = block_size (у нас из GGUF = 8)
|
| 60 |
+
a_step = 2 * q_len + 2 (сколько новых строк можно доложить за шаг)
|
| 61 |
+
trash_slot= cap + q_len (слот-приёмник для pad-строк)
|
| 62 |
+
kv_total = align_up(cap + q_len + 1, 32) (всего строк кеша: ring + шумовой блок + pad)
|
| 63 |
+
kv_row = head_dim * n_head_kv (128 * 8 = 1024 f16 на строку)
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
K/V - это отдельные F16-тензоры `[kv_row, kv_total]`, НЕ llama.cpp KV-кеш. Живут в
|
| 67 |
+
`mem_buf` (persistent backend buffer, вне gallocr), обнулены один раз (пустые слоты
|
| 68 |
+
читаются FA, masked -inf, обязаны быть конечными).
|
| 69 |
+
|
| 70 |
+
### 1.2 mod-cap отображение (критично!)
|
| 71 |
+
|
| 72 |
+
В `draft_kv_begin_step` (dflash_draft_kv.cpp:226-318):
|
| 73 |
+
```cpp
|
| 74 |
+
ap_rows[i] = p % st.cap; // СЛОТ = абсолютная позиция % cap
|
| 75 |
+
st.slot_pos[p % st.cap] = p; // host-зеркало: слот -> его абсолютная позиция
|
| 76 |
+
```
|
| 77 |
+
- Абсолютные позиции сохраняются (RoPE по абсолюту).
|
| 78 |
+
- Свободные слоты перезаписываются по кольцу, НЕ удаляются сдвигом.
|
| 79 |
+
- `slot_pos[]` хранит абсолютную позицию каждого слота (для построения масок).
|
| 80 |
+
|
| 81 |
+
### 1.3 Окно и начало шага (begin_step)
|
| 82 |
+
|
| 83 |
+
```
|
| 84 |
+
win = min(committed, cap) // окно = последние `cap` закоммиченных
|
| 85 |
+
lo = committed - win // нижняя граница окна
|
| 86 |
+
start = max(next_pos, lo) // доложить всё, чего ещё нет в кеше
|
| 87 |
+
n_new = committed - start // сколько новых строк добавляем
|
| 88 |
+
```
|
| 89 |
+
- Если `n_new > a_step` -> `draft_kv_bulk_append` (разово заливает по 1024 строки).
|
| 90 |
+
- Иначе - fold-in append: реальные строки + pad-строки в `trash_slot`.
|
| 91 |
+
|
| 92 |
+
Шумовой блок:
|
| 93 |
+
```cpp
|
| 94 |
+
pq[i] = committed + i; // абсолютные позиции noise-блока (q_len штук)
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
### 1.4 Attention-маски (критично! отличается от наивного ролла)
|
| 98 |
+
|
| 99 |
+
`mask_full` (полное-внимание слои), dflash_draft_kv.cpp:280-294:
|
| 100 |
+
```cpp
|
| 101 |
+
для каждого слота s:
|
| 102 |
+
p = slot_pos[s]
|
| 103 |
+
mask[s] = (p >= lo && p < committed) ? 0 : -inf // только окно видно
|
| 104 |
+
для шумовых слотов (cap+j): 0 // noise виден (bidirectional)
|
| 105 |
+
```
|
| 106 |
+
- строки за пределами окна = -inf (полностью исключены).
|
| 107 |
+
- шумовой блок виден всем шумовым строкам (не-causal внутри блока).
|
| 108 |
+
|
| 109 |
+
`mask_swa` (SWA-слои), :295-317:
|
| 110 |
+
```cpp
|
| 111 |
+
eff_win = min(swa_window, win); swa_lo = committed - eff_win;
|
| 112 |
+
mask[s] = (p >= swa_lo && p < committed) ? 0 : -inf
|
| 113 |
+
// общее якорение окна на `committed` для ВСЕХ шумовых строк (важно для trained drafter)
|
| 114 |
+
// шумовые строки: causal внутри блока (j в <= q)
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
> ВАЖНО для нашего порта: у пользовательской draft GGUF **нет** sliding-window меты
|
| 118 |
+
> (нет `attention.sliding_window`), поэтому `use_iswa=false`, слоёв SWA нет,
|
| 119 |
+
> `causal_attn` выключен, и работает только `mask_full`-подобное поведение
|
| 120 |
+
> (бидирекц. шумовой блок + полное окно). SWA-ветка для этой модели неактивна.
|
| 121 |
+
|
| 122 |
+
### 1.5 Один фикс-topology граф (CUDA graph)
|
| 123 |
+
|
| 124 |
+
- Степовый граф строится ОДИН раз в init и реплеится бесконечно.
|
| 125 |
+
- Все входные тензоры - в dedicated backend буферах (не gallocr-recycled).
|
| 126 |
+
- `ggml_gallocr_alloc_graph` один раз; дальше только `backend_tensor_set` + `graph_compute`.
|
| 127 |
+
|
| 128 |
+
### 1.6 Драйвер (qwen35_backend.cpp:2689-2785)
|
| 129 |
+
|
| 130 |
+
```cpp
|
| 131 |
+
ring_cap = feature_mirror_.cap;
|
| 132 |
+
draft_ctx = min(committed, min(ring_cap, max(DRAFT_CTX_MAX_DEFAULT=2048, cfg_.draft_ctx_max)));
|
| 133 |
+
draft_start = committed - draft_ctx;
|
| 134 |
+
|
| 135 |
+
// ring path (DFLASH_DRAFT_KV, по умолчанию вкл):
|
| 136 |
+
draft_kv_init(draft_kv_, dw_, backend, kv_cap = draft_ctx, lm_head=nullptr); // 1 раз
|
| 137 |
+
draft_kv_begin_step(draft_kv_, dw_, backend, feature_mirror_, committed);
|
| 138 |
+
backend_tensor_set(draft_kv_.inp_embed, noise_embed); // шумовой блок [q_len] f32
|
| 139 |
+
backend_graph_compute(draft_kv_.gf); // все слои draft
|
| 140 |
+
get(draft_kv_.hidden_states) -> local_hidden // [hidden*q_len]
|
| 141 |
+
```
|
| 142 |
+
- `used_remote_draft` / `use_mirror_view` - опции отключения ring (fallback legacy,
|
| 143 |
+
который пересчитывает ВСЁ окно каждый шаг). Ring = частный, но более быстрый случай.
|
| 144 |
+
|
| 145 |
+
### 1.7 Что даёт ring математически
|
| 146 |
+
- Результат равен legacy-пересчёту полного окна (абсолютные RoPE, маски).
|
| 147 |
+
- Память: `2 * n_layer * kv_row * kv_total * 2 / 1e6 МБ` на F16. Для cap≈4096 ->
|
| 148 |
+
доли МБ, против ~1 ГБ при полном `-c 101072`.
|
| 149 |
+
- Acceptance не меняется (математически то же окно).
|
| 150 |
+
|
| 151 |
+
---
|
| 152 |
+
|
| 153 |
+
## 2. Как это устроено в llama.cpp (dflash3 - рабочая) и почему наивный ролл ломает
|
| 154 |
+
|
| 155 |
+
Файл: `common/speculative.cpp`, `struct common_speculative_impl_draft_dflash` (строки ~912-1358).
|
| 156 |
+
|
| 157 |
+
### 2.1 По��ок в рабочей версии
|
| 158 |
+
- `ctor`: читает block_size=8, mask_token_id=248070, selector_top_k=16 из GGUF;
|
| 159 |
+
включ `llama_set_embeddings_layer_inp(ctx_tgt, target_layer_ids[k]+1)` (выходы слоёв);
|
| 160 |
+
`llama_set_embeddings_nextn(ctx_dft, true, !is_dflash2)` (латтис селектора);
|
| 161 |
+
`llama_set_causal_attn(ctx_dft, false)` (шумовой блок бидирекц.).
|
| 162 |
+
- `process()` (prefill + каждый target-токен): берёт фичи target-слоёв, `llama_encode`
|
| 163 |
+
-> `inp_g`, `llama_decode(ctx_dft, batch_inject)` ЗАПИСЫВАЕТ инжект в draft KV на
|
| 164 |
+
абсолютных позициях. (В сломанной версии тут ещё и roll seq_rm - вот баг.)
|
| 165 |
+
- `draft()`: один `llama_decode(ctx_dft, batch)` всего шумового блока `[last, mask...x7]`
|
| 166 |
+
на позициях `n..n+7`, читает латтис из `llama_get_embeddings_nextn`, строит цепочку.
|
| 167 |
+
|
| 168 |
+
### 2.2 Почему acceptance упал до 30%
|
| 169 |
+
- draft KV - это llama.cpp `llama_kv_cache` на полный `-c` (в рабочей) или урезанный
|
| 170 |
+
`(4096+headroom)` (в сломанной).
|
| 171 |
+
- Сломанная версия перед каждой инжекцией делала
|
| 172 |
+
`llama_memory_seq_rm(mem_dft, seq, 0, pos-window)`: физически вырезала строки KV.
|
| 173 |
+
Это НЕ mod-cap ring. Это:
|
| 174 |
+
- удаляет позиции < границы (создаёт дырку в начале),
|
| 175 |
+
- НО llama.cpp attention (plain `build_attn_inp_kv`) собирает ВСЕ присутствующие
|
| 176 |
+
в кеше строки и attends по ним БЕЗ явной маски окна -inf. Наивный ролл полагался
|
| 177 |
+
на наличие пустых/evict-ячеек, которые attention читает как мусор,
|
| 178 |
+
- плюс `n_ctx=(4096+headroom)` слишком мал: noise-блок + инжект не вмещаются
|
| 179 |
+
по round-robin -> вытеснение (eviction) режет нужные строки.
|
| 180 |
+
- Итог: selector-lattice сходил с мусора -> цепочка мусорная -> target режет до 30%.
|
| 181 |
+
|
| 182 |
+
### 2.3 Ключевое отличие от Luce, которое надо воспроизвести
|
| 183 |
+
Luce:
|
| 184 |
+
- кеш фикс. размера `kv_total`, слоты = `pos % cap`, вне-окна строки ЯВНО
|
| 185 |
+
маскируются -inf, in-окна плотно присутствуют.
|
| 186 |
+
llama.cpp (plain attn, без ISWA):
|
| 187 |
+
- кеш `n_ctx`, attention собирает все строки кеша, явной per-row маски окружения нет
|
| 188 |
+
(masks строятся из `kq_mask` по `n_ctx`, а не по per-row виду).
|
| 189 |
+
|
| 190 |
+
Из этого следует: **голый перенос идеи "урежь n_ctx + режь seq_rm" невозможен без
|
| 191 |
+
нарушения математики окна**. Нужен ОДИН из двух правильных путей (см. раздел 3).
|
| 192 |
+
|
| 193 |
+
---
|
| 194 |
+
|
| 195 |
+
## 3. СТРУКТУРА DRAFT (ПЕРЕСМОТР - ВАЖНО): это обычный dense-attention трансформер, НЕ рекуррентный
|
| 196 |
+
|
| 197 |
+
> ПРЕДЫДУЩАЯ версия этого раздела (гибрид с Gated DeltaNet) была ОШИБКОЙ и снята.
|
| 198 |
+
> Проверено по llama-dflash3-pr.
|
| 199 |
+
|
| 200 |
+
### 3.1 Факты
|
| 201 |
+
- Draft строится в `src/models/dflash.cpp` (arch `LLM_ARCH_DFLASH`). Его внимание -
|
| 202 |
+
**обычные dense-проекции** `wq / wk / wv / wo` (`dflash.cpp:498-499, 588-590`).
|
| 203 |
+
В dflash.cpp НЕТ `build_recurrent_attn` / `ssm_*` (рекуррентный код из qwen35.cpp
|
| 204 |
+
относится к ОСНОВНОЙ целевой модели, не к draft).
|
| 205 |
+
- Пер-layer есть **dynamic conv** (`dflash_attn_conv_proj/base`, `dflash_ffn_conv_*`
|
| 206 |
+
`dflash.cpp:582-585, 235-236`) и **DFlash2 selector** (предшественник/следующий/скрытый
|
| 207 |
+
-> латтис выбора цепочки; читается из `llama_get_embeddings_nextn`, `speculative.cpp:1245`).
|
| 208 |
+
- `LLM_ARCH_DFLASH` НЕ входит в recurrent/hybrid списки (`llama-arch.cpp:998-1034`), значит
|
| 209 |
+
`create_memory` даёт draft'у **обычный `llama_kv_cache`** (`llama-model.cpp:2265-2351`),
|
| 210 |
+
а НЕ `llama_memory_recurrent`. Никакого рекуррентного скана истории нет.
|
| 211 |
+
- У нашей GGUF нет sliding-window меты -> `use_iswa=false` -> plain `build_attn_inp_kv()`,
|
| 212 |
+
`causal_attn=false` (шумовой блок бидирекц.), без SWA-масок.
|
| 213 |
+
|
| 214 |
+
### 3.2 Вывод для переноса
|
| 215 |
+
Это ТОЧНО тот случай, для которого Luce ring и написан: **dense-attention draft на
|
| 216 |
+
position-KV**. ~1 ГБ draft-KV при `-c 101072` - это и есть attention-KV, его и сжимаем
|
| 217 |
+
кольцом. Рекуррентной части в draft нет -> нет аргумента "нельзя окно, нужен скан истории".
|
| 218 |
+
Перенос кольца из Luce корректен и реализуем целиком.
|
| 219 |
+
|
| 220 |
+
---
|
| 221 |
+
|
| 222 |
+
## 4. КАК ПЕРЕНОСИТЬ (одобрено: переносим настоящий ring, а не наивный ролл)
|
| 223 |
+
|
| 224 |
+
Так как draft = dense-attention на plain `llama_kv_cache`, у нас есть ДВА правильных пути
|
| 225 |
+
(оба корректы математически; предпочтителен C, т.к. это буквально Luce).
|
| 226 |
+
|
| 227 |
+
### Вариант C (ОСНОВНОЙ): настоящий Luce KV-ring для draft (dense-attention)
|
| 228 |
+
Заменить штатный бескрайний `ctx_dft` KV-кеш на кольцо с фикс. `cap` слотов, слот =
|
| 229 |
+
`pos % cap`, и построить маски -inf для вне-окна. По шагам см. раздел 5 - там точный план
|
| 230 |
+
по файлам/функциям.
|
| 231 |
+
|
| 232 |
+
Свойства (равно Luce):
|
| 233 |
+
- окно = последние `cap` закоммиченных позиций,
|
| 234 |
+
- вне-окна строки физически НЕ присутствуют (или маскируются -inf) - attention их не видит,
|
| 235 |
+
- шумовой блок (q_len=8) attend'ит окно бидирекц.,
|
| 236 |
+
- absolute RoPE -> позиции не сдвигаются -> кеш не "протухает" при перемотке/новых запросах.
|
| 237 |
+
|
| 238 |
+
### Вариант B (промежуточный, меньше кода): удержание окна через сам llama.cpp
|
| 239 |
+
Не резать `seq_rm` вручную; просто задать draft'у умеренный `n_ctx` (= окно) и дать
|
| 240 |
+
`llama_kv_cache` самому держать непрерывное окно (инвариант `[pos_min,pos_max]` из
|
| 241 |
+
`apply_ubatch`, llama-kv-cache.cpp:1143-1160). НЕ вызывать `llama_memory_seq_rm` в `process()`.
|
| 242 |
+
- Pиск, из-за которого это может не дать 79.9% на длинных промптах: llama.cpp evict
|
| 243 |
+
выталкивает "старые" ячейки, и attention читает РЕЗИДЕНТНОЕ окно без -inf (window =
|
| 244 |
+
"сколько ячеек влезло"), не строго `[lo, committed)`. Для чисто растyщего промпта это
|
| 245 |
+
близко к кольцу, но менее точно при перемотках.
|
| 246 |
+
- Использовать только как экспериментальную промежуточную точку/флаг для сравнения.
|
| 247 |
+
|
| 248 |
+
### Решение
|
| 249 |
+
Реализуем Вариант C (настоящий ring) как основной, с Вариантом B как флагом отката для
|
| 250 |
+
измерения. Начинаем с того, что возвращаем в dflash3 рабочий baseline (acceptance 79.9),
|
| 251 |
+
затем накатываем ring так, чтобы acceptance НЕ менялся (это проверяется на одном промпте).
|
| 252 |
+
|
| 253 |
+
---
|
| 254 |
+
|
| 255 |
+
## 5. ПЛАН ПЕРЕНОСА (настоящий ring, dense-attention draft) - по шагам, файлам, функциям
|
| 256 |
+
|
| 257 |
+
Baseline для замеров: текущий рабочий dflash3 (acceptance 79.9%). Каждый шаг собираем и
|
| 258 |
+
сверяем acceptance на одном промпте (temp=0, n_max=7), иначе не двигаемся дальше.
|
| 259 |
+
|
| 260 |
+
### Шаг 1. Сохранить рабочий baseline (не обязательно коммитить)
|
| 261 |
+
`git stash` или снимок `common/speculative.cpp` до начала правок - чтобы можно было
|
| 262 |
+
одним `cp` вернуться к 79.9% и сравнивать.
|
| 263 |
+
|
| 264 |
+
### Шаг 2. Параметр окна (конфиг)
|
| 265 |
+
- `common/common.h`, `struct common_params_speculative_draft`: добавить
|
| 266 |
+
`int32_t n_ctx = 0; // 0 = полный контекст (без кольца); >0 = ring window`
|
| 267 |
+
- `common/arg.cpp`: опция `--spec-draft-ctx N` (пишет в `draft.n_ctx`).
|
| 268 |
+
> Здесь главное отличие от сломанной версии агента: `n_ctx` задаёт ОКНО кольца, а не
|
| 269 |
+
> просто кромсает `cparams.n_ctx`. Код ниже явно строит кольцо и нигде не режет `seq_rm`.
|
| 270 |
+
|
| 271 |
+
### Шаг 3. Аллоцировать draft-KV как кольцо (не полный `-c`)
|
| 272 |
+
В `common_speculative_init_result::common_speculative_init_result` (speculative.cpp:2452+):
|
| 273 |
+
```cpp
|
| 274 |
+
if (spec_dflash && params.speculative.draft.n_ctx > 0) {
|
| 275 |
+
// окно: min(n_ctx, ...) floor 2048, как Luce DRAFT_CTX_MAX_DEFAULT
|
| 276 |
+
const uint32_t window = std::max<uint32_t>(2048, params.speculative.draft.n_ctx);
|
| 277 |
+
// draft контекст - только окно + 1 микробатч шума:
|
| 278 |
+
cparams.n_ctx = (window + cparams.n_batch) * cparams.n_seq_max; // НЕ больше
|
| 279 |
+
}
|
| 280 |
+
```
|
| 281 |
+
ВАЖНО: это НЕ трогает target (`ctx_tgt` остаётся на полном `-c 101072`).
|
| 282 |
+
|
| 283 |
+
### Шаг 4. Убрать seq_rm-ролл, дать Llama.cpp держать непрерывное окно (минимум для начала)
|
| 284 |
+
В `process()` (speculative.cpp ~1101+): удалить блок
|
| 285 |
+
`std::vector<llama_pos> seq_end...` + `llama_memory_seq_rm`, вычислять/инжектить фичи как
|
| 286 |
+
в рабочей dflash3. За счёт `n_ctx = окно` из Шага 3 кеш сам содержит только последние
|
| 287 |
+
`window` позиций (непрерывно), attention читает их все - это эквивалент окна Luce.
|
| 288 |
+
- Это даёт Вариант B сразу и является ПЕРВЫМ измеримым результатом (см. раздел 4).
|
| 289 |
+
- Замер: acceptance должен остаться ~79.9%. Если падает - окно мало либо нужен Шаг 5.
|
| 290 |
+
|
| 291 |
+
### Шаг 5. Точные маски окна (до == Luce `mask_full`, если Шаг 4 падает по acceptance на длинных)
|
| 292 |
+
Если на длинном промпте (когда окно начинает резать) acceptance падает, значит нужно
|
| 293 |
+
явное -inf-маскирование вне-окна как в Luce `draft_kv_begin_step` (:280-294). Это требует
|
| 294 |
+
вмешательства в `build_attn_inp_kv` / `set_input_kq_mask` (llama-graph.cpp, llama-kv-cache.cpp)
|
| 295 |
+
либо отдельного модуля ring. В этом случае:
|
| 296 |
+
- ввести кастомный KV-модуль для draft (по образу Luce `DraftKvState`, см. шаг 6),
|
| 297 |
+
- либо параметр `n_ctx` с переносом позиций и kq-маской по окну.
|
| 298 |
+
|
| 299 |
+
### Шаг 6. (при необходимости) Кастомный ring-модуль = полный перенос Luce
|
| 300 |
+
Это буквально перенос `dflash_draft_kv.{h,cpp}` + `draft_graph.cpp` логики в новый
|
| 301 |
+
`common/draft-ring.{h,cpp}` под llama.cpp backend (CUDA/CPU), с драйвером в
|
| 302 |
+
`common_speculative_impl_draft_dflash::draft()` вместо `llama_decode(ctx_dft, batch)`.
|
| 303 |
+
Потребуется:
|
| 304 |
+
- ручная сборка noise-эмбеддингов + `inp_embed` [q_len],
|
| 305 |
+
- mod-cap append (слот = pos % cap, trash_slot для pad),
|
| 306 |
+
- маски (full и, если появятся SWA-слои, swa — у нас их нет),
|
| 307 |
+
- считывание hidden_states (дальше селектор строит цепочку как сейчас),
|
| 308 |
+
- fixed-topology CUDA graph (как Luce `st.gf`, переиспользуемый).
|
| 309 |
+
Это самый трудоёмкий, но полный и честный путь. ОСНОВНОЙ, если Шаг 4 не даст 79.9%.
|
| 310 |
+
|
| 311 |
+
---
|
| 312 |
+
|
| 313 |
+
## 6. Мапа корреляции Luce -> llama.cpp (одна строка = один "шаг" переноса)
|
| 314 |
+
|
| 315 |
+
| Luce (эталон) | llama.cpp / dflash3 (куда) | что переносим | сложность |
|
| 316 |
+
|---|---|---|---|
|
| 317 |
+
| `draft_kv_init` (аллокация ring-KV) | шаг 3: ограничение `cparams.n_ctx` для ctx_dft / кастомный ring | кольцо с cap слотов | C |
|
| 318 |
+
| `draft_kv_begin_step` (append+маски+noise) | шаг 4/5/6: `process()`+`draft()` в speculative.cpp | fold-in append, маски, шумовой блок | C |
|
| 319 |
+
| `draft_feature_mirror` (зеркало фич) | `features_buf` + `llama_encode`+`llama_decode(batch_inject)` (уже есть в рабочей) | целевые фичи в ring | B |
|
| 320 |
+
| `draft_graph.cpp` (dense draft граф) | dflash.cpp `graph` (уже есть) | НЕ нужно переносить заново | - |
|
| 321 |
+
| `dflash2_select_chain` | `llama_get_embeddings_nextn` + selector в `draft()` (уже есть) | выбор цепочки | B |
|
| 322 |
+
| SWA-ветка масок | НЕ применяется (нет swa в GGUF) | skip | - |
|
| 323 |
+
|
| 324 |
+
## 7. Измеряемые критерии успеха (до/после на ОДНОМ промпте, temp=0, n_max=7)
|
| 325 |
+
- acceptance % (должно держаться ~79-80%, не падать к 30%).
|
| 326 |
+
- mean chain length (~6.5).
|
| 327 |
+
- draft KV MiB (цель: ~48 МБ при cap≈4K, было ~1049 MiB).
|
| 328 |
+
- tok/s (цель: не хуже ~29-35 tok/s).
|
| 329 |
+
- параметры: те же, что в README_DFLASH2.md.
|
| 330 |
+
|
| 331 |
+
## 8. Риски (важно)
|
| 332 |
+
- Draft - dense-attention на plain KV -> кольцо экономит именно attention-KV, корректно.
|
| 333 |
+
Рекуррентной части нет, поэтому нет "скана истории" - окно полноценно для draft.
|
| 334 |
+
- Неверное окно (слишком малое) режет контекст, из которого селектор строит цепочку ->
|
| 335 |
+
снова падает acceptance (та же ловушка, что у агента). Поэтому НЕ ставить жёстко 4096,
|
| 336 |
+
а брать окно из `draft_ctx_max`/конфига min 2048, и проверять на длинном промпте.
|
| 337 |
+
- Любая правка >= большой: сначала прогнать на одном промпте и сверить acceptance.
|
| 338 |
+
- Правки нельзя делать "вслепую": каждая правка = замер, иначе риск не увидеть деградацию.
|
| 339 |
+
- Кастомный ring (шаг 6) затрагивает граф/память draft - делать только с rollback-флагом.
|
| 340 |
+
|
| 341 |
+
## 9. Итог
|
| 342 |
+
Рабочая версия = dflash3 (acceptance 79.9%). Верифицировано: draft = dense-attention на
|
| 343 |
+
plain `llama_kv_cache`, значит правильный Luce KV-ring применим. Реализация идёт по плану
|
| 344 |
+
раздела 5: сначала параметр окна + снятие seq_rm-ролла (шаги 2-4, дают сразу измеримый
|
| 345 |
+
результат и экономию памяти), при необходимости далее точные маски / кастомный ring
|
| 346 |
+
(шаги 5-6). Acceptance сверяем на каждом шаге. Колёсо даёт тот же ~80% при меньшей памяти,
|
| 347 |
+
НЕ больше 80% (это потолок пары модели).
|
| 348 |
+
|
| 349 |
+
## 10. Ход работы (СТАТУС - что уже применено в llama-dflash3-pr)
|
| 350 |
+
|
| 351 |
+
Все правки - в рабочем проекте `llama-dflash3-pr`, свежая сборка `build-p40-ring`
|
| 352 |
+
(конфигурация P40 как в README_DFLASH2.md). `llama-common` скомпилирован успешно.
|
| 353 |
+
|
| 354 |
+
### Применено (шаги 2-4 плана раздела 5)
|
| 355 |
+
1. `common/common.h` - в `common_params_speculative_draft` добавлено поле
|
| 356 |
+
`int32_t n_ctx = 0; // DFlash draft KV ring window (0 = full context, no ring)`.
|
| 357 |
+
2. `common/arg.cpp` - добавлена опция `--spec-draft-ctx N` (env `LLAMA_ARG_SPEC_DRAFT_CTX`),
|
| 358 |
+
пишет в `params.speculative.draft.n_ctx`.
|
| 359 |
+
3. `common/speculative.cpp` - в `common_speculative_init_result` (для DFlash):
|
| 360 |
+
если `draft.n_ctx > 0`, ограничиваем `cparams.n_ctx` для **ctx_dft** до
|
| 361 |
+
`(window + headroom) * n_seq_max`, где `window = max(2048, n_ctx)`,
|
| 362 |
+
`headroom = max(n_batch, n_max+1)`. Target (`ctx_tgt`) не трогаем.
|
| 363 |
+
**seq_rm-ролла НЕТ** - llama.cpp сам держит непрерывное окно последних `n_ctx` позиций
|
| 364 |
+
(эквивалент окна кольца для dense-attention draft).
|
| 365 |
+
|
| 366 |
+
### Включить/выключить
|
| 367 |
+
- Без флага (`n_ctx=0`, ПОВЕДЕНИЕ ПО УМОЛЧАНИЮ): концепт полный (`-c`), как рабочая
|
| 368 |
+
dflash3 -> acceptance 79.9%, draft-KV ~1 ГБ. Рабочее поведение не ломаем.
|
| 369 |
+
- `--spec-draft-ctx 4096`: draft-KV ограничен ~4К окном, target полный. Замер acceptance/MiB.
|
| 370 |
+
|
| 371 |
+
### Запуск после сборки
|
| 372 |
+
```
|
| 373 |
+
cd build-p40-ring/bin
|
| 374 |
+
# эталон (без окна):
|
| 375 |
+
./llama-server ... --spec-type draft-dflash --spec-draft-n-max 7 ...
|
| 376 |
+
# с окном:
|
| 377 |
+
./llama-server ... --spec-type draft-dflash --spec-draft-n-max 7 --spec-draft-ctx 4096 ...
|
| 378 |
+
```
|
| 379 |
+
|
| 380 |
+
### Следующие шаги (после замера на GPU у владельца)
|
| 381 |
+
- Если при `--spec-draft-ctx` acceptance ~79.9% -> шаги 2-4 достаточно, фиксируем.
|
| 382 |
+
- Если падает на длинном промпте -> шаг 5 (явные маски окна) / шаг 6 (кастомный ring).
|
| 383 |
+
|
| 384 |
+
## 11. РЕЗУЛЬТАТЫ ТЕСТОВ НА GPU (Tesla P40, 24 ГБ) - кольцо работает
|
| 385 |
+
|
| 386 |
+
Сборка: `llama-dflash3-pr/build-p40-ring/bin/llama-server`.
|
| 387 |
+
Модели: target `Qwen3.8-27B-UD-Q4_K_XL.gguf`, draft `Qwen3.8-27B-DFlash2-lucebox-q8_0.gguf`.
|
| 388 |
+
|
| 389 |
+
### Тест 1. Одинаковый короткий промпт (29 токенов, n_predict=60, temp=0, reasoning on)
|
| 390 |
+
| режим | -c | draft acceptance | mean len | acc per pos | tok/s |
|
| 391 |
+
|---|---|---|---|---|---|
|
| 392 |
+
| **БЕЗ кольца** (полный draft-KV) | 8192 | **0.64865** (48/74) | 5.36 | (0.818,0.727,0.727,0.636,0.545,0.455,0.455) | 24.09 |
|
| 393 |
+
| **С кольцом** `--spec-draft-ctx 4096` | 8192 | **0.64865** (48/74) | 5.36 | (0.818,0.727,0.727,0.636,0.545,0.455,0.455) | 24.03 |
|
| 394 |
+
|
| 395 |
+
-> **Acceptance и mean len БИТ-В-БИТ одинаковы.** Кольцо НЕ ухудшает попадание.
|
| 396 |
+
|
| 397 |
+
### Тест 2. Запуск на полном контексте - кольцо критично
|
| 398 |
+
- `-c 111072` **БЕЗ** кольца: сервер НЕ стартует - `failed to allocate buffer for kv cache`
|
| 399 |
+
(draft-KV ~1 ГБ+ на 24 ГБ P40, рядом с target).
|
| 400 |
+
- `-c 111072` **С** кольцом (`--spec-draft-ctx 4096`): стартует, GPU 23940/24576 MiB.
|
| 401 |
+
|
| 402 |
+
-> Именно ради этого кольцо и нужно: draft-KV ~1049 MiB -> ~48 MiB.
|
| 403 |
+
|
| 404 |
+
### Почему 64.9% а не 79.9% в Тесте 1
|
| 405 |
+
Потому что промпт с `--reasoning on`: первые ~20 из 60 токенов - thinking-секция
|
| 406 |
+
(другое распределение, чем т��ст 433/542 из README_DFLASH2.md). Важно не абсолютное
|
| 407 |
+
значение, а что кольцо == полный контекст на ОДНОМ промпте.
|
| 408 |
+
|
| 409 |
+
### Долгий тест (эссе, n_predict=3050, temp=0, reasoning on)
|
| 410 |
+
| режим | -c | draft acceptance | mean len | tok/s |
|
| 411 |
+
|---|---|---|---|---|
|
| 412 |
+
| **DFlash с кольцом** `--spec-draft-ctx 4096` (n_max=7) | 8192 | **0.24827** (1943/7826) | 2.74 | ~11.7 |
|
| 413 |
+
| **MTP** `--spec-type draft-mtp` (n_max=3, draft вшит в модель) | 8192 | **0.48934** (1813/3705) | 2.47 | **16.7** |
|
| 414 |
+
|
| 415 |
+
Оговорки:
|
| 416 |
+
- n_max разный: DFlash чертит до 7 токенов/шаг, MTP до 3. При большем n_max acceptance
|
| 417 |
+
математически ниже (больше позиций для промаха) - видно по числу сгенерированных:
|
| 418 |
+
7826 у DFlash против 3705 у MTP. Это НЕ сравнение «яблоки с яблоками».
|
| 419 |
+
- У MTP нет отдельной draft-модели -> нет её накладных расходов на P40, поэтому 16.7 t/s.
|
| 420 |
+
- Короткий MTP-замер (1/3 = 0.333) статистически мусорный: 1 шаг спека, 3 токена.
|
| 421 |
+
|
| 422 |
+
### Тест 3. Короткий промпт (29 токенов, n_predict=60) - DFlash vs MTP
|
| 423 |
+
| режим | draft acceptance | mean len | tok/s |
|
| 424 |
+
|---|---|---|---|
|
| 425 |
+
| DFlash с кольцом (n_max=7) | **0.64865** (48/74) | 5.36 | 24.09 |
|
| 426 |
+
| MTP (n_max=3) | 0.33333 (1/3) | 2.00 | 12.63 |
|
| 427 |
+
|
| 428 |
+
-> Короткий MTP-замер статистически крошечный (3 токена, 1 принят) - не показатель.
|
| 429 |
+
По длинному тесту MTP быстрее и с лучшим acceptance, но при меньшем n_max.
|
| 430 |
+
|
| 431 |
+
### Тест 4. Тот же промт на ОРИГИНАЛЬНОМ Luce dflash_server (референс)
|
| 432 |
+
Запуск: `DFLASH_SINGLE_CHAIN_CHECKPOINT_F32=1 DFLASH_FAST_ROLLBACK_THRESHOLD=1
|
| 433 |
+
LUCE_Q8_MEMO=1 DFLASH_KV_ROTATE=0 ./server/build/dflash_server ... --fa-window 2048
|
| 434 |
+
--cache-type-k q8_0 --cache-type-v q8_0 --max-ctx 8192 --port 8011`.
|
| 435 |
+
API - OpenAI-совместимый (`/v1/chat/completions`), НЕ `/completion`.
|
| 436 |
+
|
| 437 |
+
ВАЖНО: с `reasoning: {effort: low}` Luce-спек НЕ работает:
|
| 438 |
+
`[budget-hook] spec-decode close at committed=52/3050 (remaining=3050 <= hard_limit=4096)`
|
| 439 |
+
- budget-hook Luce (hard_limit_reply_budget=4096 из model_card) при max_tokens <= 4096
|
| 440 |
+
force-close'ит на 1-м шаге и падает в AR: `[ar-decode] tokens=2837 13.58 tok/s`,
|
| 441 |
+
`target_forwards=2` - acceptance неизмерим. Это НЕ кольцо, это их бюджет-хук.
|
| 442 |
+
|
| 443 |
+
С `"thinking":{"type":"disabled"}` спек Luce работает:
|
| 444 |
+
| режим | draft acceptance | avg_commit | steps | tok/s |
|
| 445 |
+
|---|---|---|---|---|
|
| 446 |
+
| **Luce** dflash_server (q_len=8, ring cap=4096) | **0.372** (2189/5880) | **2.98** | 735 | 15.69 |
|
| 447 |
+
|
| 448 |
+
Luce ring активен: `[draft-kv] ctx-KV ring active: cap=4096 kv_total=4128 a_step=18
|
| 449 |
+
layers=5 f16 cache 80.6 MiB`.
|
| 450 |
+
|
| 451 |
+
### Сводная таблица (длинный тест, эссе, temp 0)
|
| 452 |
+
| | DFlash llama.cpp кольцо (n_max=7) | MTP (n_max=3) | Luce (q_len=8) |
|
| 453 |
+
|---|---|---|---|
|
| 454 |
+
| draft acceptance | 0.24827 (1943/7826) | 0.48934 (1813/3705) | **0.372** (2189/5880) |
|
| 455 |
+
| mean len / avg_commit | 2.74 | 2.47 | **2.98** |
|
| 456 |
+
| tok/s | ~11.7 | **16.7** | 15.69 |
|
| 457 |
+
| draft-KV | 47.81 MiB | — | 80.6 MiB f16 |
|
| 458 |
+
|
| 459 |
+
Оговорки: длины цепочек разные (7/3/8) - acceptance не сопоставим напрямую;
|
| 460 |
+
Luce мерился без thinking (с reasoning on его спек убит budget-hook'ом);
|
| 461 |
+
по avg_commit Luce (2.98) > DFlash llama.cpp (2.74) > MTP (2.47).
|
| 462 |
+
|
| 463 |
+
### Тест 5. ОДИНАКОВЫЙ промт на Luce vs llama.cpp кольцо (Count 1-500, temp 0, без thinking)
|
| 464 |
+
Одинаковый промт, одинаковый объём - честное сравнение реализации.
|
| 465 |
+
| | Luce (q_len=8) | llama.cpp кольцо (n_max=7) |
|
| 466 |
+
|---|---|---|
|
| 467 |
+
| токенов | 1892 | 1946 |
|
| 468 |
+
| tok/s | **42.42** | 33.92 |
|
| 469 |
+
| draft acceptance | **99.8%** (1892/1896) | 97.4% (1697/1743) |
|
| 470 |
+
| avg_commit / mean len | **7.98** | 7.82 |
|
| 471 |
+
|
| 472 |
+
-> Разница 42.4 vs 33.9 = 1.25x, НЕ 2x. Старые «39 vs 24» были РАЗНЫЕ промты:
|
| 473 |
+
24 tok/s = короткое эссе с reasoning (acceptance 0.65), 39 = счёт (acceptance 0.91).
|
| 474 |
+
На одинаковом контенте llama.cpp отстаёт на 25% из-за: цепочки 7 вместо 8,
|
| 475 |
+
обычного replay вместо fast-rollback, отсутствия fused-проекций для Pascal.
|
| 476 |
+
На счёте acceptance у обоих ~97-99%, поэтому разница = чистая цена имплементации.
|
| 477 |
+
Короткий счёт на Luce (51 токен): 38.90 tok/s, acceptance 91.1%, avg_commit 7.29.
|
| 478 |
+
|
| 479 |
+
### Тест 6. КОРЕНЬ разницы: MMVQ на батче 8 (Pascal)
|
| 480 |
+
Профилирование (nsys + DEBUG_TIMINGS + DBG-логи n_tokens/n_seq_tokens):
|
| 481 |
+
- verify-батч = 8 токенов (подтверждено логом n_tokens_all=8), НЕ 1.
|
| 482 |
+
- nsys: 67-74% времени verify - `mul_mat_vec_q` (по-токенные MMVQ матмулы),
|
| 483 |
+
батчевые `mul_mat_q` ~8%. fused `gated_delta_net_cuda` всего 2.6%.
|
| 484 |
+
- ПРИЧИНА: `ggml/src/ggml-cuda/mmvq.cuh:3`
|
| 485 |
+
`#define MMVQ_MAX_BATCH_SIZE 8` - на Pascal (не CDNA) `should_use_mmvq`
|
| 486 |
+
возвращает `ne11 <= MMVQ_MAX_BATCH_SIZE` => батч РОВНО 8 -> ВСЕ квантованные
|
| 487 |
+
матмулы идут через медленный по-токенный MMVQ вместо батчевого MMQ.
|
| 488 |
+
- FIX (временный, в коде): `MMVQ_MAX_BATCH_SIZE_CFG 7` в mmvq.cuh + mmvq.cu:336
|
| 489 |
+
(батч 8 -> MMQ). Итог на счёте 1-500 (temp 0, без thinking):
|
| 490 |
+
| | до фикса (MMVQ@8) | после (MMQ@8) | Luce |
|
| 491 |
+
|---|---|---|---|
|
| 492 |
+
| t_decode (verify) | 196 ms | 169 ms | 161 ms |
|
| 493 |
+
| tok/s | 34.13 | 36.81 | 42.42 |
|
| 494 |
+
| draft acceptance | 0.97361 | 0.91903 | 0.998 |
|
| 495 |
+
| mean len | 7.82 | 7.43 | 7.98 |
|
| 496 |
+
- verify почти догнал Luce (169 vs 161ms). Остаток 13% (36.8 vs 42.4) -
|
| 497 |
+
acceptance: Luce 99.8% vs llama.cpp 91.9-97.4% (MMQ менее точен на границе
|
| 498 |
+
argmax). Это отдельная задача (точность verify-логитов/селектора), не скорость kernel.
|
| 499 |
+
- ВНИМАНИЕ: MMVQ_MAX_BATCH_SIZE_CFG=7 - экспериментальный фикс в коде ggml.
|
| 500 |
+
Проверить на реальном промте качество (acceptance упал с 0.974 до 0.919).
|
| 501 |
+
- Короткий счёт (Count 1-20, 92 токена): MMVQ@8 28.87 tok/s -> MMQ@8 34.71 tok/s
|
| 502 |
+
(+20%), но acceptance 0.868 vs 0.796 - тоже упал. Вывод: скорость ядра (verify)
|
| 503 |
+
догнали, остаток - acceptance (MMQ менее точен на границе argmax).
|
| 504 |
+
|
| 505 |
+
### Тест 7. ПРАВИЛЬНЫЙ перенос: MMQ по типам НЕ работает, ключ - консистентность
|
| 506 |
+
Попытка разделить MMQ-порог по типу кванта: q8_0 (драфт) -> MMVQ (точный),
|
| 507 |
+
K-кванты target (Q4_K/Q5_K/Q6_K) -> MMQ (быстрый). Результат НАМНОГО ХУЖЕ:
|
| 508 |
+
| конфиг | t_decode | acceptance | tok/s |
|
| 509 |
+
|---|---|---|---|
|
| 510 |
+
| MMVQ все (baseline) | 196 ms | 0.97361 | 34.13 |
|
| 511 |
+
| MMQ все (cap 7) | 169 ms | 0.91903 | 36.81 |
|
| 512 |
+
| MMQ только K-кванты (q8_0->MMVQ) | 164 ms | **0.83934** | 35.55 |
|
| 513 |
+
|
| 514 |
+
-> Причина: acceptance зависит НЕ от «точности» одного пути, а от КОНСИСТЕНТНОСТИ
|
| 515 |
+
драфт и verify. Когда драфт (q8_0, MMVQ) и verify (target, MMQ) считают по-разному,
|
| 516 |
+
argmax на границе РАСХОДИТСЯ -> acceptance падает сильнее, чем у любого единого пути.
|
| 517 |
+
Правило: драфт и verify должны идти через ОДИН и тот же матмул-путь (оба MMVQ или
|
| 518 |
+
оба MMQ). Полный MMQ (cap 7) - лучший компромисс: verify 196->169ms, tok/s 34.1->36.9.
|
| 519 |
+
|
| 520 |
+
Почему у Luce acceptance 99.8% а у нас 91.9%: у Luce verify использует GPU argmax
|
| 521 |
+
(ggml_argmax в графе, перенос на CPU только 8xint32) + их драфт и verify численно
|
| 522 |
+
консистентны (свои kernels, F32 lm_head). У llama.cpp verify читает ПОЛНЫЕ logits
|
| 523 |
+
(8x248320 float = 8MB) на CPU каждый шаг + CPU-argmax + MMQ на границе argmax
|
| 524 |
+
менее точен, чем MMVQ (0.919 vs 0.974 при едином пути). Остаток 13% =
|
| 525 |
+
acceptance (7.43 vs 7.98 токенов/шаг) + цена шага (verify 169 vs 161ms,
|
| 526 |
+
post_decode+sampl 7.6ms CPU-чтение logits у нас, у Luce нет).
|
| 527 |
+
|
| 528 |
+
Что нужно для полного паритета:
|
| 529 |
+
1. GPU-argmax в verify (как Luce): ggml_argmax в графе, перенос 8xint32 вместо 8MB.
|
| 530 |
+
2. Консистентность: драфт и verify на одном пути (F32 lm_head + GPU argmax).
|
| 531 |
+
|
| 532 |
+
### Тест 8. ФИНАЛЬНАЯ оптимизация: MMQ cap 7 + backend sampling (-bs) = 37.66 tok/s
|
| 533 |
+
Ключевые открытия:
|
| 534 |
+
- Через chat API (честное сравнение) MMQ cap 7 ДАЁТ ВЫШЕ acceptance, чем MMVQ:
|
| 535 |
+
baseline cap 8 (MMVQ) 0.843 vs cap 7 (MMQ) 0.919! Прошлые выводы «MMQ менее
|
| 536 |
+
точен» были испорчены сравнением через РАЗНЫЕ API (/completion 0.974 vs chat).
|
| 537 |
+
- llama.cpp УЖЕ имеет GPU-argmax семплер: `llama_sampler_greedy_backend_apply`
|
| 538 |
+
(llama-sampler.cpp:1086 ggml_argmax). Флаг сервера `-bs` / `--backend-sampling`
|
| 539 |
+
(arg.cpp:2296) переводит сэмплинг на GPU -> verify-логиты НЕ читаются на CPU.
|
| 540 |
+
- t_sampl 2.07 -> 0.03ms, t_post_decode 5.7 -> 0.15ms (не читаем 8MB logits/шаг!).
|
| 541 |
+
|
| 542 |
+
Итоговые цифры (chat API, Count 1-500, temp 0):
|
| 543 |
+
| конфиг | t_decode | t_post+sampl | acceptance | tok/s |
|
| 544 |
+
|---|---|---|---|---|
|
| 545 |
+
| baseline (MMVQ cap 8) | 196 ms | 7.8 ms | 0.843 | 30.20 |
|
| 546 |
+
| MMQ cap 7 | 169 ms | 7.8 ms | 0.919 | 36.92 |
|
| 547 |
+
| **MMQ cap 7 + -bs** | 171 ms | **0.18 ms** | **0.919** | **37.66** |
|
| 548 |
+
| Luce | 161 ms | ~0 | 0.998 | 42.42 |
|
| 549 |
+
|
| 550 |
+
Короткий счёт (Count 1-20): MMQ+bs 35.80 tok/s, acceptance 0.868 (было 34.71/0.868).
|
| 551 |
+
Итого выжали 30.2 -> 37.7 tok/s (+25%) на том же промте. Остаток до Luce:
|
| 552 |
+
acceptance (0.919 vs 0.998 - MMQ на границе argmax) + их fused-граф verify (171 vs 161ms).
|
| 553 |
+
Команда запуска: ... --spec-draft-ctx 4096 --temp 0 --jinja -bs
|
| 554 |
+
|
| 555 |
+
### Тест 9. Python-код (сложная задача) - DFlash vs ngram режимы
|
| 556 |
+
Промт: "Write a Python class implementing a concurrent LRU cache with TTL support...":
|
| 557 |
+
class+type hints+docstrings, lock-free variant, tradeoffs. n_predict 4096, temp 0, chat API.
|
| 558 |
+
|
| 559 |
+
| режим | draft acceptance | mean len | tok/s | verify | примечание |
|
| 560 |
+
|---|---|---|---|---|---|
|
| 561 |
+
| **DFlash cap7+bs** | 0.25271 (2615/10348) | 2.77 | **13.89** | 171ms | лучший на коде |
|
| 562 |
+
| ngram-mod (match24/min48/max64) | — (0 drafts!) | — | 12.90 | 77.6ms | НЕ генерировал: #gen drafts=0 |
|
| 563 |
+
| ngram-map-k4v (n8/m24/minhits1) | 0.06591 (29/440) | 2.32 | 12.57 | 77.6ms | 440 drafts за 4043 вызова |
|
| 564 |
+
| AR (без спека) | — | — | ~13.2 | 75.9ms | база |
|
| 565 |
+
|
| 566 |
+
Выводы:
|
| 567 |
+
- На КОДЕ DFlash выигрывает у ngram и AR лишь ~5-8% (13.89 vs 12.6-13.2):
|
| 568 |
+
код менее предсказуем, чем счёт (acceptance 0.25 vs 0.92 на числах).
|
| 569 |
+
- ngram-режимы на коде почти бесполезны: ngram ловит ПОВТОРЯЮЩИЙСЯ текст,
|
| 570 |
+
а код не повторяется. ngram-mod вообще дал 0 драфтов (n_match=24 слишком длинный
|
| 571 |
+
для кода + n_min=48), ngram-map-k4v - 1% acceptance.
|
| 572 |
+
- Сравнение на счёте (Count 1-500): DFlash 37.66 tok/s vs ngram - не тестировался,
|
| 573 |
+
но ngram на числах тоже работал бы плохо (нет повторов).
|
| 574 |
+
- DFlash силён на: числах/JSON/структурном тексте. Слаб на: свободном коде/прозе.
|
| 575 |
+
|
| 576 |
+
### Тест 10. Python-код: DFlash vs MTP vs Luce (тот же промт)
|
| 577 |
+
Тот же промт LRU cache, n_predict 4096, temp 0. MTP вшит в GGUF (без -md).
|
| 578 |
+
|
| 579 |
+
| режим | acceptance | mean len / avg_commit | tok/s | verify |
|
| 580 |
+
|---|---|---|---|---|
|
| 581 |
+
| **Luce** (q_len=8) | **0.709** (3591/5064) | **5.67** | **29.81** | 161ms |
|
| 582 |
+
| **MTP** llama.cpp (n_max=3) | 0.483 (2423/5016) | 2.45 | **16.24** | ~122ms |
|
| 583 |
+
| DFlash cap7+bs (n_max=7) | 0.25271 (2615/10348) | 2.77 | 13.89 | 171ms |
|
| 584 |
+
| ngram-map-k4v | 0.0659 | 2.32 | 12.57 | 77.6ms |
|
| 585 |
+
| AR (без спека) | — | — | ~13.2 | 75.9ms |
|
| 586 |
+
|
| 587 |
+
ПОЧЕМУ DFlash просел на коде (0.25 acceptance, 13.89 tok/s):
|
| 588 |
+
- DFlash чертит ЦЕПОЧКУ из 7 токенов подряд. На числах угадать след. число
|
| 589 |
+
легко (acceptance 0.92), на коде (свободная генерация) 7 токенов подряд
|
| 590 |
+
почти никогда не совпадают -> acceptance 0.25, спек ~бесполезен (13.89 ~ AR 13.2).
|
| 591 |
+
- MTP (3 токена) с acceptance 0.48 выигрывает: короткая цепочка чаще проходит
|
| 592 |
+
целиком -> 16.24 tok/s.
|
| 593 |
+
- Luce 29.81 tok/s - их fused-граф verify (161ms) + GPU argmax + их own kernels:
|
| 594 |
+
тот же драфт, но verify в 1.4x быстрее и acceptance 0.709 (их численно
|
| 595 |
+
консистентный путь). Разница DFlash llama.cpp vs Luce НА КОДЕ огромна (13.9 vs
|
| 596 |
+
29.8 = 2.1x) - не из-за кольца, а из-за скорости/точности verify-графа.
|
| 597 |
+
- Вывод: DFlash llama.cpp хорош на структурном тексте (37.7 tok/s на счёте),
|
| 598 |
+
но на коде проигрывает MTP и сильно Luce.
|
| 599 |
+
|
| 600 |
+
### Тест 11. ⭐ НАЙДЕНА ПРИЧИНА "коллапса" на коде: режим reasoning, а НЕ порт
|
| 601 |
+
**Вывод: перенос DFlash2 в llama.cpp был корректен. Разница 0.25 vs 0.71 на коде —
|
| 602 |
+
артефакт режима thinking (llama.cpp сервер по умолчанию `--reasoning on`, Luce
|
| 603 |
+
тестировался с thinking disabled).**
|
| 604 |
+
|
| 605 |
+
Одинаковый бинарь, тот же LRU-промт, n_predict 4096, temp 0, MMQ cap 7 + -bs:
|
| 606 |
+
|
| 607 |
+
| режим | acceptance | mean len | acc per pos | tok/s |
|
| 608 |
+
|---|---|---|---|---|
|
| 609 |
+
| llama.cpp DFlash, **thinking ON** (старое) | 0.25271 | 2.77 | (0.720, 0.468, 0.295, 0.178, 0.115, 0.060, 0.034) | 13.89 |
|
| 610 |
+
| llama.cpp DFlash, **--reasoning off** (MMVQ cap 8) | 0.64238 (3350/5215) | 5.50 | (0.919, 0.804, 0.690, 0.607, 0.546, 0.497, 0.434) | 23.41 |
|
| 611 |
+
| **llama.cpp DFlash, --reasoning off + MMQ cap 7 (ФИНАЛ)** | **0.64724** (3354/5182) | **5.53** | **(0.914, 0.810, 0.710, 0.632, 0.548, 0.479, 0.435)** | **27.69** |
|
| 612 |
+
| Luce DFlash (thinking disabled) | 0.709 (3591/5064) | 5.67 | (плоская) | 29.81 |
|
| 613 |
+
|
| 614 |
+
Что произошло:
|
| 615 |
+
- С thinking ON модель пишет `<think>`-блоки: внутренние рассуждения
|
| 616 |
+
непредсказуемы, цепочка драфта экспоненциально затухает
|
| 617 |
+
(0.72, 0.47, 0.30, ...) — это НЕ дефект драфтера, а свойства текста.
|
| 618 |
+
- С thinking OFF кривая плоская (0.92, 0.80, 0.69, ...), acceptance 0.64,
|
| 619 |
+
скорость 23.4 tok/s. Разрыв с Luce (0.709 / 29.81) теперь малый:
|
| 620 |
+
acceptance 90%, скорость 78%.
|
| 621 |
+
|
| 622 |
+
Дополнительно (тот же бинарь, thinking off):
|
| 623 |
+
- Count 1-500 (финал, MMQ cap 7): acceptance **0.99759**, mean len 7.98,
|
| 624 |
+
**40.41 tok/s** (Luce: 0.998 / 7.98 / 42.42 — acceptance равен, скорость 95%).
|
| 625 |
+
- Проза (эссе 3050, финал MMQ cap 7): acceptance 0.288, mean 3.02,
|
| 626 |
+
15.27 tok/s — Luce на прозе ~0.372 / 15.69 (с thinking low): свободная
|
| 627 |
+
проза тяжела для обоих, это не регресс порта (acceptance 78%, скорость 97%).
|
| 628 |
+
|
| 629 |
+
Опровергнутые гипотезы (все проверены замером, все no-op):
|
| 630 |
+
1. **seq_rm-стирание фич** (ckpt.pos_max+1 vs pos_next()): бит-в-бит
|
| 631 |
+
одинаковый acceptance (0.26731 vs 0.25271, кривая acc-per-pos идентична до
|
| 632 |
+
3 знака). Причина: update_pos() обновляет ckpt.pos_max на каждое шаге,
|
| 633 |
+
ckpt.pos_max+1 == pos_next() в нормальном потоке; checkpoint
|
| 634 |
+
(update_dft/load_dft) обновляется каждый шаг, а не раз в 512.
|
| 635 |
+
2. **Кольцо окна** (--spec-draft-ctx 0/4096): одинаково.
|
| 636 |
+
3. **Математика цепочки** (unary raw vs log-prob, порядок mul_mat,
|
| 637 |
+
transposed weights): тождественна Luce (score = cand_lp + Σ pr·hp·sc,
|
| 638 |
+
prev_row = выбранный кандидат). hproj [hdim,rank], pred/succ [rank,vocab]
|
| 639 |
+
— одинаковые shape в обоих загрузчиках.
|
| 640 |
+
4. **K/V контекста драфта**: llama.cpp embd-путь делает ровно shallow-инъекцию
|
| 641 |
+
(wk/wv @ fused + k_norm + rope) как Luce draft_ctx_kv_rows.
|
| 642 |
+
5. **Фичи таргета**: t_layer_inp[il+1] = post-layer hidden (qwen35.cpp:246) =
|
| 643 |
+
то же, что Luce capture (cur после FFN+residual).
|
| 644 |
+
|
| 645 |
+
Код сейчас: MMQ_MAX_BATCH_SIZE_CFG=7 (MMQ на батче 8, P40), -bs, seq_rm
|
| 646 |
+
возвращён к оригиналу (ckpt.pos_max+1) с поясняющим комментарием.
|
| 647 |
+
|
| 648 |
+
## Test 12 — DFlash vs MTP, thinking ON/OFF (код LRU, 4 ячейки)
|
| 649 |
+
|
| 650 |
+
Замер: тот же LRU-промпт (Python-класс кэша, 4096 токенов, temp 0).
|
| 651 |
+
DFlash-n7: `--spec-type draft-dflash --spec-draft-n-max 7` + `-md Qwen3.8-27B-DFlash2-lucebox-q8_0.gguf`.
|
| 652 |
+
MTP-n3: `--spec-type draft-mtp --spec-draft-n-max 3`, MTP-слои из таргет-GGUF.
|
| 653 |
+
Thinking ON = дефолт (chat template), OFF = `--reasoning off`.
|
| 654 |
+
|
| 655 |
+
| Конфиг | Thinking | Acceptance | Mean len | tok/s |
|
| 656 |
+
|---|---|---|---|---|
|
| 657 |
+
| DFlash-n7 | OFF | 0.647 | 5.53 | **27.69** |
|
| 658 |
+
| DFlash-n7 | ON | 0.25271 | 2.77 | 13.90 |
|
| 659 |
+
| MTP-n3 | OFF | 0.8205 | 3.46 | **23.61** |
|
| 660 |
+
| MTP-n3 | ON | 0.48452 | 2.45 | 16.31 |
|
| 661 |
+
|
| 662 |
+
acc-per-pos (OFF/ON):
|
| 663 |
+
- DFlash-n7: OFF (0.914, 0.810, 0.710, 0.632, 0.548, 0.479, 0.435)
|
| 664 |
+
ON (0.695, 0.434, 0.283, 0.170, 0.098, 0.055, 0.034)
|
| 665 |
+
- MTP-n3: OFF (0.931, 0.816, 0.715)
|
| 666 |
+
ON (0.706, 0.461, 0.286)
|
| 667 |
+
|
| 668 |
+
Выводы:
|
| 669 |
+
1. **Thinking ON убивает оба**, но DFlash страдает сильнее: падение
|
| 670 |
+
acceptance 0.647→0.253 (-61%), MTP 0.821→0.485 (-41%). Reasoning-текст
|
| 671 |
+
непредсказуем для любой цепочки, длинная (7) рвётся быстрее короткой (3).
|
| 672 |
+
2. С thinking ON **по скорсти MTP (16.3) > DFlash (13.9)** — длинная цепочка
|
| 673 |
+
платит verify 8 ради ~2.4 принятых.
|
| 674 |
+
3. С thinking OFF **по скорости DFlash (27.7) > MTP (23.6)** — цепочка живёт
|
| 675 |
+
на коде, хвост 4-7 окупает verify.
|
| 676 |
+
4. Первые 3 позиции: MTP чуть точнее DFlash (0.931/0.816/0.715 vs
|
| 677 |
+
0.914/0.810/0.710) — DFlash декодит блок параллельно с масками и не видит
|
| 678 |
+
собственные пики соседей, MTP поочередно видит свой предыдущий токен.
|
| 679 |
+
5. Итог для сценариев:
|
| 680 |
+
- агент/код: DFlash-n7 + `--reasoning off` (27.7 t/s);
|
| 681 |
+
- рерайт длинного текста: DFlash-n3 (~23 на коде, короче на прозе) или
|
| 682 |
+
MTP-n3 — эквивалентны по форме, DFlash-n3 быстрее драфтит;
|
| 683 |
+
- если нужен reasoning ON (дефолт) — MTP-n3 меньше теряет (16.3 vs 13.9),
|
| 684 |
+
но оба сильно ниже than OFF.
|
| 685 |
+
|
| 686 |
+
Комбо DFlash+MTP в одном сервере НЕ работает в этом билде:
|
| 687 |
+
`--spec-type draft-dflash,draft-mtp` c -md падает ("MTP context requested but
|
| 688 |
+
model doesn't contain MTP layers") — код создаёт ОДИН ctx_dft для всех типов и
|
| 689 |
+
просит MTP-слои у DFlash-GGUF. Плюс даже при успехе первый-выигравший (MTP
|
| 690 |
+
приоритетнее) сделал бы комбо = чистый MTP. Эффективное объединение = DFlash
|
| 691 |
+
с коротким блоком (n_max=3), даёт кривую MTP (0.932/0.842/0.741) и ту же
|
| 692 |
+
скорость (~23.3).
|
| 693 |
+
|
| 694 |
+
## Test 13 — Динамическая длина цепочки по margin селектора [DFLASH-DYNAMIC]
|
| 695 |
+
|
| 696 |
+
Проблема: thinking ON рубит спекуляцию (acceptance 0.25, 13.9 t/s). Идея —
|
| 697 |
+
не отключать reasoning, а дать цепочке самой резаться, когда селектор неуверен.
|
| 698 |
+
|
| 699 |
+
### A. Первая попытка: softmax-уверенность выбранного токена — ДЕКОРАТИВНА
|
| 700 |
+
p_chosen = softmax(top-16 скоров); обрыв при p_chosen < p_min.
|
| 701 |
+
На коде thinking ON: 14.56 t/s (+4.7%), проверено всё те же 6.8/7 токенов.
|
| 702 |
+
Причина: скоры селектора ~сырые логиты (20-45), softmax по топ-16 би-модален —
|
| 703 |
+
378/378 пиков p>0.7. "Уверенно неправ": высокий score НЕ означает правильный
|
| 704 |
+
токен на reasoning. Порог 0.2 не отделяет неуверенность.
|
| 705 |
+
|
| 706 |
+
### B. Правильный сигнал: margin (top1-top2) в лог-скорах — РАБОТАЕТ
|
| 707 |
+
Margins реально колеблются: 36% пиков margin<0.3 ("монетка"), остальные >1.5.
|
| 708 |
+
Обрыв: margin < p_min*3.0. n_min=1 (иначе короткие цепочки стираются).
|
| 709 |
+
|
| 710 |
+
Подбор порога (код LRU 4096, thinking ON):
|
| 711 |
+
| p_min (margin*3) | проверено/шаг | принято/шаг | tok/s |
|
| 712 |
+
|---|---|---|---|
|
| 713 |
+
| 0 (статика n7) | 7.4 | 2.77 | 13.90 |
|
| 714 |
+
| 0.15 | 4.0 | 2.79 | 16.92 |
|
| 715 |
+
| 0.2 | 3.8 | 2.64 | 16.00 |
|
| 716 |
+
| 0.3 | 3.4 | 2.71 | 17.10 |
|
| 717 |
+
| **0.35** | **3.1** | **2.83** | **18.26** |
|
| 718 |
+
| 0.4 | 3.0 | 2.61 | 17.21 |
|
| 719 |
+
|
| 720 |
+
Оптимум 0.35: +31% скорости от статики, acceptance вырос до 0.638
|
| 721 |
+
(не упал!), проверено всего 3.1/7. Это ВЫШЕ жёсткого n3 (17.30) и MTP-n3
|
| 722 |
+
(16.31). Динамика умнее жёсткой обрезки: где селектор уверен — цепочка
|
| 723 |
+
идёт дальше, где колеблется — рвётся.
|
| 724 |
+
|
| 725 |
+
### C. Проза (эссе 3050, thinking ON) — главный выигрыш
|
| 726 |
+
| Режим | acceptance | mean len | tok/s |
|
| 727 |
+
|---|---|---|---|
|
| 728 |
+
| Статика n7 | 0.288 | 3.02 | 15.27 |
|
| 729 |
+
| **Динамика p_min 0.35** | **0.769** | **4.07** | **24.61** |
|
| 730 |
+
|
| 731 |
+
acceptance x2.7, скорость +61%. Проза больше НЕ провал DFlash — динамика
|
| 732 |
+
решает "почему на прозе мало": цепочка рвётся ровно там, где селектор
|
| 733 |
+
колеблется, и не платит за мусорный хвост verify.
|
| 734 |
+
|
| 735 |
+
### Режимы (итог)
|
| 736 |
+
- кодинг, без reasoning: DFlash-n7 + `--reasoning off` → 27.7 t/s
|
| 737 |
+
- **с мышлением (дефолт): DFlash-n7 + p_min 0.35 → 18.26 (код) / 24.61 (проза)**
|
| 738 |
+
- комбо не нужно: динамика сама адаптируется к тексту
|
| 739 |
+
|
| 740 |
+
Код: common/speculative.cpp, цикл селектора DFlash2 — обрыв по margin
|
| 741 |
+
(p_min*3.0), debug CHAIN печатает m= и p=. Сборка build-p40-ring.
|
| 742 |
+
|
| 743 |
+
## Test 14 — DFlash на Qwen3.6-35B-A3B (MoE)
|
| 744 |
+
|
| 745 |
+
Пользовательская модель: Qwen_Qwen3.6-35B-A3B-Q4_K_M.gguf (qwen35moe, 3B активных
|
| 746 |
+
из 35B). DFlash-драфта v2 под неё нет — сконвертирован официальный
|
| 747 |
+
z-lab/Qwen3.6-35B-A3B-DFlash (DFlash v1, 6 dense-слоёв, hidden 2048,
|
| 748 |
+
block_size 16, target_layer_ids [1,6,11,16,22,27,32,37] = 8 слоёв таргета,
|
| 749 |
+
fc [2048,16384]).
|
| 750 |
+
|
| 751 |
+
Конвертация (Luce server/scripts):
|
| 752 |
+
```
|
| 753 |
+
caмa: python3 convert_dflash_to_gguf.py draft-hf/model.safetensors draft-a3b-f16.gguf
|
| 754 |
+
python3 quantize_dflash_draft.py draft-a3b-f16.gguf draft-a3b-q8_0.gguf --scheme q8_0
|
| 755 |
+
```
|
| 756 |
+
Результат: draft-a3b-q8_0.gguf (410MB, 43 tensor q8_0 + 26 as-is).
|
| 757 |
+
Прошла без правок кода форка — форк загрузил qwen35moe таргет + dflash v1 драфт.
|
| 758 |
+
|
| 759 |
+
Замеры (P40, thinking ON, dynamic 0.35):
|
| 760 |
+
| Тест | acceptance | mean len | tok/s |
|
| 761 |
+
|---|---|---|---|
|
| 762 |
+
| Код LRU 4096 | 0.584 | 3.60 | **55.79** |
|
| 763 |
+
| Проза эссе 3050 | 0.483 | 2.65 | **46.51** |
|
| 764 |
+
|
| 765 |
+
acc per pos (код): (0.813, 0.550, 0.402, 0.312, 0.228, 0.169, 0.128)
|
| 766 |
+
acc per pos (проза): (0.751, 0.417, 0.221, 0.118, 0.076, 0.045, 0.022)
|
| 767 |
+
|
| 768 |
+
Сравнение с 27B-UD:
|
| 769 |
+
- код: 27B 27.69 t/s vs A3B **55.79** (x2.0) — MoE таргет дешевле в verify
|
| 770 |
+
- проза: 27B динам. 24.61 vs A3B **46.51** (x1.9)
|
| 771 |
+
|
| 772 |
+
Вывод: спекуляция на MoE-модели (A3B) в 2 раза эффективнее, чем на dense-27B,
|
| 773 |
+
потому что verify-батч (8 токенов) считает только 3B активных параметров.
|
| 774 |
+
DFlash-v1 (без селектора v2) тоже работает с dynamic-обрезанием (margin из
|
| 775 |
+
top-k). Для полноты — был бы DFlash2-драфт под A3B, gains ещё выше.
|
| 776 |
+
|
| 777 |
+
Команда (динамика 0.35):
|
| 778 |
+
llama-server -m Qwen_Qwen3.6-35B-A3B-Q4_K_M.gguf -md draft-a3b-q8_0.gguf
|
| 779 |
+
--spec-type draft-dflash --spec-draft-n-max 7 --spec-draft-n-min 1 --spec-draft-p-min 0.35 ...
|
| 780 |
+
|
| 781 |
+
## Test 15 — A3B: DFlash vs MTP vs AR (полная таблица)
|
| 782 |
+
|
| 783 |
+
P40, thinking ON, MoE-таргет Qwen3.6-35B-A3B-Q4_K_M (3B активных).
|
| 784 |
+
DFlash = наша конвертация z-lab v1 + dynamic 0.35; MTP = вшит в таргет-GGUF.
|
| 785 |
+
|
| 786 |
+
Код LRU 4096:
|
| 787 |
+
| Режим | acceptance | mean | tok/s | vs AR |
|
| 788 |
+
|---|---|---|---|---|
|
| 789 |
+
| AR baseline | - | - | 52.05 | 1.00x |
|
| 790 |
+
| DFlash n7 dyn 0.35 | 0.584 | 3.60 | 55.79 | 1.07x |
|
| 791 |
+
| DFlash n12 dyn 0.35 | 0.541 | 4.11 | 53.98 | 1.04x |
|
| 792 |
+
| MTP вшит n3 | 0.662 | 2.99 | 68.37 | 1.31x |
|
| 793 |
+
|
| 794 |
+
Проза эссе 3050:
|
| 795 |
+
| Режим | acceptance | mean | tok/s | vs AR |
|
| 796 |
+
|---|---|---|---|---|
|
| 797 |
+
| AR baseline | - | - | 52.72 | 1.00x |
|
| 798 |
+
| DFlash n7 dyn 0.35 | 0.483 | 2.65 | 46.51 | 0.88x |
|
| 799 |
+
| MTP вшит | 0.497 | 2.49 | 57.61 | 1.09x |
|
| 800 |
+
|
| 801 |
+
Выводы:
|
| 802 |
+
1. На A3B **MTP (вшит) — лучший режим**: код 68.4 (+31% AR), проза 57.6 (+9%).
|
| 803 |
+
Дёшево: MTP-голова вшита в MoE-таргет, verify почти бесплатный.
|
| 804 |
+
2. DFlash v1 на A3B слабый: на коде +7% (55.8), на прозе МЕДЛЕННЕЕ AR (46.5 vs 52.7).
|
| 805 |
+
v1 БЕЗ селектора (последоват. argmax) на свободном тексте не окупает даже
|
| 806 |
+
дешёвый verify. Для A3B нужен DFlash2-драфт (с селектором), его нет нигде.
|
| 807 |
+
3. n_max=12 НЕ помогает: mean 4.11 выше, но tok/s 53.98 < n7 55.79 (блок 12 масок
|
| 808 |
+
дороже драфтить, хвост 8-12 не окупается). Оптимум n7.
|
| 809 |
+
4. Пользователю: на 35B-A3B лучше всего собственный MTP (без -md, без драфта) —
|
| 810 |
+
`--spec-type draft-mtp --spec-draft-n-max 3`. DFlash-драфт v1 держать как
|
| 811 |
+
запасной для кода, где чуть быстрее AR (55.8 vs 52.0).
|
| 812 |
+
|
| 813 |
+
Вопрос "динамика для MTP": стандартный флаг УЖЕ есть — impl_draft_mtp в
|
| 814 |
+
speculative.cpp:1760 делает `if (cur_p->data[0].p < params.p_min) break;` —
|
| 815 |
+
обрыв по вероятности токена (не по margin как DFlash). Работает через
|
| 816 |
+
`--spec-draft-p-min`. Проверено по коду, не декоративно.
|
| 817 |
+
|
| 818 |
+
## Test 16 — MTP динамика (n_max=7 + p_min 0.3) на A3B — ДЕКОРАТИВНА
|
| 819 |
+
|
| 820 |
+
Проверка: даёт ли MTP сам себе длину через --spec-draft-p-min (обрыв по
|
| 821 |
+
вероятности токена в impl_draft_mtp, speculative.cpp:1760). Код LRU 4096:
|
| 822 |
+
|
| 823 |
+
| Режим | acceptance | mean len | проверено/шаг | принято/шаг | tok/s |
|
| 824 |
+
|---|---|---|---|---|---|
|
| 825 |
+
| MTP n3 (base) | 0.662 | 2.99 | ~3.0 | 2.63 | 68.37 |
|
| 826 |
+
| MTP n7 p_min0.3 | 0.454 | 3.67 | ~5.8 | 2.63 | 46.97 |
|
| 827 |
+
|
| 828 |
+
Вывод: p_min 0.3 у MTP НЕ режет цепочку на коде — проверено 5.8/шаг вместо
|
| 829 |
+
~3.0, принято столько же (2.63). Причина та же, что у softmax-динамики DFlash:
|
| 830 |
+
probability(top-1) у MTP почти всегда > 0.3 ("уверенно неправ" на позициях
|
| 831 |
+
3-7, где acceptance 0.31/0.25/0.18/0.14). Порог по p не отделяет мусорный
|
| 832 |
+
хвост.
|
| 833 |
+
|
| 834 |
+
Итог: для MTP жёсткий --spec-draft-n-max 3 оптимален (68.4 на A3B, 23.6 на
|
| 835 |
+
27B). Динамика по p бесполезна и для DFlash (margin - рабочая, p - нет),
|
| 836 |
+
и для MTP (p не режет). Единственная реально работающая авто-длина - наш
|
| 837 |
+
margin-обрыв в DFlash2 (Test 13).
|
| 838 |
+
|
| 839 |
+
## Test 17 — margin-динамика для MTP [DFLASH-DYNAMIC MTP] + ��водная таблица режимов
|
| 840 |
+
|
| 841 |
+
После Test 16 добавлен margin-обрыв и для MTP (speculative.cpp:1759-1774):
|
| 842 |
+
вместо порога по p0 обрыв по **запасу p0-p1** (margin = p0, если один кандидат;
|
| 843 |
+
margin = p0-p1 иначе; обрыв при margin < p_min). Порог берётся из
|
| 844 |
+
--spec-draft-p-min, но применяется к ВЕРОЯТНОСТНОМУ запасу (0..1), а не к
|
| 845 |
+
логам-скорам. n_min=1 обязателен, иначе короткие цепочки стираются.
|
| 846 |
+
|
| 847 |
+
Замеры (A3B, код LRU 4096; сравнить: MTP-n3 base 68.37, n7 p_min0.3 46.97,
|
| 848 |
+
n7 margin0.3 63.60, n7 margin0.15 58.15):
|
| 849 |
+
|
| 850 |
+
| Режим | проверено/шаг | принято/шаг | acceptance | mean | tok/s |
|
| 851 |
+
|---|---|---|---|---|---|
|
| 852 |
+
| MTP n3 (жестко) | ~3.0 | 2.63 | 0.662 | 2.99 | 68.37 |
|
| 853 |
+
| MTP n7 + p_min0.3 (старый p) | ~5.8 | 2.63 | 0.454 | 3.67 | 46.97 |
|
| 854 |
+
| MTP n7 + margin0.15 | ~4.1 | 2.69 | 0.654 | 4.11 | 58.15 |
|
| 855 |
+
| MTP n7 + margin0.3 | ~3.3 | 2.62 | **0.793** | 4.09 | 63.60 |
|
| 856 |
+
| MTP n7 + margin0.3 (проза) | - | 1.24 | 0.669 | 2.79 | 52.94 |
|
| 857 |
+
|
| 858 |
+
Вывод по margin-MTP: в отличие от p-порога, margin РЕАЛЬНО режет цепочку
|
| 859 |
+
(проверено 5.8 -> 3.3-4.1). На коде margin0.3 даёт acceptance 0.793 (против
|
| 860 |
+
0.662 у n3) при том же принятом — режет «монетки», тянет где уверен. НО на
|
| 861 |
+
A3B жёсткое n3 всё равно быстрее (68.4 vs 63.6): декод блока n7 дороже, а
|
| 862 |
+
дешёвый verify не окупает длинный хвост. Margin-MTP бессмысленен на A3B, где
|
| 863 |
+
verify почти бесплатен; он ценен только для 27B (дорогой verify), где стоит
|
| 864 |
+
проверить как 27B+MTP+margin0.3.
|
| 865 |
+
|
| 866 |
+
### СВОДНАЯ ТАБЛИЦА эффективных режимов (все цифры tok/s, temp 0)
|
| 867 |
+
|
| 868 |
+
**Qwen3.8-27B-UD (dense, дорогой verify ~169ms/шаг).**
|
| 869 |
+
| Режим | код | проза | примечание |
|
| 870 |
+
|---|---|---|---|
|
| 871 |
+
| AR (без спека) | ~13.2 | ~13.2 | база |
|
| 872 |
+
| DFlash2 n7 статика | 13.90 | 15.27 | хвост 4-7 мусор на reasoning |
|
| 873 |
+
| DFlash2 n7 + margin0.35 | **18.26** | **24.61** | лучшая авто-длина (+31%/+61%) |
|
| 874 |
+
| DFlash2 + --reasoning off | 27.69 | - | код без мышления, жёсткий n7 |
|
| 875 |
+
| MTP n3 (off/on) | 23.61 / 16.31 | - | вшит, дёшево; on меньше теряет |
|
| 876 |
+
| DFlash2 Count-500 | 40.41 | - | структурный текст (числа) |
|
| 877 |
+
|
| 878 |
+
**Qwen3.6-35B-A3B (MoE, 3B активных, дешёвый verify).**
|
| 879 |
+
| Режим | код | проза | vs AR код |
|
| 880 |
+
|---|---|---|---|
|
| 881 |
+
| AR (без спека) | 52.05 | 52.72 | 1.00x |
|
| 882 |
+
| DFlash v1 n7 + dyn0.35 | 55.79 | 46.51 | 1.07x |
|
| 883 |
+
| DFlash v1 n12 + dyn0.35 | 53.98 | - | 1.04x (блок 12 не окупается) |
|
| 884 |
+
| MTP вшит n3 | **68.37** | **57.61** | 1.31x — ЛУЧШИЙ на A3B |
|
| 885 |
+
| MTP n7 + margin0.3 | 63.60 | 52.94 | acceptance 0.793, но медленнее n3 |
|
| 886 |
+
| MTP n7 + p0.3 (декор) | 46.97 | - | p не режет |
|
| 887 |
+
|
| 888 |
+
### Итоговые рекомендации
|
| 889 |
+
- **A3B**: `--spec-type draft-mtp --spec-draft-n-max 3` (без p_min, без margin) =
|
| 890 |
+
код 68.4 / проза 57.6. verify дёшев, жёсткое короткое n3 — оптимум.
|
| 891 |
+
- **27B dense**: `--spec-type draft-dflash --spec-draft-n-max 7 --spec-draft-n-min 1
|
| 892 |
+
--spec-draft-p-min 0.35` = код 18.26 / проза 24.61 (margin-динамика, Test 13).
|
| 893 |
+
Без мышления: `--reasoning off` -> 27.7 на коде.
|
| 894 |
+
- **Куда копать дальше**: 27B + MTP + margin (тест не гонялся; у 27B дорогой
|
| 895 |
+
verify, длинная точная MTP-цепочка могла бы дать больше, чем жёсткое n3).
|
| 896 |
+
|
| 897 |
+
## Test 18 — 27B + MTP + margin 0.3 (reasoning ON) — margin-MTP впервые РАБОТАЕТ
|
| 898 |
+
|
| 899 |
+
Проверка единственного неиспытанного режима: 27B (дорогой verify) + MTP-n7 +
|
| 900 |
+
margin (вероятностный запас p0-p1, --spec-draft-p-min 0.3). Код LRU, temp 0,
|
| 901 |
+
reasoning ON (дефолт). Сравнение с Test 12/13 (тот же промт):
|
| 902 |
+
|
| 903 |
+
| Режим (27B, reasoning ON, код LRU) | acceptance | tok/s |
|
| 904 |
+
|---|---|---|
|
| 905 |
+
| MTP-n3 жёстко (Test 12) | 0.485 | 16.31 |
|
| 906 |
+
| **MTP-n7 + margin0.3 (Test 18)** | **0.592** | **17.81** |
|
| 907 |
+
| DFlash2-n7 статика (Test 13A) | 0.253 | 13.90 |
|
| 908 |
+
| DFlash2-n7 + margin0.35 (Test 13B) | 0.638 | **18.26** |
|
| 909 |
+
|
| 910 |
+
Замер: микробенч 512ток = 16.71 t/s (draft 464, accept 274/0.591); полный
|
| 911 |
+
4096ток = 17.81 t/s (draft 4259, accept 2522/0.592).
|
| 912 |
+
|
| 913 |
+
Вывод:
|
| 914 |
+
1. **margin-MTP впервые даёт реальный прирост на 27B**: +9.2% к жёсткому
|
| 915 |
+
MTP-n3 (17.81 vs 16.31), acceptance вырос 0.485 -> 0.592. Пр��чина: на A3B
|
| 916 |
+
MTP-голова не тянет дальше 2-3 и verify дёшев (нечего экономить), а на 27B
|
| 917 |
+
дорогой verify окупает длинную уверенную цепочку — margin режет «монетки»
|
| 918 |
+
и тянет где уверен. margin-MTP бессмыслен на A3B, но полезен на 27B.
|
| 919 |
+
2. НО DFlash2+margin0.35 (18.26) всё ещё впереди MTP+margin (17.81) на 2.5%:
|
| 920 |
+
DFlash2-селектор даёт более точную длинную цепочку, чем MTP-голова на коде.
|
| 921 |
+
3. Итог для 27B код (reasoning ON): best = DFlash2+margin0.35 (18.26),
|
| 922 |
+
второй = MTP+n7+margin0.3 (17.81), оба бьют жёсткое n3 (16.31).
|
| 923 |
+
|
| 924 |
+
Команда (27B + MTP + margin):
|
| 925 |
+
llama-server -m Qwen3.8-27B-UD-Q4_K_XL.gguf
|
| 926 |
+
--spec-type draft-mtp --spec-draft-n-max 7 --spec-draft-n-min 1 --spec-draft-p-min 0.3
|
| 927 |
+
(reasoning ON, без -md; margin из --spec-draft-p-min применяется к p0-p1
|
| 928 |
+
по коду speculative.cpp:1759-1774)
|
| 929 |
+
|
| 930 |
+
## Test 19 — Luce dflash_server + DDTree (прямой тест движка на P40)
|
| 931 |
+
|
| 932 |
+
Проверка самого Luce-движка (./server/build/dflash_server с --ddtree) на
|
| 933 |
+
твоих моделях, вместо разговоров о переносе. Обе модели = arch qwen35/qwen35moe
|
| 934 |
+
(принимаются gguf_target_loader). Обе на 40 P40. LRU-код, temp 0, thinking off.
|
| 935 |
+
|
| 936 |
+
### 19A. Dense 27B (Qwen3.8-27B-UD + DFlash2-lucebox) — РАБОТАЕТ, рекорд
|
| 937 |
+
Команда: dflash_server Qwen3.8-27B-UD-Q4_K_XL.gguf --draft Qwen3.8-27B-DFlash2-q8_0.gguf
|
| 938 |
+
--ddtree --ddtree-budget 22 --fa-window 2048 -ctk q8_0 -ctv q8_0 --max-ctx 8192
|
| 939 |
+
Драфт DFlash2-селектор сработал: [draft GGUF] DFlash 2 selector enabled rank=256 top_k=16.
|
| 940 |
+
LRU 512 ток: 19.48 t/s, avg_commit 5.33, acceptance 54.2%.
|
| 941 |
+
LRU 4096 ток: 21.46 t/s, avg_commit 5.97, acceptance 62.1%, 686 verify-steps.
|
| 942 |
+
|
| 943 |
+
| Режим (27B dense, код LRU 4096, thinking off) | tok/s | avg_commit/AL | acceptance |
|
| 944 |
+
|---|---|---|---|
|
| 945 |
+
| llama.cpp AR | ~13.2 | - | - |
|
| 946 |
+
| llama.cpp DFlash2 статика n7 | 13.90 | 2.77 | 0.253 |
|
| 947 |
+
| llama.cpp DFlash2 + margin0.35 | 18.26 | ~3.1 | 0.638 |
|
| 948 |
+
| **Luce DFlash2 + DDTree budget22** | **21.46** | **5.97** | **0.621** |
|
| 949 |
+
|
| 950 |
+
Вывод: Lucе DDTree на P40 даёт +17.5% к лучшему llama.cpp маржину (21.46 vs 18.26),
|
| 951 |
+
+63% к AR. Ключ — ДЕРЕВО: avg_commit 5.97 (почти 6 токенов/шаг) против ~3 у цепочки.
|
| 952 |
+
Подтверждает info.md: DDTree поднимает AL за счёт спасения ветвей (древовидного verify).
|
| 953 |
+
Prefill small: 43 ток за 1.6с (26 tok/s — kernel-launch dominated, не показательно).
|
| 954 |
+
|
| 955 |
+
### 19B. A3B MoE (Qwen3.6-35B-A3B + draft-a3b-q8_0) — НЕСОВМЕСТИМ, 0.7% acceptance
|
| 956 |
+
Команда: dflash_server Qwen_Qwen3.6-35B-A3B-Q4_K_M.gguf --draft draft-a3b-q8_0.gguf
|
| 957 |
+
--ddtree --ddtree-budget 22 ...
|
| 958 |
+
Лог: [draft] drafter target_layer_ids invalid (n=8, slots=5); keeping derived capture layers
|
| 959 |
+
LRU 512 ток: 11.05 t/s, avg_commit 1.12, acceptance 0.7% (54/7328) — спек сломан.
|
| 960 |
+
|
| 961 |
+
Вывод: наш конвертированный draft-a3b (DFlash v1, 8 target_layers, 6 dense-слоёв)
|
| 962 |
+
НЕ совместим с Lucе DDTree, который жёстко ждёт 5-слойный z-lab драфт (target_layer_ids=5).
|
| 963 |
+
Поэтому Lucе A3B даёт 11 t/s (почти AR без выигрыша). Для A3B лучший режим остаётся
|
| 964 |
+
llama.cpp MTP-n3 (68.37 код). НЕ переносить DDTree-сервер на A3B с этим драфтом.
|
| 965 |
+
|
| 966 |
+
### Итог Test 19
|
| 967 |
+
- DDTree-дерево реально выигрывает на dense 27B (+17.5% к маржину, AL ~6 vs ~3) —
|
| 968 |
+
это главный незадействованный потенциал. Стоит перенести ДЕРЕВО (ddtree.{h,cpp} +
|
| 969 |
+
tree-verify) в llama-dflash3-pr порт, а не standalone-сервер.
|
| 970 |
+
- На A3B Lucе бессмысленен (драфт несовместим); A3B = MTP-n3 на llama.cpp.
|
| 971 |
+
|
| 972 |
+
## Test 20 — Lucе DFlash2 CHAIN (без DDTree) — цепочка быстрее дерева на коде!
|
| 973 |
+
|
| 974 |
+
КОНТРОЛЬНЫЙ эксперимент: тот же Lucе 27B + DFlash2, но БЕЗ --ddtree (чистая
|
| 975 |
+
цепочка), чтобы отделить вклад дерева от вклада самого Lucе-движка. Код LRU,
|
| 976 |
+
temp 0, thinking off, 4096 (модель сама остановилась на 3219, finish=stop).
|
| 977 |
+
|
| 978 |
+
| Режим (Luce 27B, код LRU) | tok/s | avg_commit | acceptance | steps |
|
| 979 |
+
|---|---|---|---|---|
|
| 980 |
+
| Lucе DFlash2 CHAIN (no ddtree) | **32.02** | **6.09** | **0.761** | 529 |
|
| 981 |
+
| Lucе DFlash2 + DDTree budget22 | 21.46 | 5.97 | 0.621 | 686 |
|
| 982 |
+
| llama.cpp DFlash2 + margin0.35 (thinking ON) | 18.26 | ~3.1 | 0.638 | - |
|
| 983 |
+
|
| 984 |
+
ШОКИРУЮЩИЙ вывод: на КОДЕ (высоко-предсказуемом) Lucе CHAIN (32 t/s) в 1.5x
|
| 985 |
+
БЫСТРЕЕ дерева (21.46)! Причина: на структурном тексте цепочка длиной ~6 уже
|
| 986 |
+
почти вся принимается (acc 76%, avg_commit 6.09), а дерево тратит budget на
|
| 987 |
+
ветвления, которые не окупаются. Дерево выигрывает на НЕпредсказуемом тексте,
|
| 988 |
+
где ветвления спасают принятие; на коде это лишние вызовы.
|
| 989 |
+
|
| 990 |
+
Также Lucе chain (32) >> наш llama.cpp маржин (18.26): разрыв 1.7x. НО это
|
| 991 |
+
МИШАС сравнения: наш маржин был с thinking ON, Lucе — thinking off
|
| 992 |
+
(наш llama.cpp DFlash2 n7 + --reasoning off давал 27.69, Test 12). Нужен
|
| 993 |
+
apples-to-apples: наш DFlash2 chain с thinking OFF vs Lucе chain 32.
|
| 994 |
+
|
| 995 |
+
Lucе цепь уже умеет адаптивность через --verify-width (trimmed per step by
|
| 996 |
+
drafter confidence) - аналог нашего margin. --ddtree-tau - для дерева.
|
| 997 |
+
|
| 998 |
+
Итог: для кода лучший режим у Lucе - CHAIN (32 t/s), НЕ дерево. Дерево стоит
|
| 999 |
+
тестить на прозе/непредсказуемом, где acceptance ниже и ветвления окупаются.
|
| 1000 |
+
|
| 1001 |
+
## Test 21 — ЧЕСТНОЕ сравнение: наш llama.cpp DFlash2 margin vs Lucе (thinking OFF)
|
| 1002 |
+
|
| 1003 |
+
Исправление мишаса Test 20: наш маржин был с thinking ON (18.26), Lucе - off.
|
| 1004 |
+
Прогон нашего llama.cpp DFlash2 + margin 0.35 + --reasoning off на том же
|
| 1005 |
+
коде-промпте (модель сама останавливается: у нас 2405 ток., у Lucе 3219; тот
|
| 1006 |
+
же промпт, скорость валидна на 2-3K ток.).
|
| 1007 |
+
|
| 1008 |
+
| Режим (27B, код LRU, thinking OFF) | tok/s | acceptance | steps |
|
| 1009 |
+
|---|---|---|---|
|
| 1010 |
+
| **наш llama.cpp DFlash2 + margin0.35** | **29.86** | **0.850** | - |
|
| 1011 |
+
| Lucе DFlash2 chain | 32.02 | 0.761 | 529 |
|
| 1012 |
+
| Lucе DFlash2 + DDTree budget22 | 21.46 | 0.621 | 686 |
|
| 1013 |
+
|
| 1014 |
+
ГЛАВНЫЙ ВЫВОД: разрыв на самом деле НЕ 1.7x, а всего ~7%! Наш llama.cpp маржин
|
| 1015 |
+
(29.86) почти догнал Lucе chain (32.02), и наш acceptance даже выше (0.85 vs
|
| 1016 |
+
0.76). Прежние 18.26 были с thinking ON - нечестная база. При thinking OFF
|
| 1017 |
+
наш маржин - 29.86, на уровне Lucе.
|
| 1018 |
+
|
| 1019 |
+
Разрыв 7% объясняется НЕ деревом, а имплементацией Lucе (fast-rollback +
|
| 1020 |
+
fused-граф verify + --verify-width confidence-trim), а не качеством драфта.
|
| 1021 |
+
DTree же на коде не помогает (не окупает ветвления), и даже медленнее.
|
| 1022 |
+
|
| 1023 |
+
Дерево стоит тестить на прозе/непредсказуемом тексте, где acceptance низкий и
|
| 1024 |
+
ветвления реально спасают. На коде (структура) цепочка эффективнее.
|
| 1025 |
+
|
| 1026 |
+
Далее: проверить DTree на прозе (где он теоретически выигрывает), и/или выжать
|
| 1027 |
+
оставшиеся 7% до Lucе (verify-width аналог, fast-rollback в наш порт).
|
| 1028 |
+
|
| 1029 |
+
## Test 22 — DTree на прозе НЕ выигрывает (и Lucе в целом проигрывает нашему маржину)
|
| 1030 |
+
|
| 1031 |
+
Проверка гипотезы из Test 20/21: дерево должно спасать на непредсказуемом
|
| 1032 |
+
тексте (проза), где acceptance низкий. Lucе 27B + DDTree budget22, проза 1024,
|
| 1033 |
+
thinking off:
|
| 1034 |
+
|
| 1035 |
+
| Режим (27B, проза 1024, thinking OFF) | tok/s | avg_commit | acceptance |
|
| 1036 |
+
|---|---|---|---|
|
| 1037 |
+
| Lucе DFlash2 chain | 11.97 | 3.29 | 0.287 |
|
| 1038 |
+
| Lucе DFlash2 + DDTree budget22 | 11.98 | 3.29 | 0.287 |
|
| 1039 |
+
| (сравнение) наш llama.cpp DFlash2 mar gin0.35, thinking ON (Test 13C) | 24.61 | 4.07 | 0.769 |
|
| 1040 |
+
|
| 1041 |
+
ВЫВОД: на прозе Lucе DDTree и chain ИДЕНТИЧНЫ (~12 t/s, avg_commit 3.29,
|
| 1042 |
+
acceptance 28.7%) - дерево НЕ спасает. Причина: на свободном тексте acceptance
|
| 1043 |
+
этого DFlash2-драфта низкий на всех ветвях (28%), ветвления не окупают
|
| 1044 |
+
доп. декод. Lucе в целом на прозе (12 t/s) сильно проигрывает нашему llama.cpp
|
| 1045 |
+
маржину (24.61) - 2x.
|
| 1046 |
+
|
| 1047 |
+
Итоговый вывод по всей линейке (Test 19-22):
|
| 1048 |
+
- НАШ llama.cpp DFlash2 + margin 0.35 - самый универсальный и сильный:
|
| 1049 |
+
код 29.86 (off) / 18.26 (on), проза 24.61 (on). Лучшая авто-длина.
|
| 1050 |
+
- Lucе chain: код 32.02 (off) - на 7% быстрее нашего, но проза 11.97 (off) -
|
| 1051 |
+
в 2x хуже нашего маржина.
|
| 1052 |
+
- Lucе DDTree: НЕ даёт выигрыша ни на коде (21.46 < chain 32), ни на прозе
|
| 1053 |
+
(11.98 = chain). Дерево для этого драфта/текстов неэффективно.
|
| 1054 |
+
- margin+дtree: бессмысленно - дерево само по себе не помогает нигде;
|
| 1055 |
+
наш margin (тop1-top2 в логах селектора) сильнее, чем ddtree-tau Lucе
|
| 1056 |
+
(кумулятивный log-prob) для этого DFlash2.
|
| 1057 |
+
|
| 1058 |
+
Финал: остаётся выжать ~7% до Lucе chain на коде (fast-rollback / fused-verify)
|
| 1059 |
+
ИЛИ распространить наш маржин-подход на более длинные цепочки. Дерево не нужно.
|
| 1060 |
+
|
| 1061 |
+
## Test 23 — ЧЕСТНАЯ проза (thinking OFF): наш маржин обгоняет Lucе
|
| 1062 |
+
|
| 1063 |
+
Устранение последнего мишаса: наш 24.61 на прозе был с thinking ON, а Lucе -
|
| 1064 |
+
с off (несопоставимо). Прогон нашего llama.cpp DFlash2 + margin 0.35, --reasoning
|
| 1065 |
+
off, тот же эссе-промпт, 1024 ток.
|
| 1066 |
+
|
| 1067 |
+
| Режим (27B, проза 1024, thinking OFF) | tok/s | acceptance |
|
| 1068 |
+
|---|---|---|
|
| 1069 |
+
| **наш llama.cpp DFlash2 + margin0.35** | **16.38** | **0.505** |
|
| 1070 |
+
| Lucе DFlash2 chain | 11.97 | 0.287 |
|
| 1071 |
+
| Lucе DFlash2 + DDTree | 11.98 | 0.287 |
|
| 1072 |
+
|
| 1073 |
+
Вывод: наш llama.cpp маржин на прозе (16.38) в 1.37x быстрее Lucе (11.97)!
|
| 1074 |
+
Наш acceptance тоже выше (0.505 vs 0.287). margin работает на прозе: режет
|
| 1075 |
+
мусорный хвост длинной цепочки, чего у Lucе нет (ucе уперся в 12 t/s).
|
| 1076 |
+
|
| 1077 |
+
ОТВЕТ НА "ПОЧЕМУ МЫ НЕ ДОХОДИЛИ ДО 30":
|
| 1078 |
+
Мы ДОХОДИЛИ на коде! Наш DFlash2 margin = 29.86 (thinking OFF), просто раньше
|
| 1079 |
+
мерили с thinking ON (18.26) - дефолт. С --reasoning off наш порт даёт 29.86,
|
| 1080 |
+
почти как Lucе chain 32. Так что 30+ НЕ из-за Lucе-движка, а из-за отсутствия
|
| 1081 |
+
reasoning текста в промпте.
|
| 1082 |
+
|
| 1083 |
+
ГДЕ Lucе-движок реально быстрее: только на коде +7% (32 vs 29.86) - цена
|
| 1084 |
+
fast-rollback + fused-verify + verify-width. На прозе наш маржин сильнее
|
| 1085 |
+
(16.38 vs 11.97) - Lucе-цепочка не умеет резать хвост как наш margin.
|
| 1086 |
+
|
| 1087 |
+
Полная честная картина (thinking OFF):
|
| 1088 |
+
| | код | проза |
|
| 1089 |
+
|---|---|---|
|
| 1090 |
+
| наш llama.cpp DFlash2 + margin0.35 | **29.86** | **16.38** |
|
| 1091 |
+
| Lucе chain | 32.02 | 11.97 |
|
| 1092 |
+
| Lucе DDTree | 21.46 | 11.98 |
|
| 1093 |
+
|
| 1094 |
+
Наш маржин универсальнее: код почти паритет (7% слабее), проза на 37% сильнее.
|
| 1095 |
+
|
| 1096 |
+
## Test 24 — ⚠️ ОШИБКА ИСПРАВЛЕНА: Lucе "thinking ON" был БЕЗ мышления
|
| 1097 |
+
|
| 1098 |
+
Ранняя версия этого теста утверждала, что Lucе на 69% быстрее на reasoning.
|
| 1099 |
+
Это было НЕВЕРНО: мой запрос к Lucе с "thinking ON" на самом деле дал
|
| 1100 |
+
reasoning_tokens=0 (Lucе не включил мышление по умолчанию), т.е. это был тот
|
| 1101 |
+
же Lucе-БЕЗ-мышления, что и 32 t/s — просто короткая порция (1024 ток).
|
| 1102 |
+
|
| 1103 |
+
Честный тест: Lucе с явным "thinking":{"type":"enabled"}:
|
| 1104 |
+
[budget-hook] spec-decode close at committed=41/1024 (remaining=1024 <= hard_limit=4096)
|
| 1105 |
+
reasoning_tokens=1, spec_decode_ran=True, decode 13.6 t/s (~AR)
|
| 1106 |
+
-> Lucе при реальном мышлении СПЕК УБИВАЕТСЯ budget-hook'ом (как в Test 4):
|
| 1107 |
+
force-close на раннем шаге, падает в AR (13.3 t/s).
|
| 1108 |
+
|
| 1109 |
+
ЧЕСТНАЯ КАРТИНА (27B код, реальное мышление):
|
| 1110 |
+
| Режим | реально думал? | tok/s |
|
| 1111 |
+
|---|---|---|
|
| 1112 |
+
| наш llama.cpp DFlash2 + margin0.35 | ДА (reasoning_tokens>0) | 18.26 (4096) / 17.28 (1024) |
|
| 1113 |
+
| Lucе chain "thinking ON" (мой ошибочный) | НЕТ (reasoning_tokens=0) | 29.14 (это БЕЗ мышления) |
|
| 1114 |
+
| Lucе chain реально thinking enabled | бюджет-хук убил спек | 13.3 (~AR) |
|
| 1115 |
+
|
| 1116 |
+
ГЛАВНЫЙ ВЫВОД: наш llama.cpp с реальным мышлением (18.26) БЫСТРЕЕ Lucе с
|
| 1117 |
+
реальным мышлением (13.3)! Прежний "разрыв 1.7x" в пользу Lucе был артефактом
|
| 1118 |
+
моей ошибки (сравнил Lucе-без-мышления с нашим-с-мышлением).
|
| 1119 |
+
|
| 1120 |
+
resoning-текст ≈ проза по характеру (непредсказуем): наш маржин держит на нём
|
| 1121 |
+
нормально, а Lucе там вообще проигрывает (спек убит бюджетом). Переносить
|
| 1122 |
+
verify-width на основе этого теста НЕ нужно — мотивации больше нет.
|
| 1123 |
+
|
| 1124 |
+
(Сохраняю модель arch: Qwen3.8-27B-UD = qwen35 ГИБРИД с Gated DeltaNet
|
| 1125 |
+
(qwen35.ssm.* веса, build_inp_mem_hybrid) - как Qwen3.5-27B из info.md.
|
| 1126 |
+
Значит persist-ядро DeltaNet и fast-rollback ПРИМЕНИМЫ к ней.)
|
| 1127 |
+
|
| 1128 |
+
## Test 25 — КВАНТОВАНИЕ ДРАФТА: Q4-mix ЛУЧШЕ Q8, Q2 чуть медленнее
|
| 1129 |
+
(код 1024, reasoning OFF, DFlash2 margin 0.35, наш порт llama.cpp, P40)
|
| 1130 |
+
|
| 1131 |
+
Проблема: z-lab Q2_K GGUF (arch=dflash, маппинг mask_token через канон)
|
| 1132 |
+
не инициализируется у нас -> "invalid or missing mask_token_id", спек НЕ
|
| 1133 |
+
работает, чистый AR 13.13 t/s. Это ошибка ФОРМАТА (z-lab отличается от
|
| 1134 |
+
нашего qwen35-dflash-draft), не квантования.
|
| 1135 |
+
|
| 1136 |
+
Решение: сквантовали ИЗ НАШЕГО f16 (convert_dflash_to_gguf.py из
|
| 1137 |
+
Qwen3.8-27B-DFlash2-src/model.safetensors) -> llama-quantize Q2_K (наш
|
| 1138 |
+
формат, mask корректен). И quantize_dflash_draft.py --scheme q4-mix.
|
| 1139 |
+
|
| 1140 |
+
ПОЧЕМУ не через quantize_dflash_draft.py --scheme q2-k: в python-gguf
|
| 1141 |
+
(gguf.quants) реализовано КВАНТОВАНИЕ только для Q4_0/Q4_1/Q5_0/Q5_1/Q8_0/
|
| 1142 |
+
BF16; для K-quants (Q2_K/Q3_K/Q4_K) есть ТОЛЬКО dequantize_blocks (чтение),
|
| 1143 |
+
quantize_blocks бросит NotImplementedError. Q2 доступен лишь через
|
| 1144 |
+
llama-quantize (C++). q4-mix работает потому что Q4_0 реализован в python.
|
| 1145 |
+
|
| 1146 |
+
Результаты (код 1024, our DFlash2 margin 0.35, reasoning off):
|
| 1147 |
+
| Драфт | Размер | tok/s | acceptance | mean len |
|
| 1148 |
+
|-----------------|--------|-------|------------|----------|
|
| 1149 |
+
| Q8 (lucebox) | 2.0 GB | ~29-30| 0.85 | 5.27 |
|
| 1150 |
+
| Q4-mix (self) | 1.2 GB | 30.26 | 0.827 | 5.25 |
|
| 1151 |
+
| Q2_K (self) | 0.87GB | 27.08 | 0.763 | 4.69 |
|
| 1152 |
+
| Q2 z-lab (broken)| 0.7 GB| 13.13 | (спек off) | - |
|
| 1153 |
+
|
| 1154 |
+
ВЫВОД: q4-mix = ЛУЧШИЙ (30.26 t/s > Q8, acceptance 0.827, 1.2GB). Q2_K
|
| 1155 |
+
приемлем (27 t/s, 865MB) только если критична VRAM. Q8 и Q4-mix почти не
|
| 1156 |
+
различаются по acceptance; Q2 теряет ~10% скорости из-за Q2 на head
|
| 1157 |
+
селектора. Для P40-скорости юзаем q4-mix.
|
| 1158 |
+
|
| 1159 |
+
## Test 26 — n_max SWEEP + DFlash2 блочная генерация (ВЫВОДЫ)
|
| 1160 |
+
q4mix-драфт, код 1024, reasoning OFF, DFlash2 margin p-min 0.35.
|
| 1161 |
+
|
| 1162 |
+
n_max свип (tok/s):
|
| 1163 |
+
| n_max | tok/s | acceptance | mean |
|
| 1164 |
+
|-------|-------|------------|------|
|
| 1165 |
+
| 4 | 27.77 | 0.861 | 4.04 |
|
| 1166 |
+
| 5 | 27.41 | - | - |
|
| 1167 |
+
| 7 | 30.26 | 0.827 | 5.25 |
|
| 1168 |
+
| 8 | 30.18 | 0.827 | 5.25 |
|
| 1169 |
+
-> ОПТИМУМ n_max=7 (30.26). Уменьшение НЕ помогает (теряем длинные
|
| 1170 |
+
цепочки), 8 не лучше (маржин и так режет на ~5.25, блок стоит дорого).
|
| 1171 |
+
|
| 1172 |
+
ВАЖНАЯ ПРАВКА (мой прошлый ошибка): наш DFlash2 НЕ последовательный.
|
| 1173 |
+
draft_dflash::draft() (speculative.cpp 1198+) строит ОДИН noise-блок
|
| 1174 |
+
(id_last + n_max масок) и ДЕКОДИТ ЕГО ОДНИМ llama_decode (1233) - это
|
| 1175 |
+
блочная диффузия, как PR 27342 "decode every sequence's full noise block
|
| 1176 |
+
in one pass". Квадратичный chain_heads-цикл (1710) принадлежит MTP, НЕ
|
| 1177 |
+
DFlash. Маржин обрезает ПРИНЯТУЮ часть ПОСЛЕ декода блока (по lattice,
|
| 1178 |
+
1311-1316) - нельзя "не генерить лишние": блочная диффузия даёт уверены
|
| 1179 |
+
fnость позиции i только после декода всего блока (state i зависит от
|
| 1180 |
+
масок 0..i-1). Стоимость draft = стоимость декода n_max+1-блока, не
|
| 1181 |
+
зависит от маржина.
|
| 1182 |
+
|
| 1183 |
+
bottleneck: draft-decode 86% (4199ms) vs verify 14% (657ms) на коде 1024.
|
Qwen3.8-27B-DFlash2-q4mix-self.gguf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:39eb9823453c9f14816e4c14fe60eb18e6cbcdf5f5205affbb53500d1049ccc2
|
| 3 |
+
size 1213164576
|
README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# llama.cpp DFlash2 P40 (Pascal) optimization
|
| 2 |
+
|
| 3 |
+
Speculative decoding (DFlash2 block-diffusion drafter) tuned specifically for
|
| 4 |
+
**NVIDIA Tesla P40** (Pascal, compute capability 6.1, no FP16 tensor cores).
|
| 5 |
+
|
| 6 |
+
Fork of `ggml-org/llama.cpp` (PR #27342 lineage) with DFlash2 support, optimized
|
| 7 |
+
for the Pascal backend: int8 DP4A (not FP16), multi-stage quantization of the
|
| 8 |
+
draft, and an adaptive margin that shortens the verify batch where the
|
| 9 |
+
draft's selector is uncertain.
|
| 10 |
+
|
| 11 |
+
```
|
| 12 |
+
Mini-article: Title placeholder -> 30.26 tok/s on Pascal P40 with 27B dense
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
## What this repo is
|
| 18 |
+
|
| 19 |
+
A runnable, measured build of llama.cpp + DFlash2 speculative decoding on a
|
| 20 |
+
single Tesla P40 (24 GB, sm_61). Everything in this repo's `DFLASH2_RING_PORT.md`
|
| 21 |
+
is a real measurement on the P40; no other GPUs / cloud numbers are claimed.
|
| 22 |
+
|
| 23 |
+
Primary pairing tested:
|
| 24 |
+
- **Target:** Qwen3.8-27B-UD `Qwen3.8-27B-UD-Q4_K_XL.gguf` (qwen35 hybrid, Gated DeltaNet)
|
| 25 |
+
- **Draft:** DFlash2 `Qwen3.8-27B-DFlash2-q4mix-self.gguf` (our q4-mix quant)
|
| 26 |
+
|
| 27 |
+
---
|
| 28 |
+
|
| 29 |
+
## Best command (hit it)
|
| 30 |
+
|
| 31 |
+
See `run_best.sh`. Summary:
|
| 32 |
+
|
| 33 |
+
```bash
|
| 34 |
+
./build-p40-ring/bin/llama-server \
|
| 35 |
+
-m Qwen3.8-27B-UD-Q4_K_XL.gguf \
|
| 36 |
+
-md Qwen3.8-27B-DFlash2-q4mix-self.gguf \
|
| 37 |
+
-ngl 999 -ngld 999 -c 8192 -b 512 -ub 512 -np 1 \
|
| 38 |
+
--load-mode mlock --cache-ram 32768 --checkpoint-min-step 512 \
|
| 39 |
+
-fa 1 -ctk q8_0 -ctv q8_0 -ctkd q8_0 -ctvd q8_0 --kv-unified \
|
| 40 |
+
--spec-type draft-dflash \
|
| 41 |
+
--spec-draft-n-max 7 --spec-draft-n-min 1 --spec-draft-p-min 0.35 \
|
| 42 |
+
--spec-draft-ctx 0 --temp 0 --jinja --reasoning off -bs \
|
| 43 |
+
-lv 4 --host 0.0.0.0 --port 8080
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
Key flags:
|
| 47 |
+
- `--spec-type draft-dflash` - DFlash2 block-diffusion drafter
|
| 48 |
+
- `--spec-draft-n-max 7` - sweep optimum (Test 26: 7=30.26 > 8=30.18 > 5=27.4 > 4=27.8)
|
| 49 |
+
- `--spec-draft-p-min 0.35` - adaptive margin (top1-top2 on raw selector logits),
|
| 50 |
+
cuts the chain where the selector is a coin flip. Shortens the verify batch.
|
| 51 |
+
- `--spec-draft-n-min 1` - keep short chains alive
|
| 52 |
+
- `-bs` - backend (GPU) argmax sampling, no CPU logit readback
|
| 53 |
+
- `-fa 1`, `-ctk/ctv q8_0`, `--kv-unified` - flash attention + q8 KV
|
| 54 |
+
- `--reasoning off` - fastest mode; use `on` for real reasoning (see below)
|
| 55 |
+
|
| 56 |
+
To switch to the reasoning scenario replace `--reasoning off` with `--reasoning on`.
|
| 57 |
+
|
| 58 |
+
---
|
| 59 |
+
|
| 60 |
+
## Measured results (Tesla P40, single GPU)
|
| 61 |
+
|
| 62 |
+
All numbers are our own runs, logged in `DFLASH2_RING_PORT.md`.
|
| 63 |
+
|
| 64 |
+
### Code prompt, reasoning OFF, greedy
|
| 65 |
+
|
| 66 |
+
| Draft quant | size | tok/s | acceptance | mean len |
|
| 67 |
+
|---|---|---|---|---|
|
| 68 |
+
| **q4-mix (recommended)** | 1.2 GB | **30.26** | 0.83 | 5.25 |
|
| 69 |
+
| Q8 (lucebox) | 2.0 GB | ~30 | 0.85 | 5.27 |
|
| 70 |
+
| Q2-hybrid (fc Q2, head) | 0.96GB | 29.0 | 0.80 | 5.17 |
|
| 71 |
+
| Q2-all | 0.87GB | 27.1 | 0.76 | 4.69 |
|
| 72 |
+
|
| 73 |
+
Draft quant is q4-mix = backbone q4_0 + dflash.* heads q8_0 (protects the
|
| 74 |
+
selector). q4-mix wins: same acceptance as Q8 with a smaller, faster draft;
|
| 75 |
+
Q2 is slower because the 2-bit selector heads lose near-tie accuracy.
|
| 76 |
+
|
| 77 |
+
### n_max sweep (q4-mix, code, reasoning OFF)
|
| 78 |
+
|
| 79 |
+
| n_max | tok/s |
|
| 80 |
+
|---|---|
|
| 81 |
+
| 4 | 27.8 |
|
| 82 |
+
| 5 | 27.4 |
|
| 83 |
+
| **7** | **30.26** |
|
| 84 |
+
| 8 | 30.18 |
|
| 85 |
+
|
| 86 |
+
n_max=7 is the optimum; adaptive margin already caps the useful chain at ~5.25.
|
| 87 |
+
|
| 88 |
+
### Thinking scenario (code, reasoning ON, greedy)
|
| 89 |
+
|
| 90 |
+
- ours (DFlash2 q4-mix + margin): **~18 t/s** with real reasoning tokens.
|
| 91 |
+
- Luce chain with real thinking: 13.3 t/s (their spec is killed by a budget hook),
|
| 92 |
+
i.e. our port is **faster than Luce on the reasoning scenario**.
|
| 93 |
+
|
| 94 |
+
### Reference (same 27B, code, reasoning OFF)
|
| 95 |
+
|
| 96 |
+
- our DFlash2 q4-mix + margin: **30.26**; plain Luce chain: ~32; ngram-mod /
|
| 97 |
+
ngram-map-k4v on top of DFlash2: **28.2** (drags us down, not used).
|
| 98 |
+
|
| 99 |
+
---
|
| 100 |
+
|
| 101 |
+
## What was done / optimizations (P40 Pascal)
|
| 102 |
+
|
| 103 |
+
1. **MMQ DP4A (int8), no FP16.**
|
| 104 |
+
Pascal has no FP16 tensor cores. `ggml-cuda` selects the MMQ DP4A kernel
|
| 105 |
+
(`__dp4a`, cc >= 610) for quantized weights via
|
| 106 |
+
`(!fp16_mma_hardware_available(cc) || ...)`, compile arch `61`. Draft and
|
| 107 |
+
target both run the int8 matmul path. No FP16 path is used for quant weights.
|
| 108 |
+
2. **Draft quantization pipeline** (`server/scripts/quantize_dflash_draft.py`
|
| 109 |
+
+ `llama-quantize`):
|
| 110 |
+
- `q4-mix`: backbone q4_0, `dflash.*` heads + conv q8_0 -> 1.2 GB, acceptance-neutral.
|
| 111 |
+
- Q2 available only via `llama-quantize` (python-gguf has no K-quant *write*).
|
| 112 |
+
3. **DFlash2 adaptive chain (margin)** (`common/speculative.cpp`):
|
| 113 |
+
raw selector logits top1-top2 margin vs a threshold from `p_min`; stops the
|
| 114 |
+
chain when the pick is a coin flip. This is what shortens the verify batch
|
| 115 |
+
and makes n_max=7 optimal.
|
| 116 |
+
4. **DFlash2 is block-diffusion**: builds one noise block (id_last + n_max masks)
|
| 117 |
+
and decodes it in a **single pass** (`draft_dflash::draft`, one
|
| 118 |
+
`llama_decode`) - same approach as upstream PR #27342.
|
| 119 |
+
5. **Backend (GPU) argmax** (`-bs`): no CPU-side logit readback during draft/verify.
|
| 120 |
+
6. **Flash attention** (`-fa`) + `q8_0` KV for both models, unified KV.
|
| 121 |
+
7. **Duplicate draft quant tooling** and honest per-scenario comparison vs
|
| 122 |
+
Luce (see DFLASH2_RING_PORT.md).
|
| 123 |
+
|
| 124 |
+
---
|
| 125 |
+
|
| 126 |
+
## HF model
|
| 127 |
+
|
| 128 |
+
Our recommended draft (q4-mix, 1.2 GB):
|
| 129 |
+
|
| 130 |
+
- Filename: `Qwen3.8-27B-DFlash2-q4mix-self.gguf`
|
| 131 |
+
- HF repo: **https://huggingface.co/maxwelhelp/llama.cpp-DFlash2-pascal6-optimized**
|
| 132 |
+
|
| 133 |
+
Target: `Qwen3.8-27B-UD-Q4_K_XL.gguf` (Qwen3.8-27B-UD, qwen35 hybrid).
|
| 134 |
+
|
| 135 |
+
---
|
| 136 |
+
|
| 137 |
+
## Building for P40
|
| 138 |
+
|
| 139 |
+
```bash
|
| 140 |
+
cmake -B build-p40-ring -DCMAKE_CUDA_ARCHITECTURES=61 -DLLAMA_CUDA=ON -DGGML_CUDA=ON -DLLAMA_CURL=ON
|
| 141 |
+
cmake --build build-p40-ring --config Release -j
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
---
|
| 145 |
+
|
| 146 |
+
## Notes / honesty
|
| 147 |
+
|
| 148 |
+
- No cloud numbers; all measurements are local on the P40 (see the log).
|
| 149 |
+
- ngram-mod / ngram-map-k4v did **not** help on top of DFlash2 and are not used.
|
| 150 |
+
- On the reasoning scenario our port already beats Luce; q4-mix + margin is the
|
| 151 |
+
recommended release config.
|
| 152 |
+
|
| 153 |
+
---
|
| 154 |
+
License note: this is a private fork for experimental Pascal tuning; it wraps
|
| 155 |
+
upstream llama.cpp (MIT) and DFlash2 draft weights (Apache-2.0 / z-lab).
|
run_best.sh
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# =============================================================================
|
| 3 |
+
# run_best.sh - DFlash2 + llama.cpp speculative decoding on Tesla P40 (Pascal)
|
| 4 |
+
#
|
| 5 |
+
# BEST configuration measured across the whole test series (DFLASH2_RING_PORT.md).
|
| 6 |
+
# Target: Qwen3.8-27B-UD (Q4_K_XL), Draft: DFlash2 q4-mix self-quant GGUF.
|
| 7 |
+
#
|
| 8 |
+
# Results (code 1024, reasoning OFF, greedy):
|
| 9 |
+
# q4mix draft + DFlash2 + n_max=7 + adaptive margin p_min=0.35 -> 30.26 tok/s
|
| 10 |
+
# (acceptance 0.83, mean len 5.25)
|
| 11 |
+
#
|
| 12 |
+
# Why this is the optimum (see DFLASH2_RING_PORT.md Tests 13-26):
|
| 13 |
+
# - q4-mix draft quant: 30.26 > Q8 (~30) > q2h8-hybrid (29) > Q2-all (27).
|
| 14 |
+
# - n_max=7 is the sweep optimum: 7=30.26 > 8=30.18 > 5=27.4 > 4=27.8.
|
| 15 |
+
# - adaptive margin (p_min=0.35) cuts the chain where the selector is
|
| 16 |
+
# uncertain (top1-top2 log-margin), giving short verify batches.
|
| 17 |
+
# - P40 hits are: MMQ DP4A (int8, no FP16 tensor cores), -bs backend argmax,
|
| 18 |
+
# FA flash-attn, q8_0 KV.
|
| 19 |
+
# =============================================================================
|
| 20 |
+
set -euo pipefail
|
| 21 |
+
|
| 22 |
+
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 23 |
+
MODELS=/home/maxwelhelp/models
|
| 24 |
+
TARGET="$MODELS/Qwen3.8-27B-UD-Q4_K_XL.gguf"
|
| 25 |
+
DRAFT="$MODELS/Qwen3.8-27B-DFlash2-q4mix-self.gguf"
|
| 26 |
+
PORT="${PORT:-8080}"
|
| 27 |
+
|
| 28 |
+
exec "$REPO/build-p40-ring/bin/llama-server" \
|
| 29 |
+
-m "$TARGET" \
|
| 30 |
+
-md "$DRAFT" \
|
| 31 |
+
-ngl 999 -ngld 999 -c 8192 -b 512 -ub 512 -np 1 \
|
| 32 |
+
--load-mode mlock --cache-ram 32768 --checkpoint-min-step 512 \
|
| 33 |
+
-fa 1 -ctk q8_0 -ctv q8_0 -ctkd q8_0 -ctvd q8_0 --kv-unified \
|
| 34 |
+
--spec-type draft-dflash \
|
| 35 |
+
--spec-draft-n-max 7 --spec-draft-n-min 1 --spec-draft-p-min 0.35 \
|
| 36 |
+
--spec-draft-ctx 0 --temp 0 --jinja --reasoning off -bs \
|
| 37 |
+
-lv 4 --host 0.0.0.0 --port "$PORT"
|
scripts/convert_dflash_to_gguf.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c554274151c882d6f725568603ff0520f2f5e7f2d88b2f3abd837f47a42789ac
|
| 3 |
+
size 29840
|
scripts/quantize_dflash_draft.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2fe29182ac9b82b5e8d6dabe66f94a2cc269531088b43e5debe9322a0eb67be5
|
| 3 |
+
size 4683
|