Skip to content

gh-150942: Speed up frame local item collection#151002

Open
omkar-334 wants to merge 3 commits into
python:mainfrom
omkar-334:speed-up-frame-locals-items
Open

gh-150942: Speed up frame local item collection#151002
omkar-334 wants to merge 3 commits into
python:mainfrom
omkar-334:speed-up-frame-locals-items

Conversation

@omkar-334
Copy link
Copy Markdown
Contributor

@omkar-334 omkar-334 commented Jun 6, 2026

Use _PyList_AppendTakeRef while collecting frame-local (name, value) pairs
instead of PyList_Append followed by Py_DECREF, removing an incref/decref
pair per returned item pair.

benchmarks

Benchmark main this PR speedup
frame_locals_items_100locals 9.09 ms 8.61 ms 1.06x
frame_locals_items_1000locals 17.19 ms 16.59 ms 1.04x
frame_locals_items_5000locals 29.92 ms 28.94 ms 1.03x
geomean 1.04x
Benchmark script
"""Micro-benchmark for frame.f_locals.items() result-list building."""

from __future__ import annotations

import argparse
import json
import statistics
import sys
import time
from pathlib import Path


def make_frame(local_count: int):
    assignments = "\n".join(f"    v{i} = {i}" for i in range(local_count))
    src = (
        "def make_frame():\n"
        f"{assignments}\n"
        "    return sys._getframe()\n"
    )
    ns = {"sys": sys}
    exec(src, ns)
    return ns["make_frame"]()


def time_items(frame, loops: int) -> float:
    t0 = time.perf_counter_ns()
    for _ in range(loops):
        frame.f_locals.items()
    return (time.perf_counter_ns() - t0) / 1e9


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--locals", type=int, default=1_000)
    ap.add_argument("--loops", type=int, default=2_000)
    ap.add_argument("--trials", type=int, default=15)
    ap.add_argument("--warmup", type=int, default=3)
    ap.add_argument("--label", default="run")
    ap.add_argument("--outdir", default="results/frame-locals-items")
    args = ap.parse_args()

    frame = make_frame(args.locals)
    item_count = len(frame.f_locals.items())

    for _ in range(args.warmup):
        time_items(frame, args.loops)

    trials = [time_items(frame, args.loops) for _ in range(args.trials)]

    outdir = Path(args.outdir)
    outdir.mkdir(parents=True, exist_ok=True)
    summary = {
        "label": args.label,
        "python": sys.executable,
        "locals": args.locals,
        "item_count": item_count,
        "loops": args.loops,
        "trials": args.trials,
        "warmup": args.warmup,
        "items_built": item_count * args.loops,
        "seconds": {
            "min": min(trials),
            "median": statistics.median(trials),
            "mean": statistics.fmean(trials),
            "stdev": statistics.pstdev(trials),
            "all": trials,
        },
        "items_per_sec_at_min": (item_count * args.loops) / min(trials),
    }
    out = outdir / f"bench_{args.label}.json"
    out.write_text(json.dumps(summary, indent=2))

    s = summary["seconds"]
    print(
        f"{args.label}: min={s['min']*1000:.2f}ms median={s['median']*1000:.2f}ms "
        f"mean={s['mean']*1000:.2f}ms stdev={s['stdev']*1000:.2f}ms "
        f"items/s@min={summary['items_per_sec_at_min']:.2e}"
    )
    print(f"wrote {out}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

@omkar-334 omkar-334 requested a review from markshannon as a code owner June 6, 2026 03:01
@omkar-334 omkar-334 changed the title Speed up frame local item collection gh-150942: Speed up frame local item collection Jun 6, 2026
Comment thread Misc/NEWS.d/next/Core_and_Builtins/2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst Outdated
Copy link
Copy Markdown
Contributor

@sergey-miryanov sergey-miryanov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants