From 41000e0be372912ef31415e01feef6db56abc399 Mon Sep 17 00:00:00 2001 From: junhangis Date: Tue, 26 Jul 2022 16:43:01 +0800 Subject: [PATCH] =?UTF-8?q?improve=20the=20performance=20of=20now()=C2=A3?= =?UTF-8?q?=C2=ACcurrent=5Ftime,=20current=5Fdate,=20etc.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CREATE or replace PROCEDURE t_now(num integer) AS DECLARE n numeric :=0; curtime timestamp; BEGIN LOOP n := n + 1; curtime := now(); EXIT WHEN n > num; END LOOP; END; / before optimization: openGauss=# call t_now(1000000); t_now ------- (1 row) Time: 4714.785 ms after optimization: openGauss=# call t_now(1000000); t_now ------- (1 row) Time: 4575.570 ms --- src/common/backend/utils/adt/datetime.cpp | 47 ++++++++++++++++++++--- src/include/knl/knl_session.h | 7 ++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/common/backend/utils/adt/datetime.cpp b/src/common/backend/utils/adt/datetime.cpp index f8ae0f43c..39bfbced5 100644 --- a/src/common/backend/utils/adt/datetime.cpp +++ b/src/common/backend/utils/adt/datetime.cpp @@ -348,15 +348,52 @@ void GetCurrentDateTime(struct pg_tm* tm) * * Get the transaction start time ("now()") broken down as a struct pg_tm, * including fractional seconds and timezone offset. + * + * Internally, we cache the result, since this could be called many times + * in a transaction, within which now() doesn't change. */ void GetCurrentTimeUsec(struct pg_tm* tm, fsec_t* fsec, int* tzp) { - int tz; + TimestampTz cur_ts = GetCurrentTransactionStartTimestamp(); - timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, fsec, NULL, NULL); - /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */ - if (tzp != NULL) - *tzp = tz; + /* + * The cache key must include both current time and current timezone. + * By representing the timezone by just a pointer, we're assuming that + * distinct timezone settings could never have the same pointer value. + * This is true by virtue of the hashtable used inside pg_tzset(); + * however, it might need another look if we ever allow entries in that + * hash to be recycled. + */ + if (cur_ts != u_sess->cache_ts || session_timezone != u_sess->cache_timezone) { + /* + * Make sure cache is marked invalid in case of error after partial + * update within timestamp2tm. + */ + u_sess->cache_timezone = NULL; + + /* + * Perform the computation, storing results into cache. We do not + * really expect any error here, since current time surely ought to be + * within range, but check just for sanity's sake. + */ + if (timestamp2tm(cur_ts, + &u_sess->cache_tz, &u_sess->cache_tm, &u_sess->cache_fsec, + NULL, session_timezone) != 0) { + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + } + + /* OK, so mark the cache valid. */ + u_sess->cache_ts = cur_ts; + u_sess->cache_timezone = session_timezone; + } + + *tm = u_sess->cache_tm; + *fsec = u_sess->cache_fsec; + if (tzp != NULL) { + *tzp = u_sess->cache_tz; + } } /* TrimTrailingZeros() diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 57c98dc46..8363217f0 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -2772,6 +2772,13 @@ typedef struct knl_session_context { struct knl_u_clientConnTime_context clientConnTime_cxt; knl_u_hook_context hook_cxt; + + /* The datetime cache in current transaction. */ + TimestampTz cache_ts = 0; + pg_tz* cache_timezone = NULL; + struct pg_tm cache_tm; + fsec_t cache_fsec; + int cache_tz; } knl_session_context; enum stp_xact_err_type {