Feature or enhancement
Proposal:
The call_tzinfo_method() (invoked from tzinfo.utcoffset() and tzinfo.dst()) use:
offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg);
On every call this parses the "O" format string, builds a temporary argument tuple, and rebuilds the method-name str (PyObject_GetAttrString). Switching to PyObject_CallMethodOneArg with interned strings improves performance.
Benchmarks
Wall-clock, PGO+LTO build
| operation |
before |
after |
speedup |
astimezone() UTC→local |
225.3 ns |
124.3 ns |
1.81× |
timestamp() |
461.5 ns |
270.2 ns |
1.71× |
isoformat() (aware) |
667.2 ns |
542.1 ns |
1.23× |
utcoffset() (direct) |
116.6 ns |
48.4 ns |
2.41× |
now().isoformat() (control) |
664.8 ns |
681.8 ns |
0.98× |
tzname() (control) |
48.8 ns |
48.3 ns |
1.01× |
Benchmark script
"""Microbenchmark for datetime call_tzinfo_method (gh-150942)."""
import datetime as dt
import time
from datetime import timezone
from zoneinfo import ZoneInfo
ny = ZoneInfo("America/New_York")
utc_dt = dt.datetime(2024, 6, 7, 16, 30, tzinfo=timezone.utc)
ny_dt = dt.datetime(2024, 6, 7, 12, 30, tzinfo=ny)
def bench(fn, N=1_000_000, repeat=7):
best = float("inf")
for _ in range(repeat):
start = time.perf_counter()
for _ in range(N):
fn()
best = min(best, (time.perf_counter() - start) / N * 1e9)
return best
CASES = {
"astimezone() UTC->local": lambda: utc_dt.astimezone(ny),
"timestamp()": lambda: ny_dt.timestamp(),
"isoformat()": lambda: ny_dt.isoformat(),
"utcoffset()": lambda: ny_dt.utcoffset(),
"now().isoformat()": lambda: dt.datetime.now().isoformat(), # naive: control
"tzname() [control]": lambda: ny_dt.tzname(),
}
print(f"{'operation':<26}{'ns/op':>10}")
print("-" * 36)
for name, fn in CASES.items():
print(f"{name:<26}{bench(fn):>9.1f}")
Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
No response
Linked PRs
Feature or enhancement
Proposal:
The
call_tzinfo_method()(invoked fromtzinfo.utcoffset()andtzinfo.dst()) use:On every call this parses the
"O"format string, builds a temporary argument tuple, and rebuilds the method-namestr(PyObject_GetAttrString). Switching toPyObject_CallMethodOneArgwith interned strings improves performance.Benchmarks
Wall-clock, PGO+LTO build
astimezone()UTC→localtimestamp()isoformat()(aware)utcoffset()(direct)now().isoformat()(control)tzname()(control)Benchmark script
Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
No response
Linked PRs