File size: 10,674 Bytes
f53c54f bc3209b f53c54f f65ef17 f53c54f f65ef17 f53c54f bc3209b f53c54f bc3209b f53c54f bc3209b f53c54f 722130e f53c54f 722130e 111541a 26151cf f53c54f 111541a f53c54f bc3209b f53c54f 111541a f53c54f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | #!/usr/bin/env bash
# build.sh - one-click build of stable-diffusion.cpp experiment variants for Z-Image-Turbo (CPU).
#
# ./build.sh --all # deps + source + all variants + models
# ./build.sh amx_lto avx512 # only the named variants
# ./build.sh --list # show variants and their CMake flags
# ./build.sh --skip-models amx_lto # no model download
# ./build.sh --skip-openvino --all # skip the OpenVINO INT4 env + model (about 5.5 GB)
#
# Environment overrides:
# WORK=./work working dir (source, binaries, models, venv)
# SDCPP_REF=<commit> upstream stable-diffusion.cpp commit (pinned by default)
# JOBS=<n> parallel build jobs (default: nproc)
# HF_TOKEN=<token> only needed if a model repo becomes gated
# VAE_REPO / VAE_FILE VAE source (default: ffxvs/vae-flux/ae.safetensors, a mirror of the FLUX.1 VAE)
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK="${WORK:-$PWD/work}"
SDCPP_REPO="${SDCPP_REPO:-https://github.com/leejet/stable-diffusion.cpp}"
SDCPP_REF="${SDCPP_REF:-77b72a2efed78f55de26892c7e909cee001e8b83}"
JOBS="${JOBS:-$(nproc)}"
VAE_REPO="${VAE_REPO:-ffxvs/vae-flux}"
VAE_FILE="${VAE_FILE:-ae.safetensors}"
SRC="$WORK/stable-diffusion.cpp"
BIN="$WORK/bin"
MODELS="$WORK/models"
VENV="$WORK/venv"
ALL_VARIANTS=(stock avx2 avx512 amx_lto llamafile)
variant_flags() {
case "$1" in
# upstream defaults: -march=native, no AMX, no LTO
stock) echo "-DGGML_NATIVE=ON" ;;
# AVX2 only (AVX512 disabled), for measuring the AVX512 contribution
avx2) echo "-DGGML_NATIVE=OFF -DGGML_AVX=ON -DGGML_AVX2=ON -DGGML_FMA=ON -DGGML_F16C=ON -DGGML_BMI2=ON -DGGML_AVX512=OFF -DGGML_LTO=ON" ;;
# explicit AVX512 + VNNI + BF16 + AMX (no -march=native)
avx512) echo "-DGGML_NATIVE=OFF -DGGML_AVX=ON -DGGML_AVX2=ON -DGGML_FMA=ON -DGGML_F16C=ON -DGGML_BMI2=ON -DGGML_AVX512=ON -DGGML_AVX512_VBMI=ON -DGGML_AVX512_VNNI=ON -DGGML_AVX512_BF16=ON -DGGML_AVX_VNNI=ON -DGGML_AMX_TILE=ON -DGGML_AMX_INT8=ON -DGGML_AMX_BF16=ON -DGGML_LTO=ON" ;;
# native + AMX + LTO (best build on the reference machine)
amx_lto) echo "-DGGML_NATIVE=ON -DGGML_AMX_TILE=ON -DGGML_AMX_INT8=ON -DGGML_AMX_BF16=ON -DGGML_LTO=ON" ;;
# amx_lto + llamafile sgemm for F32 matmul/conv (VAE im2col path): stage 1
llamafile) echo "-DGGML_NATIVE=ON -DGGML_AMX_TILE=ON -DGGML_AMX_INT8=ON -DGGML_AMX_BF16=ON -DGGML_LTO=ON -DGGML_LLAMAFILE=ON" ;;
*) return 1 ;;
esac
}
log() { printf '\033[1;34m[build]\033[0m %s\n' "$*"; }
die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; }
usage() {
sed -n '2,14p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0
}
# ---------------------------------------------------------------- args
DO_DEPS=1; DO_MODELS=1; DO_OV=1; SELECTED=()
for arg in "$@"; do
case "$arg" in
-h|--help) usage ;;
--list)
for v in "${ALL_VARIANTS[@]}"; do printf '%-9s %s\n' "$v" "$(variant_flags "$v")"; done
exit 0 ;;
--all) SELECTED=("${ALL_VARIANTS[@]}") ;;
--skip-deps) DO_DEPS=0 ;;
--skip-models) DO_MODELS=0 ;;
--skip-openvino) DO_OV=0 ;;
-*) die "unknown option: $arg" ;;
*) variant_flags "$arg" >/dev/null || die "unknown variant: $arg (see --list)"; SELECTED+=("$arg") ;;
esac
done
if [[ ${#SELECTED[@]} -eq 0 && $DO_MODELS -eq 1 ]]; then
log "no variant given; use --all or name variants (see --list). Only fetching models."
fi
[[ "$(uname -m)" == "x86_64" ]] || log "warning: variants are tuned for x86_64 (AVX/AMX flags); this host is $(uname -m)"
mkdir -p "$WORK" "$BIN" "$MODELS"
# ---------------------------------------------------------------- deps
if [[ $DO_DEPS -eq 1 ]]; then
if command -v apt-get >/dev/null 2>&1; then
log "installing system packages (apt)"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq git cmake build-essential python3 python3-venv ca-certificates curl
else
log "apt-get not found; assuming git, cmake, a C++17 compiler and python3 (>=3.9) are installed"
fi
command -v git >/dev/null && command -v cmake >/dev/null || die "git and cmake are required"
if [[ ! -x "$VENV/bin/python" ]]; then
log "creating python venv at $VENV"
python3 -m venv "$VENV"
fi
"$VENV/bin/pip" install -q --upgrade pip
"$VENV/bin/pip" install -q huggingface_hub gguf numpy pillow
fi
PY="$VENV/bin/python"
[[ -x "$PY" ]] || PY="$(command -v python3)"
OV_VENV="$WORK/venv-ov"
OV_MODEL_REPO="${OV_MODEL_REPO:-HelloSun/Z-Image-Turbo-OpenVINO-INT4}"
OV_MODEL_DIR="$MODELS/Z-Image-Turbo-OpenVINO-INT4"
if [[ $DO_DEPS -eq 1 && $DO_OV -eq 1 ]]; then
if [[ ! -x "$OV_VENV/bin/python" ]]; then
log "creating OpenVINO venv at $OV_VENV (pinned: diffusers 0.37.1, optimum-intel 2.2.0, openvino 2026.4.0)"
python3 -m venv "$OV_VENV"
"$OV_VENV/bin/pip" install -q --upgrade pip
"$OV_VENV/bin/pip" install -q 'diffusers==0.37.1' 'transformers==4.57.6' 'tokenizers==0.22.0' \
'optimum==2.3.0' 'optimum-intel==2.2.0' 'openvino==2026.4.0' 'huggingface-hub==0.36.2' pillow numpy
fi
fi
# ---------------------------------------------------------------- source
if [[ ${#SELECTED[@]} -gt 0 ]]; then
if [[ ! -d "$SRC/.git" ]]; then
log "cloning $SDCPP_REPO"
git clone -q "$SDCPP_REPO" "$SRC"
fi
log "checking out sd.cpp $SDCPP_REF"
git -C "$SRC" fetch -q origin || true
git -C "$SRC" checkout -q -f "$SDCPP_REF"
git -C "$SRC" reset -q --hard "$SDCPP_REF"
git -C "$SRC" submodule update -q --init --recursive
# discard leftovers of earlier patch runs so the patches below always apply to the pinned sources
git -C "$SRC" submodule foreach -q --recursive 'git checkout -q -- . 2>/dev/null || true'
# Apply our patches (idempotent: skip a patch that is already applied).
apply_patch() {
local repo="$1" patch="$2"
if git -C "$repo" apply --reverse --check "$patch" >/dev/null 2>&1; then
log "patch already applied: $(basename "$patch")"
else
git -C "$repo" apply --check "$patch" || die "patch does not apply: $(basename "$patch")"
git -C "$repo" apply "$patch"
log "applied patch: $(basename "$patch")"
fi
}
apply_patch "$SRC" "$ROOT/patches/sdcpp-0001-zimage-ffn-w13-and-cpu-extra-buft.patch"
apply_patch "$SRC/ggml" "$ROOT/patches/ggml-0001-cpu-op-profile.patch"
apply_patch "$SRC/ggml" "$ROOT/patches/ggml-0002-flash-attn-kpack.patch"
apply_patch "$SRC/ggml" "$ROOT/patches/ggml-0003-flash-attn-tiles.patch"
apply_patch "$SRC/ggml" "$ROOT/patches/ggml-0004-conv2d-im2col.patch"
# ------------------------------------------------------------ build
for v in "${SELECTED[@]}"; do
log "building variant '$v': $(variant_flags "$v")"
build_dir="$SRC/build-$v"
# shellcheck disable=SC2086
cmake -S "$SRC" -B "$build_dir" -DCMAKE_BUILD_TYPE=Release $(variant_flags "$v") >"$WORK/cmake-$v.log" 2>&1 \
|| { tail -n 30 "$WORK/cmake-$v.log"; die "cmake failed for $v"; }
cmake --build "$build_dir" --config Release --target sd-cli -j "$JOBS" >"$WORK/build-$v.log" 2>&1 \
|| { tail -n 30 "$WORK/build-$v.log"; die "build failed for $v"; }
mkdir -p "$BIN/$v"
cp "$build_dir/bin/sd-cli" "$BIN/$v/sd-cli"
log "-> $BIN/$v/sd-cli"
done
# q4k helper: wraps ggml's own Q4_K quantizer for tools/requant_refiner.py
ref_build="$SRC/build-llamafile"
[[ -f "$ref_build/ggml/src/libggml-base.a" ]] || ref_build="$SRC/build-${SELECTED[0]}"
if [[ -f "$ref_build/ggml/src/libggml-base.a" ]]; then
log "building q4k quantizer helper"
gcc -O2 -fopenmp -I "$SRC/include" -I "$SRC/ggml/include" -I "$SRC/ggml/src" \
"$ROOT/tools/q4k.c" "$ref_build/ggml/src/libggml-base.a" \
-o "$WORK/q4k" -lpthread -lm -lstdc++ \
|| die "failed to build q4k helper"
fi
fi
# ---------------------------------------------------------------- models
if [[ $DO_MODELS -eq 1 ]]; then
log "downloading models into $MODELS"
"$PY" - "$MODELS" "$VAE_REPO" "$VAE_FILE" <<'PY'
import os, sys
from huggingface_hub import hf_hub_download
models_dir, vae_repo, vae_file = sys.argv[1:4]
files = [
("leejet/Z-Image-Turbo-GGUF", "z_image_turbo-Q4_K.gguf"),
("leejet/Z-Image-Turbo-GGUF", "z_image_turbo-Q8_0.gguf"),
("leejet/Z-Image-Turbo-GGUF", "z_image_turbo-Q4_0.gguf"),
("unsloth/Qwen3-4B-Instruct-2507-GGUF", "Qwen3-4B-Instruct-2507-Q4_K_M.gguf"),
(vae_repo, vae_file),
]
for repo, name in files:
dst = os.path.join(models_dir, name)
if os.path.exists(dst):
print(f" have {name}")
continue
print(f" fetching {repo}/{name}")
path = hf_hub_download(repo_id=repo, filename=name, local_dir=models_dir)
print(f" -> {path}")
PY
if [[ $DO_OV -eq 1 ]]; then
if [[ ! -f "$OV_MODEL_DIR/transformer/openvino_model.bin" ]]; then
log "downloading OpenVINO INT4 model $OV_MODEL_REPO (about 5.5 GB)"
"$PY" - "$OV_MODEL_REPO" "$OV_MODEL_DIR" <<'PY'
import sys
from huggingface_hub import snapshot_download
repo, dst = sys.argv[1:3]
snapshot_download(repo_id=repo, local_dir=dst,
ignore_patterns=["examples/*", "generated/*", "*.png", "README.md", "REPORT.md"])
print(" ->", dst)
PY
else
log "have OpenVINO model at $OV_MODEL_DIR"
fi
fi
# FFN gate/up fusion at the weight level (w1 + w3 -> w13); see tools/fuse_ffn.py
if [[ ! -f "$MODELS/z_image_turbo-Q4_K-ffn13.gguf" ]]; then
log "fusing FFN w1/w3 -> w13 (Q4_K)"
"$PY" "$ROOT/tools/fuse_ffn.py" "$MODELS/z_image_turbo-Q4_K.gguf" "$MODELS/z_image_turbo-Q4_K-ffn13.gguf"
fi
# refiner requant: the reference GGUF stores the 2 noise + 2 context refiner
# blocks as Q8_0, which is ~2x slower than Q4_K on a memory-bound CPU GEMM.
# Rewrite just those 16 attention/FFN matrices as Q4_K (adaLN left at Q8_0).
if [[ -x "$WORK/q4k" && ! -f "$MODELS/z_image_turbo-Q4_K-ffn13-rq.gguf" ]]; then
log "requantizing refiner blocks Q8_0 -> Q4_K"
Q4K="$WORK/q4k" "$PY" "$ROOT/tools/requant_refiner.py" \
"$MODELS/z_image_turbo-Q4_K-ffn13.gguf" "$MODELS/z_image_turbo-Q4_K-ffn13-rq.gguf"
fi
fi
log "done"
for v in "${ALL_VARIANTS[@]}"; do
[[ -x "$BIN/$v/sd-cli" ]] && log "binary: $BIN/$v/sd-cli"
done
exit 0
|