From fa6b57855e78a2edfb7a8e9083fbd9ffada532c9 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Fri, 27 Oct 2023 13:17:29 +0200 Subject: [PATCH] (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 --- sandbox/grist/moment.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sandbox/grist/moment.py b/sandbox/grist/moment.py index d7cb3fa5..c3ff0533 100644 --- a/sandbox/grist/moment.py +++ b/sandbox/grist/moment.py @@ -8,6 +8,12 @@ import iso8601 import six 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 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 # is ignored and may be None. +@lru_cache(maxsize=1024) def ts_to_dt(timestamp, zone, 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() # Converts timestamp in seconds to date. +@lru_cache(maxsize=1024) def ts_to_date(timestamp): return DATE_EPOCH + timedelta(seconds=timestamp)