mede CPU (o veredito se INVERTE: Q4_K_M ganha 1,46-1,51x) e documenta `sasori draw`
Browse files
README.md
CHANGED
|
@@ -92,6 +92,44 @@ decode da VAE). Os três braços rodam **no mesmo pod, ao mesmo tempo**, cada um
|
|
| 92 |
> permanente. Com o modelo carregado uma vez e 5 repetições, a comparação correta é a tabela acima:
|
| 93 |
> **empate com o FP16**, e vantagem sobre o `Q4_K_M`. O número anterior estava errado e foi retirado.
|
| 94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
### Fidelidade — CLIP-score pareado (ViT-L/14, N=16 pares: 8 prompts × 2 seeds)
|
| 96 |
|
| 97 |
| braço | CLIP-score | retenção vs FP16 | Wilcoxon p | placar par-a-par (FP/braço) |
|
|
@@ -121,11 +159,12 @@ rubber ducks" sai correta e o fotorrealismo se mantém; o que muda é a trajetó
|
|
| 121 |
degradação.
|
| 122 |
- **CLIP-score não é FID.** Mede alinhamento imagem-texto pareado, não fidelidade perceptual da
|
| 123 |
distribuição. Nenhum FID de Fréchet foi computado.
|
| 124 |
-
- **Escopo da medição:** 1 modelo,
|
| 125 |
-
|
| 126 |
-
8 prompts, 1 CLIP (ViT-L/14), N=16
|
| 127 |
-
|
| 128 |
-
interno — mas não foi avaliada
|
|
|
|
| 129 |
|
| 130 |
## Como rodar
|
| 131 |
|
|
@@ -158,13 +197,51 @@ stable-diffusion.cpp/build/bin/sd-cli \
|
|
| 158 |
Use a flag só se o modelo não couber na sua GPU.
|
| 159 |
- **VRAM necessária** (TQ3P, 1024², sem offload): **14,7 GB** com o text encoder também em TQ3P (7,1 GB o DiT + 7,5 GB o TE + 0,1 GB a VAE), ou 22,9 GB mantendo o text encoder em BF16.
|
| 160 |
- **CPU**: o kernel TQ{K}P usa `_mm256_maddubs_epi16` (AVX2) e é um mpGEMM direto — o trit
|
| 161 |
-
multiplica sem dequantizar. Passe `-t <núcleos>`. **
|
| 162 |
-
|
| 163 |
-
atrás de ambos — a vantagem de ~1,5× sobre o `Q4_K_M` acima é de **GPU**, não extrapole.
|
| 164 |
- **Servir com o modelo quente:** `sd-server` mantém os pesos carregados; via `sd-cli` cada
|
| 165 |
imagem paga load + setup outra vez (o que também distorce medição de velocidade — veja a
|
| 166 |
nota de correção acima). Para uso repetido, use o servidor.
|
| 167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
### Ternarizar você mesmo (e o que aprendi sobre paralelizar isso)
|
| 169 |
|
| 170 |
```bash
|
|
|
|
| 92 |
> permanente. Com o modelo carregado uma vez e 5 repetições, a comparação correta é a tabela acima:
|
| 93 |
> **empate com o FP16**, e vantagem sobre o `Q4_K_M`. O número anterior estava errado e foi retirado.
|
| 94 |
|
| 95 |
+
### Velocidade em CPU — o veredito se INVERTE
|
| 96 |
+
|
| 97 |
+
EPYC 4564P (16 vCPU, AVX2/AVX512), 124 GB de RAM (folgada de propósito: mede o **teto**, sem
|
| 98 |
+
`--mmap` nem releitura de disco), 4 steps, `-t 16`, executado via `sasori draw`.
|
| 99 |
+
|
| 100 |
+
**512²**
|
| 101 |
+
|
| 102 |
+
| braço | encode do prompt | sampling | **s/it** | VAE decode | total por imagem |
|
| 103 |
+
|---|---|---|---|---|---|
|
| 104 |
+
| TQ3P (DiT K3 + TE K3) | 17,51 s | 259,43 s | **64,86** | 9,43 s | ~4,8 min |
|
| 105 |
+
| Q4_K_M + Q8_0 | 12,81 s | 171,32 s | **42,83** | 9,46 s | ~3,2 min |
|
| 106 |
+
|
| 107 |
+
**1024²**
|
| 108 |
+
|
| 109 |
+
| braço | encode do prompt | sampling | **s/it** | VAE decode | total por imagem |
|
| 110 |
+
|---|---|---|---|---|---|
|
| 111 |
+
| TQ3P (DiT K3 + TE K3) | 17,19 s | 833,03 s | **208,26** | 35,89 s | ~14,8 min |
|
| 112 |
+
| Q4_K_M + Q8_0 | 12,17 s | 570,16 s | **142,54** | 36,90 s | ~10,3 min |
|
| 113 |
+
|
| 114 |
+
**Em CPU o `Q4_K_M` é 1,51× (512²) / 1,46× (1024²) MAIS RÁPIDO que o TQ3P — o inverso quase exato
|
| 115 |
+
da GPU** (onde o TQ3P era 1,52× mais rápido). A simetria não é coincidência: em CPU o denoise é limitado por **bytes
|
| 116 |
+
lidos por peso**, e o K3 a 6,1875 bpw lê ~37 % mais que o `Q4_K_M` a ~4,5 bpw; na GPU o gargalo é
|
| 117 |
+
o caminho de kernel, e ali o TQ3P entra pelo `dequant → cuBLAS`. O text encoder ternário paga o
|
| 118 |
+
mesmo preço (17,5 s vs 12,8 s no encode do prompt). Isto reproduz e reforça o que o projeto já
|
| 119 |
+
havia medido no SD3.5-Medium em CPU (TQ3P 24,0 vs Q4_K_M 18,9 s/it).
|
| 120 |
+
|
| 121 |
+
**Recomendação honesta, por hardware:**
|
| 122 |
+
|
| 123 |
+
| onde você roda | escolha | por quê |
|
| 124 |
+
|---|---|---|
|
| 125 |
+
| **GPU** | **TQ3P (este repo)** | 1,52× mais rápido que o `Q4_K_M`, empata com o FP16, 2,44× menos bytes que o FP16 |
|
| 126 |
+
| **CPU** | **`Q4_K_M`** ([unsloth](https://huggingface.co/unsloth/FLUX.2-klein-9B-GGUF)) | 1,51× mais rápido que este artefato; ternário não é o formato certo aí |
|
| 127 |
+
|
| 128 |
+
E, em qualquer hardware, **CPU não é um caminho confortável para o 9B**: no melhor caso (RAM
|
| 129 |
+
folgada, sem `--mmap`) são ~3–5 min por imagem a 512² e **10–15 min a 1024²**. Numa máquina de
|
| 130 |
+
16 GB, onde o pacote de 14,3 GB só entra com `--mmap`, é pior que isso. Para CPU o alvo razoável é
|
| 131 |
+
o **klein-4B**, 2,3× menor e com licença Apache-2.0.
|
| 132 |
+
|
| 133 |
### Fidelidade — CLIP-score pareado (ViT-L/14, N=16 pares: 8 prompts × 2 seeds)
|
| 134 |
|
| 135 |
| braço | CLIP-score | retenção vs FP16 | Wilcoxon p | placar par-a-par (FP/braço) |
|
|
|
|
| 159 |
degradação.
|
| 160 |
- **CLIP-score não é FID.** Mede alinhamento imagem-texto pareado, não fidelidade perceptual da
|
| 161 |
distribuição. Nenhum FID de Fréchet foi computado.
|
| 162 |
+
- **Escopo da medição:** 1 modelo, 4 steps, euler, VAE `small-decoder`. **GPU** (A100-80 PCIe):
|
| 163 |
+
768² e 1024², 5 repetições em regime permanente. **CPU** (EPYC 4564P, 16 vCPU): 512², 1 execução
|
| 164 |
+
por braço, RAM folgada. Fidelidade: 1024² na GPU, 2 seeds, 8 prompts, 1 CLIP (ViT-L/14), N=16
|
| 165 |
+
pares. Não medido: ARM, outros samplers, FID, o regime com `--mmap` sob RAM apertada, e a
|
| 166 |
+
fidelidade da **edição** (ela funciona — foi exercitada no webapp interno — mas não foi avaliada
|
| 167 |
+
quantitativamente).
|
| 168 |
|
| 169 |
## Como rodar
|
| 170 |
|
|
|
|
| 197 |
Use a flag só se o modelo não couber na sua GPU.
|
| 198 |
- **VRAM necessária** (TQ3P, 1024², sem offload): **14,7 GB** com o text encoder também em TQ3P (7,1 GB o DiT + 7,5 GB o TE + 0,1 GB a VAE), ou 22,9 GB mantendo o text encoder em BF16.
|
| 199 |
- **CPU**: o kernel TQ{K}P usa `_mm256_maddubs_epi16` (AVX2) e é um mpGEMM direto — o trit
|
| 200 |
+
multiplica sem dequantizar. Passe `-t <núcleos>`. **Medido** (ver a seção de CPU acima): aqui o
|
| 201 |
+
`Q4_K_M` ganha por 1,51×; a vantagem de ~1,5× do TQ3P é **exclusivamente de GPU**.
|
|
|
|
| 202 |
- **Servir com o modelo quente:** `sd-server` mantém os pesos carregados; via `sd-cli` cada
|
| 203 |
imagem paga load + setup outra vez (o que também distorce medição de velocidade — veja a
|
| 204 |
nota de correção acima). Para uso repetido, use o servidor.
|
| 205 |
|
| 206 |
+
### `sasori draw` — deixa a ferramenta escolher as flags de memória
|
| 207 |
+
|
| 208 |
+
O `stable-diffusion.cpp` tem várias flags de residência de peso (`--mmap`, `--offload-to-cpu`,
|
| 209 |
+
`--clip-on-cpu`, `--vae-on-cpu`, `--stream-layers`, `--max-vram`) e saber **qual usar em qual
|
| 210 |
+
tamanho** é chato. O formato TQ{K}P decide quantos *bytes* um peso custa; quem decide o que fica
|
| 211 |
+
residente e quando é o runtime — então o sasori calcula o encaixe e escolhe:
|
| 212 |
+
|
| 213 |
+
```bash
|
| 214 |
+
python3 -m sasori draw flux-2-klein-9b-TQ3P.gguf \
|
| 215 |
+
--llm text_encoder/Qwen3-8B-TQ3P.gguf \
|
| 216 |
+
--vae vae/full_encoder_small_decoder.safetensors \
|
| 217 |
+
-p "a lovely cat sitting on a wooden table, soft window light" \
|
| 218 |
+
--sd-cpp <path do stable-diffusion.cpp buildado> -o cat.png
|
| 219 |
+
|
| 220 |
+
# editar (kontext-style): o prompt passa a ser a INSTRUÇÃO
|
| 221 |
+
python3 -m sasori draw ... -r foto.png -p "troque o fundo por uma praia ao amanhecer"
|
| 222 |
+
|
| 223 |
+
# ver a contabilidade e o comando sem rodar nada
|
| 224 |
+
python3 -m sasori draw ... --dry-run
|
| 225 |
+
```
|
| 226 |
+
|
| 227 |
+
Ele soma os pesos em disco + os buffers de compute que a resolução implica (coeficientes medidos:
|
| 228 |
+
VAE **4,76 KB/px**, denoiser **1,36 KB/px**, dobrando com imagem de referência), compara com a VRAM
|
| 229 |
+
livre / a RAM disponível de verdade, e **imprime cada flag com o motivo**. Numa GPU pequena adiciona
|
| 230 |
+
`--offload-to-cpu` + `--clip-on-cpu`; em CPU sem RAM suficiente adiciona `--mmap` **avisando que
|
| 231 |
+
aquilo "cabe" ao custo de reler os pesos do disco a cada step** — em vez de apresentar isso como um
|
| 232 |
+
encaixe limpo. Exemplo real, numa máquina de 16 GB:
|
| 233 |
+
|
| 234 |
+
```
|
| 235 |
+
[sasori draw] pesos 14.18 GB + buffers ~6.12 GB (1024x1024) = 20.30 GB
|
| 236 |
+
[sasori draw] ⚠ pesos+buffers 20.3 GB > RAM disponivel 11.1 GB -> --mmap: CABE, mas cada step pode
|
| 237 |
+
reler do DISCO o que for despejado (14.2 GB de pesos). Menor resolucao ou um modelo
|
| 238 |
+
menor e mais rapido que este modo.
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
**A VAE é o maior consumidor de buffer e não depende do tamanho do denoiser** (a mesma VAE serve o
|
| 242 |
+
4B e o 9B): 4 994 MB a 1024² contra ~1 250 MB a 512². Baixar a resolução é a alavanca de memória
|
| 243 |
+
mais forte que você tem.
|
| 244 |
+
|
| 245 |
### Ternarizar você mesmo (e o que aprendi sobre paralelizar isso)
|
| 246 |
|
| 247 |
```bash
|