(core) Cache converting timestamp to date

Summary: Adds `@functools.lru_cache` to `ts_to_dt` and `ts_to_date` which are used in `_make_rich_value` in Date/DateTime columns, i.e. every time such a column is accessed in a formula. I noticed that these operations are surprisingly expensive while working on https://phab.getgrist.com/D4075. This is just an easy way to potentially significantly speed up certain docs and formulas.

Test Plan:
Put this code in an engine test case:

```
def test_(self):
  self.apply_user_action(["AddTable", "Table1", [
    {"id": "A", "type": "DateTime:America/Chicago"},
  ]])
  self.add_records("Table1", ["A"], [
    [i] for i in range(1000)
  ])
  formula = "for _ in range(1000): $A\nreturn 1"
  import time
  start = time.time()
  self.add_column(
    "Table1", formula, type="Any", isFormula=True, formula=formula
  )
  elapsed = time.time() - start
  print(f"Took {elapsed:.2f} for formula {formula}")
```

The time goes from ~10s to ~1s. Similar tests showed no noticeable slowdown when caching had no effect.

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D4099
This commit is contained in:
Alex Hall 2023-10-27 13:17:29 +02:00
parent eea9f47e50
commit fa6b57855e

View File

@ -8,6 +8,12 @@ import iso8601
import six import six
from six.moves import zip from six.moves import zip
try:
from functools import lru_cache
except ImportError:
from backports.functools_lru_cache import lru_cache # noqa
# This is prepared by sandbox/install_tz.py # This is prepared by sandbox/install_tz.py
ZoneRecord = namedtuple("ZoneRecord", ("name", "abbrs", "offsets", "untils")) ZoneRecord = namedtuple("ZoneRecord", ("name", "abbrs", "offsets", "untils"))
@ -45,6 +51,7 @@ def utc_to_ts_ms(dt):
# Converts timestamp in seconds to datetime in the given timezone. If tzinfo is given, then zone # Converts timestamp in seconds to datetime in the given timezone. If tzinfo is given, then zone
# is ignored and may be None. # is ignored and may be None.
@lru_cache(maxsize=1024)
def ts_to_dt(timestamp, zone, tzinfo=None): def ts_to_dt(timestamp, zone, tzinfo=None):
return (EPOCH_UTC + timedelta(seconds=timestamp)).astimezone(tzinfo or zone.get_tzinfo(None)) return (EPOCH_UTC + timedelta(seconds=timestamp)).astimezone(tzinfo or zone.get_tzinfo(None))
@ -57,6 +64,7 @@ def dt_to_ts(dt, timezone=None):
return (dt.replace(tzinfo=None) - offset - EPOCH).total_seconds() return (dt.replace(tzinfo=None) - offset - EPOCH).total_seconds()
# Converts timestamp in seconds to date. # Converts timestamp in seconds to date.
@lru_cache(maxsize=1024)
def ts_to_date(timestamp): def ts_to_date(timestamp):
return DATE_EPOCH + timedelta(seconds=timestamp) return DATE_EPOCH + timedelta(seconds=timestamp)