Decrease event loop latency by binding time.monotonic to loop.time directly (#98288)

* Decrease event loop latency by binding time.monotonic to loop.time directly

This is a small improvment to decrease event loop latency. While the goal is
is to reduce Bluetooth connection time latency, everything using asyncio
is a bit more responsive as a result.

* relo per comments

* fix too fast by adding resolution, ensure monotonic time is patchable by freezegun

* fix test that freezes time too late and has a race loop
This commit is contained in:
J. Nick Koston 2023-08-13 19:37:45 -05:00 committed by GitHub
parent 07e20e1eab
commit 790c1bc251
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 22 additions and 4 deletions

View file

@ -8,6 +8,7 @@ import logging
import os
import subprocess
import threading
from time import monotonic
import traceback
from typing import Any
@ -114,6 +115,10 @@ class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
loop.set_default_executor = warn_use( # type: ignore[method-assign]
loop.set_default_executor, "sets default executor on the event loop"
)
# bind the built-in time.monotonic directly as loop.time to avoid the
# overhead of the additional method call since its the most called loop
# method and its roughly 10%+ of all the call time in base_events.py
loop.time = monotonic # type: ignore[method-assign]
return loop

View file

@ -420,6 +420,9 @@ def async_fire_time_changed(
_async_fire_time_changed(hass, utc_datetime, fire_all)
_MONOTONIC_RESOLUTION = time.get_clock_info("monotonic").resolution
@callback
def _async_fire_time_changed(
hass: HomeAssistant, utc_datetime: datetime | None, fire_all: bool
@ -432,7 +435,7 @@ def _async_fire_time_changed(
continue
mock_seconds_into_future = timestamp - time.time()
future_seconds = task.when() - hass.loop.time()
future_seconds = task.when() - (hass.loop.time() + _MONOTONIC_RESOLUTION)
if fire_all or mock_seconds_into_future >= future_seconds:
with patch(

View file

@ -8,6 +8,7 @@ from typing import Any
from unittest.mock import patch
from freezegun import freeze_time
import pytest
from homeassistant import config as hass_config
from homeassistant.components.recorder import Recorder
@ -1286,12 +1287,14 @@ async def test_initialize_from_database(
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.CELSIUS
@pytest.mark.freeze_time(
datetime(dt_util.utcnow().year + 1, 8, 2, 12, 23, 42, tzinfo=dt_util.UTC)
)
async def test_initialize_from_database_with_maxage(
recorder_mock: Recorder, hass: HomeAssistant
) -> None:
"""Test initializing the statistics from the database."""
now = dt_util.utcnow()
current_time = datetime(now.year + 1, 8, 2, 12, 23, 42, tzinfo=dt_util.UTC)
current_time = dt_util.utcnow()
# Testing correct retrieval from recorder, thus we do not
# want purging to occur within the class itself.

View file

@ -2,8 +2,9 @@
from __future__ import annotations
import datetime
import time
from homeassistant import util
from homeassistant import runner, util
from homeassistant.util import dt as dt_util
@ -12,5 +13,11 @@ def _utcnow() -> datetime.datetime:
return datetime.datetime.now(datetime.UTC)
def _monotonic() -> float:
"""Make monotonic patchable by freezegun."""
return time.monotonic()
dt_util.utcnow = _utcnow # type: ignore[assignment]
util.utcnow = _utcnow # type: ignore[assignment]
runner.monotonic = _monotonic # type: ignore[assignment]