mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-04-07 08:03:40 +00:00
perf(interval_pool): hoist fetch_groups and precompute period criteria
- Move UTC import from inline (inside _has_real_gaps_in_range) to module-level in manager.py - Hoist get_fetch_groups() out of while loop in _get_cached_intervals: eliminates ~384 function calls per invocation - Pre-compute criteria_by_day dict in build_periods before the for-loop: eliminates ~381 redundant NamedTuple constructions per call; only ref_price/avg_price vary by day (max 3 entries), flex/min_distance/ reverse_sort are constant throughout Impact: Reduces unnecessary object creation during the hot paths called every 15 minutes and during all relaxation phases.
This commit is contained in:
parent
636bd7a797
commit
8f05f8cac7
2 changed files with 16 additions and 12 deletions
|
|
@ -112,6 +112,19 @@ def build_periods( # noqa: PLR0913, PLR0915, PLR0912 - Complex period building
|
|||
intervals_filtered_by_flex = 0
|
||||
intervals_filtered_by_min_distance = 0
|
||||
|
||||
# Pre-compute criteria per day (flex/min_distance/reverse_sort are constant throughout;
|
||||
# only ref_price and avg_price vary by day — max 3 entries: yesterday/today/tomorrow)
|
||||
criteria_by_day: dict[date, TibberPricesIntervalCriteria] = {
|
||||
day: TibberPricesIntervalCriteria(
|
||||
ref_price=ref_prices[day],
|
||||
avg_price=avg_prices[day],
|
||||
flex=flex,
|
||||
min_distance_from_avg=min_distance_from_avg,
|
||||
reverse_sort=reverse_sort,
|
||||
)
|
||||
for day in ref_prices
|
||||
}
|
||||
|
||||
for price_data in all_prices:
|
||||
starts_at = time.get_interval_time(price_data)
|
||||
if starts_at is None:
|
||||
|
|
@ -133,13 +146,7 @@ def build_periods( # noqa: PLR0913, PLR0915, PLR0912 - Complex period building
|
|||
ref_date = date_key
|
||||
|
||||
# Check flex and minimum distance criteria (using smoothed price and interval's own day reference)
|
||||
criteria = TibberPricesIntervalCriteria(
|
||||
ref_price=ref_prices[ref_date],
|
||||
avg_price=avg_prices[ref_date],
|
||||
flex=flex,
|
||||
min_distance_from_avg=min_distance_from_avg,
|
||||
reverse_sort=reverse_sort,
|
||||
)
|
||||
criteria = criteria_by_day[ref_date]
|
||||
in_flex, meets_min_distance = check_interval_criteria(price_for_criteria, criteria)
|
||||
|
||||
# Track why intervals are filtered
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
import contextlib
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
|
@ -459,8 +459,6 @@ class TibberPricesIntervalPool:
|
|||
True if a real gap exists, False if the range is fully covered.
|
||||
|
||||
"""
|
||||
from datetime import UTC # noqa: PLC0415 - UTC constant needed here only
|
||||
|
||||
cached_intervals = self._get_cached_intervals(start_iso, end_iso)
|
||||
|
||||
if not cached_intervals:
|
||||
|
|
@ -582,14 +580,13 @@ class TibberPricesIntervalPool:
|
|||
resolution_change_naive = datetime(2025, 10, 1) # noqa: DTZ001
|
||||
interval_minutes = INTERVAL_QUARTER_HOURLY if current_naive >= resolution_change_naive else INTERVAL_HOURLY
|
||||
|
||||
fetch_groups = self._cache.get_fetch_groups()
|
||||
while current_naive < end_naive:
|
||||
# Check if this timestamp exists in index (O(1) lookup)
|
||||
current_dt_key = current_naive.isoformat()[:19]
|
||||
location = self._index.get(current_dt_key)
|
||||
|
||||
if location is not None:
|
||||
# Get interval from fetch group
|
||||
fetch_groups = self._cache.get_fetch_groups()
|
||||
fetch_group = fetch_groups[location["fetch_group_index"]]
|
||||
interval = fetch_group["intervals"][location["interval_index"]]
|
||||
# CRITICAL: Return shallow copy to prevent external mutations
|
||||
|
|
|
|||
Loading…
Reference in a new issue