GPU Notebook

Sizing guide: what really fits in 16 GB

Role
Instrumentation, measurement and advice
Tools
  • Python
  • ComfyUI
  • llama.cpp
  • mlx-lm
  • PyTorch MPS
  • SVG
Status
Functional
Time per deliverable, fully local: median and min–max over real runs, log scale.
Time per deliverable, fully local: median and min–max over real runs, log scale.
measurements read from 7 projects
18
video distillation speed-up (4 steps vs 16)
×7.8
slowdown once swap saturates
×8.9
peak of the 9B LoRA: the dedicated-GPU threshold, measured
12.3 GB
The problem
Choosing a local generation setup is often done by feel; it needs measured values, conditions included.
For whom
The portfolio’s local projects, and any team that has to size a generation machine.
The outcome
A verdict per task from 18 real measurements: 7B image in 141 s, distilled video in 40 s, 3B LLM at 28–53 tok/s; 1.2B LoRA in 18.6 min, 9B LoRA on a dedicated GPU (12.3 GB peak).

Context

The question from a client who wants “local AI”: what fits on a 16 GB machine, how long does it take, and where should you rent a GPU? The portfolio projects produced real measurements; this project reads them, compares them and draws a verdict per task.

What I built

  • Standard-library ComfyUI and LLM benches: wall time, memory, image hashing, cold and warm kept apart.
  • Automatic synthesis: one script reads 16 measurement files (CSV, JSON, logs) and writes the table and the charts in French and English, with the source of every number.
  • Sizing guide: verdict per task, the setting that works, the point to move to a dedicated GPU, five rules drawn from the measurements.
  • Benchmark-trap detection: ComfyUI execution cache (1 s, bit-identical image), GPU throughput dropping on the 3rd call.

Architecture 6 blocks

  1. 01 · InputConditionsMachine, versions, graph and settings recorded
  2. 02 · ToolComfyUI benchtxt2img, wall time, memory, image hashing
  3. 03 · ToolLLM benchllama-server, Metal vs CPU, prompt cache off
  4. 04 · DataMeasurementsCSV and JSON, one value per run, cold and warm
  5. 05 · ToolSynthesis16 files read across 7 projects, source of every number kept
  6. 06 · OutputSizing guideVerdict per task, FR/EN charts, dedicated-GPU thresholds

Pipeline 4 steps

  1. MeasureEach project writes its timings and memory peaks.
  2. ReadThe script finds the 18 measurements in their files.
  3. CompareTime and memory charts, FR and EN.
  4. AdviseVerdict per task and switch-over points.

Decisions the choice, the reason, the alternative ruled out

  1. D1
    Publish each run separately, with no percentiles on four values.

    A median would hide the GPU throughput collapse from the 3rd warm call (52.9 → 30.0 → 28.2 tok/s).

    Ruled outAverages or percentiles.

  2. D2
    Detect ComfyUI’s execution cache and exclude it from averages.

    An already-computed graph returns in 1 s with a bit-identical image: a classic benchmarking trap.

    Ruled outAveraging everything unchecked.

  3. D3
    llama.cpp (llama-server) with Qwen2.5-3B Q4_K_M, temperature 0, prompt cache off.

    The same server switches between GPU and CPU: both series compare under identical conditions.

    Ruled outOllama, ruled out.

  4. D4
    Read the numbers from the measurement files instead of copying them.

    A sizing guide is only worth something if every number can be traced to its source; the synthesis regenerates after every new series.

    Ruled outA hand-typed table.

  5. D5
    Keep process memory and Metal buffers apart.

    Every memory figure is labelled for what it measures: no value is presented as GPU memory without a dedicated measurement.

Results

Verdict per task — MacBook Pro M1 Pro 16 GB

TaskLocal?Measured setting
768² image, 7B modelyes, batchedGGUF Q4 + 4 steps: 141 s (n = 55)
Photo retouch → 3600×2400 masteryesfull ComfyUI workflow: 171 s
16-frame video clipyes, distilled4 steps: 40 s (n = 8), 9.0 GB peak
3B LLM generationyesllama.cpp GPU: 28–53 tok/s
LoRA on a 1.2B LLMyes18.6 min, 2.2 GB peak
LoRA on a 9B LLMnoMetal peak 12.3 GB → GPU ≥ 24 GB

Every value is read from the file that produced it (source column of the synthesis table).

Qwen2.5-3B Q4_K_M, 128 generated tokens

RunPhaseMetal (tok/s)CPU (tok/s)
1cold52.510.0
2warm52.912.0
3warm30.012.8
4warm28.221.1

GPU throughput drops from the 3rd warm call: an average would have hidden it.

Deliverables 4

  • ComfyUI bench and LLM bench in Python, standard library.
  • Four measured series: SD 1.5 validation and warm, LLM on Metal and on CPU.
  • Full conditions for each series: versions, graph, settings.
  • Sizing guide, sourced synthesis table and two charts in French and English, regenerated with one command.

Environment

Hardware and measurement setup

  • Every measurement on a MacBook Pro M1 Pro 16 GB, ComfyUI 0.41.0 and PyTorch on MPS, in real-workstation conditions: min–max spreads are shown.

Reproduce README commands

Private code: read access under NDA, or a 30-minute live demo. Request access →

cd portfolio/projets/p06-gpu-notebook
python3 guide/synthese.py   # tableau + graphiques FR/EN, < 1 s

# nouvelle série image (ComfyUI lancé sur le port 8188) :
cd banc
python3 banc-comfyui.py --seeds 404,505,606 --steps 20 \
  --prompt "wet dark basalt rock close-up, hard raking light, fine mist, mineral texture, photorealistic" \
  --out ../mesures/AAAA-MM-JJ_nom-de-serie
  • MacBook Pro M1 Pro 16 GB, ComfyUI 0.41.0, PyTorch on MPS.
  • Timings measured on a real workstation: min–max spreads are shown.

Gallery 2

Peak memory per workload against 16 GB: beyond ~10 GB the machine swaps, the point to move to a dedicated GPU.
Time per deliverable, from 10 s (×4 restoration) to 5 min (non-distilled video).

What this project means for you

The same know-how, on your problem.

01

Sizing your machine

Before you buy or rent, I measure your real tasks on the hardware in question, every run shown with its conditions.

02

Choosing local or cloud

Throughput and timings measured in your conditions, to decide what runs on-site and what goes elsewhere.

03

Reproducible benches

Scripts that record versions and settings: the measurement can be redone identically after every update.