From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 79A206EC40; Thu, 19 Aug 2021 05:58:58 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 79A206EC40 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1629341938; bh=XWLX0wa/W+DQLDVnSOWaVZWVSRcn6IquCqyPD1HCkuI=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=kg46uRTixzn7egxLy0o0YSrt/Ur4uQOM2dUTsSaYO3tuh3mHIcQ2q+2p/qqrJA8ng ocRgPxvBiDNGMwHHoZi/twUBf6zOB4PWsaK1Ba1FQojBBDkHsQgK7vnHHR+sLvOAcS qH36j0LkmSPBYkK9JS0t58yVgWQKA26nBvyVpX6w= Received: from smtp33.i.mail.ru (smtp33.i.mail.ru [94.100.177.93]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 89F3F6EC5B for ; Thu, 19 Aug 2021 05:56:59 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 89F3F6EC5B Received: by smtp33.i.mail.ru with esmtpa (envelope-from ) id 1mGYEj-00022X-Ks; Thu, 19 Aug 2021 05:56:58 +0300 To: vdavydov@tarantool.org, sergepetrenko@tarantool.org, tarantool-patches@dev.tarantool.org Date: Thu, 19 Aug 2021 05:56:32 +0300 Message-Id: X-Mailer: git-send-email 2.29.2 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-4EC0790: 10 X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD92087353F0EC44DD9F9A2272A1D086A28553D1D5C4B4124EF182A05F538085040338806911685D51986F0EC11C29A0ABE3A256C1667523BD17F45243AC8711F81 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE73C714006C69EB7BAEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637BA5555A0C16230F4EA1F7E6F0F101C6723150C8DA25C47586E58E00D9D99D84E1BDDB23E98D2D38BBCA57AF85F7723F2A7159994677362899EE6F40DAC432E80CC7F00164DA146DAFE8445B8C89999728AA50765F7900637F6B57BC7E64490618DEB871D839B7333395957E7521B51C2DFABB839C843B9C08941B15DA834481F8AA50765F7900637F6B57BC7E6449061A352F6E88A58FB86F5D81C698A659EA73AA81AA40904B5D9A18204E546F3947C30C7C91C97108465AD7EC71F1DB884274AD6D5ED66289B52698AB9A7B718F8C46E0066C2D8992A16725E5C173C3A84C3A2D5570B22232E1E76E601842F6C81A1F004C906525384307823802FF610243DF43C7A68FF6260569E8FC8737B5C2249EC8D19AE6D49635B68655334FD4449CB9ECD01F8117BC8BEAAAE862A0553A39223F8577A6DFFEA7CB1724D34C644744043847C11F186F3C59DAA53EE0834AAEE X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A213B5FB47DCBC3458834459D11680B505C71AF2EC926D347B1C7D7DED1BC007C0 X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CF160826E4E1956AECF8169106A4811B94DA61ACAF85FAE159C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EF5A3EDA775A1E0ED0699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34B7CBFF60649FF2662DE2C339FD166A671B9AEEF1EDD3B96EF4541A4FEEDF203EEB3D76975EC30E561D7E09C32AA3244CDA20CBF8602F6595BD03750C3D5AE55CB018FE5BB746DCD1AD832FF50B3043B1 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojGSxK+6r6oBFwQ6KOO3hADg== X-Mailru-Sender: 6CA451E36783D721CBEA96CEA26D325DCC93EDFF14F7BBAC2497A523DB2CD057B7CBEF92542CD7C82F97C478340294DCC77752E0C033A69E0F0C7111264B8915FF1320A92A5534336C18EFA0BB12DBB0 X-Mras: Ok Subject: [Tarantool-patches] [PATCH v6 4/5] datetime: perf test for datetime parser X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Timur Safin via Tarantool-patches Reply-To: Timur Safin Cc: v.shpilevoy@tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" It was told that if field `datetime.secs` would be `double` we should get better performance in LuaJIT instead of `uint64_t` type, which is used at the moment. So we have created benchmark, which was comparing implementations of functions from `datetime.c` if we would use `double` or `int64_t` for `datetime.secs` field. Despite expectations, based on prior experience with floaing-point on x86 processors, comparison shows that `double` provides similar or sometimes better timings. And picture stays consistent be it SSE2, AVX1 or AVX2 code. Part of #5941 --- perf/CMakeLists.txt | 3 + perf/datetime-common.h | 105 +++++++++++++++++++ perf/datetime-compare.cc | 213 +++++++++++++++++++++++++++++++++++++++ perf/datetime-parser.cc | 105 +++++++++++++++++++ 4 files changed, 426 insertions(+) create mode 100644 perf/datetime-common.h create mode 100644 perf/datetime-compare.cc create mode 100644 perf/datetime-parser.cc diff --git a/perf/CMakeLists.txt b/perf/CMakeLists.txt index 3651de5b4..b5d7caf81 100644 --- a/perf/CMakeLists.txt +++ b/perf/CMakeLists.txt @@ -12,3 +12,6 @@ include_directories(${CMAKE_SOURCE_DIR}/third_party) add_executable(tuple.perftest tuple.cc) target_link_libraries(tuple.perftest core box tuple benchmark::benchmark) + +add_executable(datetime.perftest datetime-parser.cc datetime-compare.cc) +target_link_libraries(datetime.perftest cdt core benchmark::benchmark) diff --git a/perf/datetime-common.h b/perf/datetime-common.h new file mode 100644 index 000000000..6fd4e1e3b --- /dev/null +++ b/perf/datetime-common.h @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include "dt.h" +#include "datetime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static const char sample[] = "2012-12-24T15:30Z"; + +#define S(s) \ + { s, sizeof(s) - 1 } + +static struct +{ + const char *sz; + size_t len; +} tests[] = { + S("2012-12-24 15:30Z"), + S("2012-12-24 15:30z"), + S("2012-12-24 15:30"), + S("2012-12-24 16:30+01:00"), + S("2012-12-24 16:30+0100"), + S("2012-12-24 16:30+01"), + S("2012-12-24 14:30-01:00"), + S("2012-12-24 14:30-0100"), + S("2012-12-24 14:30-01"), + S("2012-12-24 15:30:00Z"), + S("2012-12-24 15:30:00z"), + S("2012-12-24 15:30:00"), + S("2012-12-24 16:30:00+01:00"), + S("2012-12-24 16:30:00+0100"), + S("2012-12-24 14:30:00-01:00"), + S("2012-12-24 14:30:00-0100"), + S("2012-12-24 15:30:00.123456Z"), + S("2012-12-24 15:30:00.123456z"), + S("2012-12-24 15:30:00.123456"), + S("2012-12-24 16:30:00.123456+01:00"), + S("2012-12-24 16:30:00.123456+01"), + S("2012-12-24 14:30:00.123456-01:00"), + S("2012-12-24 14:30:00.123456-01"), + S("2012-12-24t15:30Z"), + S("2012-12-24t15:30z"), + S("2012-12-24t15:30"), + S("2012-12-24t16:30+01:00"), + S("2012-12-24t16:30+0100"), + S("2012-12-24t14:30-01:00"), + S("2012-12-24t14:30-0100"), + S("2012-12-24t15:30:00Z"), + S("2012-12-24t15:30:00z"), + S("2012-12-24t15:30:00"), + S("2012-12-24t16:30:00+01:00"), + S("2012-12-24t16:30:00+0100"), + S("2012-12-24t14:30:00-01:00"), + S("2012-12-24t14:30:00-0100"), + S("2012-12-24t15:30:00.123456Z"), + S("2012-12-24t15:30:00.123456z"), + S("2012-12-24t16:30:00.123456+01:00"), + S("2012-12-24t14:30:00.123456-01:00"), + S("2012-12-24 16:30 +01:00"), + S("2012-12-24 14:30 -01:00"), + S("2012-12-24 15:30 UTC"), + S("2012-12-24 16:30 UTC+1"), + S("2012-12-24 16:30 UTC+01"), + S("2012-12-24 16:30 UTC+0100"), + S("2012-12-24 16:30 UTC+01:00"), + S("2012-12-24 14:30 UTC-1"), + S("2012-12-24 14:30 UTC-01"), + S("2012-12-24 14:30 UTC-01:00"), + S("2012-12-24 14:30 UTC-0100"), + S("2012-12-24 15:30 GMT"), + S("2012-12-24 16:30 GMT+1"), + S("2012-12-24 16:30 GMT+01"), + S("2012-12-24 16:30 GMT+0100"), + S("2012-12-24 16:30 GMT+01:00"), + S("2012-12-24 14:30 GMT-1"), + S("2012-12-24 14:30 GMT-01"), + S("2012-12-24 14:30 GMT-01:00"), + S("2012-12-24 14:30 GMT-0100"), + S("2012-12-24 14:30 -01:00"), + S("2012-12-24 16:30:00 +01:00"), + S("2012-12-24 14:30:00 -01:00"), + S("2012-12-24 16:30:00.123456 +01:00"), + S("2012-12-24 14:30:00.123456 -01:00"), + S("2012-12-24 15:30:00.123456 -00:00"), + S("20121224T1630+01:00"), + S("2012-12-24T1630+01:00"), + S("20121224T16:30+01"), + S("20121224T16:30 +01"), +}; +#undef S + +#define DIM(a) (sizeof(a) / sizeof(a[0])) + +int +parse_datetime(const char *str, size_t len, int64_t *sp, int32_t *np, + int32_t *op); + +#ifdef __cplusplus +} +#endif diff --git a/perf/datetime-compare.cc b/perf/datetime-compare.cc new file mode 100644 index 000000000..5096eb987 --- /dev/null +++ b/perf/datetime-compare.cc @@ -0,0 +1,213 @@ +#include "dt.h" +#include +#include +#include + +#include "datetime-common.h" + +template +struct datetime_bench +{ + T secs; + uint32_t nsec; + uint32_t offset; + +static struct datetime_bench date_array[]; +}; +template +struct datetime_bench datetime_bench::date_array[DIM(tests)]; + +/// Parse 70 datetime literals of various lengths +template +static void +Assign70() +{ + size_t index; + int64_t secs_expected; + int nanosecs; + int ofs; + using dt_bench = datetime_bench; + + for (index = 0; index < DIM(tests); index++) { + int64_t secs; + int rc = parse_datetime(tests[index].sz, tests[index].len, + &secs, &nanosecs, &ofs); + assert(rc == 0); + dt_bench::date_array[index].secs = (T)secs; + dt_bench::date_array[index].nsec = nanosecs; + dt_bench::date_array[index].offset = ofs; + } +} + +template +static void +DateTime_Assign70(benchmark::State &state) +{ + for (auto _ : state) + Assign70(); +} +BENCHMARK_TEMPLATE1(DateTime_Assign70, uint64_t); +BENCHMARK_TEMPLATE1(DateTime_Assign70, double); + +#define COMPARE_RESULT_BENCH(a, b) (a < b ? -1 : a > b) + +template +int datetime_compare(const struct datetime_bench *lhs, + const struct datetime_bench *rhs) +{ + int result = COMPARE_RESULT_BENCH(lhs->secs, rhs->secs); + if (result != 0) + return result; + + return COMPARE_RESULT_BENCH(lhs->nsec, rhs->nsec); +} + +template +static void +AssignCompare70() +{ + size_t index; + int nanosecs; + int ofs; + using dt_bench = datetime_bench; + + size_t arrays_sz = DIM(tests); + for (index = 0; index < arrays_sz; index++) { + int64_t secs; + int rc = parse_datetime(tests[index].sz, tests[index].len, + &secs, &nanosecs, &ofs); + assert(rc == 0); + dt_bench::date_array[index].secs = (T)secs; + dt_bench::date_array[index].nsec = nanosecs; + dt_bench::date_array[index].offset = ofs; + } + + for (index = 0; index < (arrays_sz - 1); index++) { + volatile int rc = datetime_compare(&dt_bench::date_array[index], + &dt_bench::date_array[index + 1]); + assert(rc == 0 || rc == -1 || rc == 1); + } +} + +template +static void +DateTime_AssignCompare70(benchmark::State &state) +{ + for (auto _ : state) + AssignCompare70(); +} +BENCHMARK_TEMPLATE1(DateTime_AssignCompare70, uint64_t); +BENCHMARK_TEMPLATE1(DateTime_AssignCompare70, double); + +template +static void +Compare20() +{ + size_t index; + int nanosecs; + int ofs; + using dt_bench = datetime_bench; + + for (size_t i = 0; i < 10; i++) { + volatile int rc = datetime_compare(&dt_bench::date_array[i], + &dt_bench::date_array[32 + i]); + assert(rc == 0 || rc == -1 || rc == 1); + } +} + +template +static void +DateTime_Compare20(benchmark::State &state) +{ + for (auto _ : state) + Compare20(); +} +BENCHMARK_TEMPLATE1(DateTime_Compare20, uint64_t); +BENCHMARK_TEMPLATE1(DateTime_Compare20, double); + + +#define SECS_EPOCH_1970_OFFSET ((int64_t)DT_EPOCH_1970_OFFSET * SECS_PER_DAY) + +template +int +datetime_to_string(const struct datetime_bench *date, char *buf, uint32_t len) +{ +#define ADVANCE(sz) \ + if (buf != NULL) { \ + buf += sz; \ + len -= sz; \ + } \ + ret += sz; + + int offset = date->offset; + /* for negative offsets around Epoch date we could get + * negative secs value, which should be attributed to + * 1969-12-31, not 1970-01-01, thus we first shift + * epoch to Rata Die then divide by seconds per day, + * not in reverse + */ + int64_t secs = (int64_t)date->secs + offset * 60 + SECS_EPOCH_1970_OFFSET; + assert((secs / SECS_PER_DAY) <= INT_MAX); + dt_t dt = dt_from_rdn(secs / SECS_PER_DAY); + + int year, month, day, sec, ns, sign; + dt_to_ymd(dt, &year, &month, &day); + + int hour = (secs / 3600) % 24, + minute = (secs / 60) % 60; + sec = secs % 60; + ns = date->nsec; + + int ret = 0; + uint32_t sz = snprintf(buf, len, "%04d-%02d-%02dT%02d:%02d", + year, month, day, hour, minute); + ADVANCE(sz); + if (sec || ns) { + sz = snprintf(buf, len, ":%02d", sec); + ADVANCE(sz); + if (ns) { + if ((ns % 1000000) == 0) + sz = snprintf(buf, len, ".%03d", ns / 1000000); + else if ((ns % 1000) == 0) + sz = snprintf(buf, len, ".%06d", ns / 1000); + else + sz = snprintf(buf, len, ".%09d", ns); + ADVANCE(sz); + } + } + if (offset == 0) { + sz = snprintf(buf, len, "Z"); + ADVANCE(sz); + } + else { + if (offset < 0) + sign = '-', offset = -offset; + else + sign = '+'; + + sz = snprintf(buf, len, "%c%02d:%02d", sign, offset / 60, offset % 60); + ADVANCE(sz); + } + return ret; +} +#undef ADVANCE + +template +static void +ToString1() +{ + char buf[48]; + struct datetime_bench dateval = datetime_bench::date_array[13]; + + volatile auto len = datetime_to_string(&dateval, buf, sizeof(buf)); +} + +template +static void +DateTime_ToString1(benchmark::State &state) +{ + for (auto _ : state) + ToString1(); +} +BENCHMARK_TEMPLATE1(DateTime_ToString1, uint64_t); +BENCHMARK_TEMPLATE1(DateTime_ToString1, double); diff --git a/perf/datetime-parser.cc b/perf/datetime-parser.cc new file mode 100644 index 000000000..61557fe8f --- /dev/null +++ b/perf/datetime-parser.cc @@ -0,0 +1,105 @@ +#include "dt.h" +#include +#include + +#include "datetime-common.h" + +/* p5-time-moment/src/moment_parse.c: parse_string_lenient() */ +int +parse_datetime(const char *str, size_t len, int64_t *sp, int32_t *np, + int32_t *op) +{ + size_t n; + dt_t dt; + char c; + int sod = 0, nanosecond = 0, offset = 0; + + n = dt_parse_iso_date(str, len, &dt); + if (!n) + return 1; + if (n == len) + goto exit; + + c = str[n++]; + if (!(c == 'T' || c == 't' || c == ' ')) + return 1; + + str += n; + len -= n; + + n = dt_parse_iso_time(str, len, &sod, &nanosecond); + if (!n) + return 1; + if (n == len) + goto exit; + + if (str[n] == ' ') + n++; + + str += n; + len -= n; + + n = dt_parse_iso_zone_lenient(str, len, &offset); + if (!n || n != len) + return 1; + +exit: + *sp = ((int64_t)dt_rdn(dt) - 719163) * 86400 + sod - offset * 60; + *np = nanosecond; + *op = offset; + + return 0; +} + +/// Parse 70 datetime literals of various lengths +static void +ParseTimeStamps() +{ + size_t index; + int64_t secs_expected; + int nanosecs; + int ofs; + parse_datetime(sample, sizeof(sample) - 1, &secs_expected, + &nanosecs, &ofs); + + for (index = 0; index < DIM(tests); index++) + { + int64_t secs; + int rc = parse_datetime(tests[index].sz, tests[index].len, + &secs, &nanosecs, &ofs); + assert(rc == 0); + assert(secs == secs_expected); + } +} + +static void +CDT_Parse70(benchmark::State &state) +{ + for (auto _ : state) + ParseTimeStamps(); +} +BENCHMARK(CDT_Parse70); + +/// Parse single datetime literal of longest length +static void +Parse1() +{ + const char civil_string[] = "2015-02-18T10:50:31.521345123+10:00"; + int64_t secs; + int nanosecs; + int ofs; + int rc = parse_datetime(civil_string, sizeof(civil_string) - 1, + &secs, &nanosecs, &ofs); + assert(rc == 0); + assert(nanosecs == 521345123); +} + +static void +CDT_Parse1(benchmark::State &state) +{ + for (auto _ : state) + Parse1(); +} +BENCHMARK(CDT_Parse1); + +BENCHMARK_MAIN(); -- 2.29.2