chore: import upstream snapshot with attribution
CI / lint (3.11) (push) Has been cancelled
CI / lint (3.12) (push) Has been cancelled
CI / lint (3.13) (push) Has been cancelled
CI / shellcheck (push) Has been cancelled
CI / shfmt (push) Has been cancelled
CI / setup (3.11) (push) Has been cancelled
CI / setup (3.12) (push) Has been cancelled
CI / setup (3.13) (push) Has been cancelled
CI / check-licenses (3.12) (push) Has been cancelled
CI / test_unit (3.11) (push) Has been cancelled
CI / test_unit (3.12) (push) Has been cancelled
CI / test_unit (3.13) (push) Has been cancelled
CI / test_unit_no_extras (3.11) (push) Has been cancelled
CI / test_unit_no_extras (3.12) (push) Has been cancelled
CI / test_json_to_html (3.12) (push) Has been cancelled
CI / test_unit_no_extras (3.13) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.12, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.11, --extra xlsx) (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.12, --extra xlsx) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.11, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.13, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.11, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.12, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.13, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.11, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.12, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.13, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.11, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.12, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.13, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.11, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.12, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.13, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.11, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.12, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.13, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.11, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.12, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.13, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
Build And Push Docker Image / set-short-sha (push) Has been cancelled
Partition Benchmark / setup (push) Has been cancelled
Partition Benchmark / Measure and compare partition() runtime (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.13, --extra xlsx) (push) Has been cancelled
CI / test_ingest_src (3.12) (push) Has been cancelled
CI / test_json_to_markdown (3.12) (push) Has been cancelled
CI / changelog (push) Has been cancelled
CI / test_dockerfile (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build And Push Docker Image / build-images (linux/amd64, opensource-linux-8core) (push) Has been cancelled
Build And Push Docker Image / build-images (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Build And Push Docker Image / publish-images (push) Has been cancelled
CI / lint (3.11) (push) Has been cancelled
CI / lint (3.12) (push) Has been cancelled
CI / lint (3.13) (push) Has been cancelled
CI / shellcheck (push) Has been cancelled
CI / shfmt (push) Has been cancelled
CI / setup (3.11) (push) Has been cancelled
CI / setup (3.12) (push) Has been cancelled
CI / setup (3.13) (push) Has been cancelled
CI / check-licenses (3.12) (push) Has been cancelled
CI / test_unit (3.11) (push) Has been cancelled
CI / test_unit (3.12) (push) Has been cancelled
CI / test_unit (3.13) (push) Has been cancelled
CI / test_unit_no_extras (3.11) (push) Has been cancelled
CI / test_unit_no_extras (3.12) (push) Has been cancelled
CI / test_json_to_html (3.12) (push) Has been cancelled
CI / test_unit_no_extras (3.13) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.12, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.11, --extra xlsx) (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.12, --extra xlsx) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.11, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.13, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.11, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.12, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.13, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.11, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.12, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.13, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.11, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.12, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.13, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.11, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.12, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.13, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.11, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.12, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.13, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.11, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.12, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.13, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
Build And Push Docker Image / set-short-sha (push) Has been cancelled
Partition Benchmark / setup (push) Has been cancelled
Partition Benchmark / Measure and compare partition() runtime (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.13, --extra xlsx) (push) Has been cancelled
CI / test_ingest_src (3.12) (push) Has been cancelled
CI / test_json_to_markdown (3.12) (push) Has been cancelled
CI / changelog (push) Has been cancelled
CI / test_dockerfile (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build And Push Docker Image / build-images (linux/amd64, opensource-linux-8core) (push) Has been cancelled
Build And Push Docker Image / build-images (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Build And Push Docker Image / publish-images (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
"""Quick local benchmark for PDF partition cold/warm timing.
|
||||
|
||||
Examples:
|
||||
uv run --active --frozen --no-sync scripts/performance/quick_partition_bench.py \
|
||||
--pdf example-docs/pdf/DA-1p.pdf --strategy fast --repeats 4 --warmups 1 --mode both
|
||||
|
||||
uv run --active --frozen --no-sync scripts/performance/quick_partition_bench.py \
|
||||
--pdf example-docs/pdf/DA-1p.pdf --pdf example-docs/pdf/chevron-page.pdf \
|
||||
--strategy hi_res --repeats 3 --warmups 1 --mode both
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import io
|
||||
import json
|
||||
import statistics
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from contextlib import redirect_stderr, redirect_stdout
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(REPO_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(REPO_ROOT))
|
||||
|
||||
|
||||
def _partition_once(pdf: str, strategy: str) -> dict[str, object]:
|
||||
sink_out = io.StringIO()
|
||||
sink_err = io.StringIO()
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
from unstructured.partition.auto import partition
|
||||
|
||||
with redirect_stdout(sink_out), redirect_stderr(sink_err):
|
||||
elements = partition(filename=pdf, strategy=strategy)
|
||||
return {
|
||||
"ok": True,
|
||||
"elapsed_s": time.perf_counter() - start,
|
||||
"elements": len(elements),
|
||||
}
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return {"ok": False, "error": f"{type(exc).__name__}: {exc}"}
|
||||
|
||||
|
||||
def _summary(values: list[float]) -> dict[str, float]:
|
||||
return {
|
||||
"mean_s": statistics.mean(values),
|
||||
"median_s": statistics.median(values),
|
||||
"min_s": min(values),
|
||||
"max_s": max(values),
|
||||
"stdev_s": statistics.stdev(values) if len(values) > 1 else 0.0,
|
||||
}
|
||||
|
||||
|
||||
def _run_cold(pdf: str, strategy: str, repeats: int) -> tuple[list[float], int, list[str]]:
|
||||
times: list[float] = []
|
||||
elements = -1
|
||||
errors: list[str] = []
|
||||
|
||||
for _ in range(repeats):
|
||||
proc = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
__file__,
|
||||
"--_child",
|
||||
"--pdf",
|
||||
pdf,
|
||||
"--strategy",
|
||||
strategy,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
lines = [line.strip() for line in (proc.stdout or "").splitlines() if line.strip()]
|
||||
json_line = next((line for line in reversed(lines) if line.startswith("{")), "")
|
||||
if not json_line:
|
||||
stderr_tail = (proc.stderr or "").strip().splitlines()
|
||||
detail = stderr_tail[-1] if stderr_tail else "no json output"
|
||||
errors.append(f"child failed rc={proc.returncode} ({detail})")
|
||||
continue
|
||||
|
||||
row = json.loads(json_line)
|
||||
if bool(row.get("ok")):
|
||||
times.append(float(row["elapsed_s"]))
|
||||
elements = int(row["elements"])
|
||||
else:
|
||||
errors.append(str(row.get("error", "unknown error")))
|
||||
|
||||
return times, elements, errors
|
||||
|
||||
|
||||
def _run_warm(
|
||||
pdf: str,
|
||||
strategy: str,
|
||||
repeats: int,
|
||||
warmups: int,
|
||||
) -> tuple[list[float], int, list[str]]:
|
||||
errors: list[str] = []
|
||||
|
||||
for _ in range(warmups):
|
||||
row = _partition_once(pdf=pdf, strategy=strategy)
|
||||
if not bool(row.get("ok")):
|
||||
errors.append(str(row.get("error", "unknown error")))
|
||||
return [], -1, errors
|
||||
|
||||
times: list[float] = []
|
||||
elements = -1
|
||||
for _ in range(repeats):
|
||||
row = _partition_once(pdf=pdf, strategy=strategy)
|
||||
if bool(row.get("ok")):
|
||||
times.append(float(row["elapsed_s"]))
|
||||
elements = int(row["elements"])
|
||||
else:
|
||||
errors.append(str(row.get("error", "unknown error")))
|
||||
|
||||
return times, elements, errors
|
||||
|
||||
|
||||
def _collect_pdfs(pdf_args: list[str], pdf_dir_args: list[str]) -> list[str]:
|
||||
paths = [str(Path(p)) for p in pdf_args]
|
||||
|
||||
for pdf_dir in pdf_dir_args:
|
||||
root = Path(pdf_dir)
|
||||
if not root.is_dir():
|
||||
raise FileNotFoundError(f"pdf-dir does not exist: {root}")
|
||||
paths.extend(str(p) for p in sorted(root.rglob("*.pdf")))
|
||||
|
||||
if not paths:
|
||||
raise ValueError("Provide at least one --pdf or --pdf-dir")
|
||||
|
||||
deduped = [Path(p) for p in dict.fromkeys(paths)]
|
||||
missing = [str(p) for p in deduped if not p.exists()]
|
||||
if missing:
|
||||
raise FileNotFoundError(f"Missing files: {', '.join(missing)}")
|
||||
|
||||
return [str(p) for p in deduped]
|
||||
|
||||
|
||||
def _print_mode(label: str, values: list[float], elements: int, errors: list[str]) -> None:
|
||||
if not values:
|
||||
first = errors[0] if errors else "unknown error"
|
||||
print(f" {label} FAILED ({first})", flush=True)
|
||||
return
|
||||
|
||||
s = _summary(values)
|
||||
print(
|
||||
f" {label} mean={s['mean_s']:.4f}s median={s['median_s']:.4f}s "
|
||||
f"min={s['min_s']:.4f}s max={s['max_s']:.4f}s n={len(values)} elements={elements}",
|
||||
flush=True,
|
||||
)
|
||||
if errors:
|
||||
print(f" {label} partial_failures={len(errors)} first_error={errors[0]}", flush=True)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Quick local PDF partition benchmark")
|
||||
parser.add_argument("--pdf", action="append", default=[], help="PDF path (repeatable)")
|
||||
parser.add_argument("--pdf-dir", action="append", default=[], help="Directory of PDFs")
|
||||
parser.add_argument("--strategy", default="fast", choices=["fast", "hi_res", "auto"])
|
||||
parser.add_argument("--repeats", type=int, default=3)
|
||||
parser.add_argument("--warmups", type=int, default=1)
|
||||
parser.add_argument("--mode", default="both", choices=["cold", "warm", "both"])
|
||||
parser.add_argument("--json-out", default="", help="Optional JSON output path")
|
||||
parser.add_argument("--_child", action="store_true", help=argparse.SUPPRESS)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args._child:
|
||||
print(json.dumps(_partition_once(args.pdf[0], args.strategy)), flush=True)
|
||||
return
|
||||
|
||||
pdfs = _collect_pdfs(args.pdf, args.pdf_dir)
|
||||
|
||||
print(
|
||||
f"strategy={args.strategy} mode={args.mode} repeats={args.repeats} "
|
||||
f"warmups={args.warmups} pdf_count={len(pdfs)}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
by_mode_times: dict[str, list[float]] = {"cold": [], "warm": []}
|
||||
by_mode_file_means: dict[str, list[float]] = {"cold": [], "warm": []}
|
||||
by_mode_failed_files: dict[str, int] = {"cold": 0, "warm": 0}
|
||||
results: list[dict[str, object]] = []
|
||||
|
||||
for pdf in pdfs:
|
||||
row: dict[str, object] = {"pdf": pdf}
|
||||
print(f"FILE {pdf}", flush=True)
|
||||
|
||||
if args.mode in ("cold", "both"):
|
||||
times, elements, errors = _run_cold(pdf, args.strategy, args.repeats)
|
||||
_print_mode("cold", times, elements, errors)
|
||||
if times:
|
||||
by_mode_times["cold"].extend(times)
|
||||
by_mode_file_means["cold"].append(statistics.mean(times))
|
||||
row["cold"] = {"ok": True, "times_s": times, "elements": elements, "errors": errors}
|
||||
else:
|
||||
by_mode_failed_files["cold"] += 1
|
||||
row["cold"] = {"ok": False, "errors": errors}
|
||||
|
||||
if args.mode in ("warm", "both"):
|
||||
times, elements, errors = _run_warm(pdf, args.strategy, args.repeats, args.warmups)
|
||||
_print_mode("warm", times, elements, errors)
|
||||
if times:
|
||||
by_mode_times["warm"].extend(times)
|
||||
by_mode_file_means["warm"].append(statistics.mean(times))
|
||||
row["warm"] = {
|
||||
"ok": True,
|
||||
"times_s": times,
|
||||
"elements": elements,
|
||||
"errors": errors,
|
||||
"warmups": args.warmups,
|
||||
}
|
||||
else:
|
||||
by_mode_failed_files["warm"] += 1
|
||||
row["warm"] = {"ok": False, "errors": errors, "warmups": args.warmups}
|
||||
|
||||
results.append(row)
|
||||
|
||||
aggregate: dict[str, object] = {}
|
||||
print("AGGREGATE", flush=True)
|
||||
for mode in ("cold", "warm"):
|
||||
times = by_mode_times[mode]
|
||||
if not times:
|
||||
continue
|
||||
s = _summary(times)
|
||||
file_mean = statistics.mean(by_mode_file_means[mode])
|
||||
succeeded = len(by_mode_file_means[mode])
|
||||
failed = by_mode_failed_files[mode]
|
||||
aggregate[mode] = {
|
||||
"summary": s,
|
||||
"file_mean_s": file_mean,
|
||||
"samples": len(times),
|
||||
"succeeded_files": succeeded,
|
||||
"failed_files": failed,
|
||||
}
|
||||
print(
|
||||
f" {mode} succeeded_files={succeeded} failed_files={failed} "
|
||||
f"file_mean={file_mean:.4f}s mean={s['mean_s']:.4f}s median={s['median_s']:.4f}s "
|
||||
f"min={s['min_s']:.4f}s max={s['max_s']:.4f}s "
|
||||
f"stdev={s['stdev_s']:.4f}s samples={len(times)}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
if args.json_out:
|
||||
out = Path(args.json_out)
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
out.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"strategy": args.strategy,
|
||||
"mode": args.mode,
|
||||
"repeats": args.repeats,
|
||||
"warmups": args.warmups,
|
||||
"pdf_count": len(pdfs),
|
||||
"per_file": results,
|
||||
"aggregate": aggregate,
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
)
|
||||
print(f"json_out={out}", flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user