Commit 333c6d7
authored
mem: free intermediate arrays during YoloX inference (#496)
Free `origin_img`, `img`/`ort_inputs`, `output`, and the **PIL pixel
buffer** at the points where they become dead in `image_processing()`,
instead of letting them linger until function return.
The two main wins:
1. **`origin_img`** — the full-resolution numpy copy of the input PIL
image stays alive through the entire ONNX `session.run()` call. `del
origin_img` frees it before inference.
2. **The PIL image itself** — after `np.array(image)` copies the pixel
data, the PIL buffer is no longer needed. `image.close()` frees it
immediately while preserving PIL metadata (`.width`, `.height`,
`.format`, `.size`).
Savings are proportional to image size: larger pages (higher DPI
renders) carry bigger unused buffers through inference.
## Benchmark
### Azure Standard_D8s_v5 — 8 vCPU Intel Xeon Platinum 8473C, 32 GiB RAM
Simulated ONNX session (35 MiB workspace), 1700×2200 letter-size image
at 200 DPI.
#### Memory
| Ref | Peak Memory | Allocations | Delta |
|:---|---:|---:|:---|
| `main` (base) | 72.0 MiB | 124 | |
| This PR (head) | 47.0 MiB | 118 | 🟢 -35% |
**Peak memory drops 25 MiB (-35%)** by freeing dead buffers before ONNX
inference. Timing is neutral (within noise).
---
*Generated by codeflash optimization agent*
<details>
<summary><b>Reproduce the benchmark locally</b></summary>
This PR includes a memory benchmark at
`benchmarks/test_benchmark_yolox.py`. Save the script below as
`compare_memory.py` and run it from the repo root:
```bash
pip install memray rich pytest-benchmark
python compare_memory.py
```
```python
#!/usr/bin/env python3
"""Compare peak memory between main and the current branch."""
import argparse, subprocess, sys, tempfile
from pathlib import Path
from rich.console import Console
from rich.table import Table
console = Console()
RUNNER = "import sys\nsys.exit(__import__('pytest').main(sys.argv[1:]))\n"
def branch():
return subprocess.run(["git","rev-parse","--abbrev-ref","HEAD"],
capture_output=True, text=True, check=True).stdout.strip()
def profile(bench, bin_path, runner, ref, bench_src=None):
head = branch()
checkout = ref != head
if checkout:
subprocess.run(["git","stash","--include-untracked"], capture_output=True)
subprocess.run(["git","checkout",ref], capture_output=True, check=True)
copied = False
try:
bp = Path(bench)
if not bp.exists() and bench_src:
bp.parent.mkdir(parents=True, exist_ok=True)
bp.write_text(bench_src, encoding="utf-8")
copied = True
subprocess.run([sys.executable,"-m","memray","run","--force","-o",bin_path,
runner, bench,"-x","-q","--no-header","-rN"],
check=True, timeout=600)
finally:
if copied:
Path(bench).unlink(missing_ok=True)
if checkout:
subprocess.run(["git","checkout",head], capture_output=True, check=True)
subprocess.run(["git","stash","pop"], capture_output=True)
def read_peak(bin_path):
from memray import FileReader
r = FileReader(bin_path)
return r.metadata.peak_memory, r.metadata.total_allocations
def fmt(n):
return f"{n/(1<<20):.1f} MiB" if n >= 1<<20 else f"{n/(1<<10):.1f} KiB" if n >= 1<<10 else f"{n} B"
def main():
p = argparse.ArgumentParser()
p.add_argument("bench", nargs="?", default="benchmarks/test_benchmark_yolox.py")
p.add_argument("--base", default="main")
args = p.parse_args()
head = branch()
if head == args.base:
console.print(f"[red]Already on {args.base} — checkout the PR branch first.[/red]"); sys.exit(1)
bench_src = Path(args.bench).read_text(encoding="utf-8") if Path(args.bench).exists() else None
with tempfile.TemporaryDirectory() as td:
runner = str(Path(td) / "run_bench.py")
Path(runner).write_text(RUNNER, encoding="utf-8")
console.print(f"\n[bold]Profiling [cyan]{head}[/cyan]...[/bold]")
profile(args.bench, f"{td}/head.bin", runner, head)
hp, ha = read_peak(f"{td}/head.bin")
console.print(f"[bold]Profiling [cyan]{args.base}[/cyan]...[/bold]")
profile(args.bench, f"{td}/base.bin", runner, args.base, bench_src=bench_src)
bp, ba = read_peak(f"{td}/base.bin")
table = Table(title=f"Memory: {args.base} vs {head}")
for col in ["Ref","Peak Memory","Allocations","Delta"]:
table.add_column(col, justify="right" if col != "Ref" else "left")
table.add_row(f"{args.base} (base)", fmt(bp), f"{ba:,}", "")
pct = (hp-bp)/bp*100 if bp else 0
icon = "🟢" if pct < -1 else ("🔴" if pct > 1 else "⚪")
table.add_row(f"{head} (head)", fmt(hp), f"{ha:,}", f"{icon} {pct:+.1f}%")
console.print(table)
if __name__ == "__main__":
main()
```
</details>1 parent b851f09 commit 333c6d7
6 files changed
Lines changed: 76 additions & 1 deletion
File tree
- benchmarks
- unstructured_inference
- models
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
1 | 6 | | |
2 | 7 | | |
3 | 8 | | |
| |||
Whitespace-only changes.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
131 | 134 | | |
132 | 135 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
| 110 | + | |
110 | 111 | | |
| 112 | + | |
111 | 113 | | |
112 | 114 | | |
113 | 115 | | |
114 | 116 | | |
| 117 | + | |
115 | 118 | | |
116 | 119 | | |
| 120 | + | |
117 | 121 | | |
118 | 122 | | |
119 | 123 | | |
| |||
0 commit comments