aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorswarmer <swarmer@yandex-team.com>2025-02-10 23:38:45 +0300
committerswarmer <swarmer@yandex-team.com>2025-02-11 00:10:29 +0300
commit17cc83379606b885d021e15a7cf5941210eba0b5 (patch)
tree83dc9cbcbcdeb9969a10bf33322b93b5e8b8ff0e
parentcc4962f760e1d454f0ce1dbe54ed92f9bf7eaf6a (diff)
downloadydb-17cc83379606b885d021e15a7cf5941210eba0b5.tar.gz
[util] GmTimeR: use LUT for the tm_yday -> tm_mday conversion
Replacing binary search with a look-up table. The performance of which does not depend on the branch predictor, and conversion works equally fast for any dates. commit_hash:fefe9665d0d4b51c2ae09ec2b2816aed30caa57b
-rw-r--r--util/datetime/systime.cpp101
1 files changed, 31 insertions, 70 deletions
diff --git a/util/datetime/systime.cpp b/util/datetime/systime.cpp
index ade1737168..a2dd48e3b0 100644
--- a/util/datetime/systime.cpp
+++ b/util/datetime/systime.cpp
@@ -102,73 +102,36 @@ namespace {
constexpr ui16 MONTH_TO_DAYS_LEAP[12] = {
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
- template <ui8 DaysInFeb>
- constexpr int DayOfYearToMonth(ui64& day) {
- Y_ASSERT(day < 366);
-
- constexpr ui8 JanDays = 31;
- constexpr ui8 FebDays = JanDays + DaysInFeb;
- constexpr ui8 MarDays = FebDays + 31;
- constexpr ui8 AprDays = MarDays + 30;
- constexpr ui8 MayDays = AprDays + 31;
- constexpr ui8 JunDays = MayDays + 30;
- constexpr ui8 JulDays = JunDays + 31;
- constexpr ui16 AugDays = JulDays + 31;
- constexpr ui16 SepDays = AugDays + 30;
- constexpr ui16 OctDays = SepDays + 31;
- constexpr ui16 NovDays = OctDays + 30;
-
- // hard-coded binary search
- // this approach is faster that lookup in array using std::lower_bound()
- // GmTimeR takes ~40 cycles vs ~60 cycles using std::lower_bound version
- if (day < JunDays) {
- if (day < MarDays) {
- if (day < JanDays) {
- return 0;
- } else if (day < FebDays) {
- day -= JanDays;
- return 1;
- } else {
- day -= FebDays;
- return 2;
- }
- } else {
- if (day < AprDays) {
- day -= MarDays;
- return 3;
- } else if (day < MayDays) {
- day -= AprDays;
- return 4;
- } else {
- day -= MayDays;
- return 5;
- }
- }
- } else {
- if (day < SepDays) {
- if (day < JulDays) {
- day -= JunDays;
- return 6;
- } else if (day < AugDays) {
- day -= JulDays;
- return 7;
- } else {
- day -= AugDays;
- return 8;
- }
- } else {
- if (day < OctDays) {
- day -= SepDays;
- return 9;
- } else if (day < NovDays) {
- day -= OctDays;
- return 10;
- } else {
- day -= NovDays;
- return 11;
- }
- }
- }
+ struct TMonth32LUT {
+ char LastMonthDay32[12];
+ char FirstMonthDay32[12];
+ };
+
+ constexpr TMonth32LUT MONTH_32_LUT[2] = {
+ {
+ // common year
+ .LastMonthDay32 = {31, 27, 26, 24, 23, 21, 20, 19, 17, 16, 14, 13},
+ .FirstMonthDay32 = {0, 1, 5, 6, 8, 9, 11, 12, 13, 15, 16, 18},
+ },
+ {
+ // leap year
+ .LastMonthDay32 = {31, 28, 27, 25, 24, 22, 21, 20, 18, 17, 15, 14},
+ .FirstMonthDay32 = {0, 1, 4, 5, 7, 8, 10, 11, 12, 14, 15, 17},
+ },
+ };
+
+ constexpr int DayOfYearToMonth(ui64& yearDay, bool leapYear) {
+ Y_ASSERT(yearDay < 366);
+ int approxMonth = yearDay / 32;
+ int approxMDay = yearDay % 32;
+ int dayThreshold = MONTH_32_LUT[leapYear].LastMonthDay32[approxMonth];
+ int currentMonthMDayOffset = MONTH_32_LUT[leapYear].FirstMonthDay32[approxMonth];
+ bool nextMonth = (approxMDay >= dayThreshold);
+ int dayCorrection = nextMonth ? -dayThreshold : currentMonthMDayOffset;
+ int day = approxMDay + dayCorrection;
+ int month = approxMonth + nextMonth;
+ yearDay = day;
+ return month;
}
class TDayNoToYearLookupTable {
@@ -286,9 +249,7 @@ struct tm* GmTimeR(const time_t* timer, struct tm* tmbuf) {
tmbuf->tm_year = year - STRUCT_TM_BASE_YEAR;
tmbuf->tm_yday = dayno;
- tmbuf->tm_mon = isLeapYear
- ? DayOfYearToMonth<29>(dayno)
- : DayOfYearToMonth<28>(dayno);
+ tmbuf->tm_mon = DayOfYearToMonth(dayno, isLeapYear);
tmbuf->tm_mday = dayno + 1;
tmbuf->tm_isdst = 0;
#ifndef _win_