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 113616EC40; Sun, 15 Aug 2021 23:53:03 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 113616EC40 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1629060783; bh=MYzRh8nmuT/8O7U3ajFpCTE6GorZA1fVYU3j3Rp+XY0=; h=To:Cc:References:Date:In-Reply-To:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=nGi9p70XsMrMb//wDRqzh6ACAXtjYZB7FDYNskZnk3+6XbhO5lo4buuaZO4OjpjBA 89SJUQy9Y7nz0Tp7MbdqG0s8QejHG73Dym/iCcL3LDVoiF2XZq6mGL+Qv+m01284RM RuGqux2Xql/5X+0uMJ2CWBZb6tBftkCnfW4krvuw= Received: from smtp50.i.mail.ru (smtp50.i.mail.ru [94.100.177.110]) (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 D5D196EC40 for ; Sun, 15 Aug 2021 23:53:01 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org D5D196EC40 Received: by smtp50.i.mail.ru with esmtpa (envelope-from ) id 1mFN7s-00039Z-ED; Sun, 15 Aug 2021 23:53:01 +0300 To: Igor Munkin , Vladislav Shpilevoy Cc: tarantool-patches@dev.tarantool.org References: <449cb4c7-c738-ed07-8517-61b663ad9f79@tarantool.org> <0931c9db-bd8e-0687-2c96-73fac1aeff7e@tarantool.org> <18971894-830a-6bbd-a87b-83cd6abe5f34@tarantool.org> <3349aff9-a95c-cab5-4610-ca1089c1e762@tarantool.org> <8e779f32-f4e0-f3ac-45c3-c976f32bfd75@tarantool.org> <20210810122147.GK27855@tarantool.org> Message-ID: Date: Sun, 15 Aug 2021 23:52:50 +0300 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 8bit X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD92087353F0EC44DD910164DC12A5633065676A9727AC27C74182A05F538085040F2172462662840B21EFCFAD047F99722F09DAF95298FE2EC8F54CFC5DD61D080 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7E9C44F3538ABC873EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F79006375D8840FA58F505298638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D8D195D991D321FBA4630A3707C48C5D8E117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EE0AC5B80A05675ACDE21AE983DBD7FFC1D8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE3A7DFDF579AB090EF03F1AB874ED89028C4224003CC836476EA7A3FFF5B025636E2021AF6380DFAD1A18204E546F3947CB11811A4A51E3B096D1867E19FE1407959CC434672EE6371089D37D7C0E48F6C8AA50765F790063757B1FBEA53BC6EDBEFF80C71ABB335746BA297DBC24807EABDAD6C7F3747799A X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A213B5FB47DCBC3458F0AFF96BAACF4158235E5A14AD4A4A4625E192CAD1D9E79D0B18DC6AC13D9A1C2AE4BC0BD50AE757 X-C1DE0DAB: 0D63561A33F958A5211B0D8A955D608FA263F372DEFDEA267BB08989DD32835AD59269BC5F550898D99A6476B3ADF6B47008B74DF8BB9EF7333BD3B22AA88B938A852937E12ACA752DA3D96DA0CEF5C48E8E86DC7131B365E7726E8460B7C23C X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34C1E32F4AD4B2486B4810608AF7CA23EF2F68A816318ABD035575BDFF315836E1D77591525A9A4E861D7E09C32AA3244C0058EBD441C30606A8D50CBD22863787725D5B54B2FE4575FACE5A9C96DEB163 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojVT2don9h9KqT3UHS/d+U4g== X-Mailru-Sender: B5B6A6EBBD94DAD833E4B9A85F720031C08955F448C60A3B7C8B8D187FDC5560875AFC7E92F41B061EC9E4A2C82A33BC8C24925A86E657CE0C70AEE3C9A96FBAB3D7EE8ED63280BE112434F685709FCF0DA7A0AF5A3A8387 X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH v3 2/9] lua: built-in module datetime 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: Safin Timur via Tarantool-patches Reply-To: Safin Timur Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" There are updates with jot.on() vs jit.off() numbers, and further investigations for C benchmarks of double vs int64_t, please see below... On 12.08.2021 23:47, Safin Timur via Tarantool-patches wrote: > > > On 10.08.2021 15:21, Igor Munkin wrote: >> Vlad, >> >> On 10.08.21, Vladislav Shpilevoy wrote: >>> Hi! Thanks for the fixes! >>> >>> On 08.08.2021 19:35, Safin Timur wrote: >>>> Much respect that you've found some time for review, even while >>>> being on vacation! Thanks! >>>> >>>> On 08.08.2021 14:26, Vladislav Shpilevoy wrote: >>>>>>>> +local datetime_index_handlers = { >>>>>>>> +    unixtime = function(self) >>>>>>>> +        return self.secs >>>>>>>> +    end, >>>>>>>> + >>>>>>>> +    timestamp = function(self) >>>>>>>> +        return tonumber(self.secs) + self.nsec / 1e9 >>>>>>> >>>>>>> 11. If you are saying the Lua number value range is enough, >>>>>>> why do you store them as 64bit integers in 'self' and >>>>>>> convert to a number only for the external API? >>>>>> >>>>>> May be I misunderstood your sentence, but let me elaborate here. >>>>>> I need seconds and nanoseconds kept separately for their efficient >>>>>> and precise handling, and for passing to c-dt. >>>>>> >>>>>> If we would keep 32-bit integers in seconds then we would be able >>>>>> to handle only dates upto 2038 year, thus we need 64-bit seconds >>>>>> for extended range. >>>>>> >>>>>> OTOH, not whole 64-bit range is practical, and required for >>>>>> expected in real-life datetime range values. It's not a problem >>>>>> that Lua number and int64_t have very different range for precise >>>>>> integer values. Lua number could handle integer values upto 9e15, >>>>>> which is corresponding to ... >>>>> >>>>> I know all that. The question is why do you keep storing cdata 64 >>>>> bits numbers >>>>> inside of the datetime objects, if you convert them all to Lua >>>>> numbers before >>>>> return? You could store self.secs as just number. And the other >>>>> fields too. Lua >>>>> number is double, it does not loose precision for integers < 2^53, >>>>> which should >>>>> be enough for the ranges you want to support. Correct? >>>> >>>> I keep using 64-bit because the primary code operating with fields >>>> is on C-side, and we need Lua number only on rare cases when user >>>> asked for composed attribute date.timestamp. Whenever we deal with >>>> seconds within arthmetic operations or transfer to c-dt, we need >>>> integer C type, larger than 32-bit. It's good fit for int64_t. >>> >>> But it is slower. Notably slower last time I benched, when I also >>> thought integers should be faster than doubles. But cdata 64 bit >>> integers >>> are slower than plain Lua numbers. Perhaps because they involve too much >>> work with metatables for everything. Besides, doubles are larger than 32 >>> bits - they are 53 bits until they start loosing int precision. And >>> it is >>> just enough for you, isn't it? >> >> Sorry for breaking into the party, but reasoning is much simpler: Lua >> numbers are just double values stored on the guest stack (or other >> TValue storage); cdata 64-bit integers are GCcdata objects, so like all >> others GC objects it has its lifetime, has to be allocated and freed and >> traversed by GC. You can consider them as GCstr except they are not >> interned. Hence, if the precision is fine (and it is AFAICS), then there >> is not need to use GCcdata instead of Lua native numbers. In other >> words, I totally agree with you. >> >>> >> >> >> >> > > > Either I do something totally wrong, or may be I'm absolutely clueless. > Oh rather both... > > Here is experiment I've proceed > https://gist.github.com/tsafin/f7f21aad53f23801839b3b278cfac380 > > I try to compare speed of accessing datetime.secs field: > - when it is current int64_t redirected via ffi to the `struct datetime`; > - when it is wrapped as (calculated) attribute of type double which we > access via FFI datetime_secs() function accessor; > - or when it's declared as `double` in the similar `struct > datetime_double`. > > No surpise that calculated attribute is 3x orders of magnitude slower > than direct ffi access to either int64_t or double field. > > OTOH, differences for timings to access to int64_t (which should be > boxed) and double (which should be unboxed) are negligible, and > essentially the same: > > ``` > ✔ ~/datetime/tarantoolt/build [tsafin/gh-5941-datetime-v4 ↑·4|✚ 3…4⚑ 3] > 23:40 $ ./src/tarantool ../../bench-datetime-secs.lua > ctype > date.secs       0.00035929679870605 > ctype > date.secsf      0.49544525146484 > ctype > date_double.secs        0.00042939186096191 > ✔ ~/datetime/tarantoolt/build [tsafin/gh-5941-datetime-v4 ↑·4|✚ 3…4⚑ 3] > 23:40 $ ./src/tarantool ../../bench-datetime-secs.lua > ctype > date.secs       0.00034856796264648 > ctype > date.secsf      0.40926361083984 > ctype > date_double.secs        0.00043344497680664 > ✔ ~/datetime/tarantoolt/build [tsafin/gh-5941-datetime-v4 ↑·4|✚ 3…4⚑ 3] > 23:40 $ ./src/tarantool ../../bench-datetime-secs.lua > ctype > date.secs       0.00034213066101074 > ctype > date.secsf      0.46818256378174 > ctype > date_double.secs        0.00037813186645508 > ✔ ~/datetime/tarantoolt/build [tsafin/gh-5941-datetime-v4 ↑·4|✚ 3…4⚑ 3] > 23:40 $ ./src/tarantool ../../bench-datetime-secs.lua > ctype > date.secs       0.00051259994506836 > ctype > date.secsf      0.6695671081543 > ctype > date_double.secs        0.00048208236694336 > ``` > > What did I do wrong? > > Thanks, > Timur After discussions with Igor it was decided to investiage differences in behavior of Lua code when jit.on() or off(). https://gist.github.com/tsafin/f7f21aad53f23801839b3b278cfac380#file-jit-on-vs-off-md jit.off() ========= ``` ✔ ~/datetime/tarantoolt/build [tsafin/msgpack-format L|✚ 1…6⚑ 3] 14:47 $ ./src/tarantool ../../bench-hash-dt.lua date.secs 0.25970935821533 date_double.secs 0.15149474143982 ✔ ~/datetime/tarantoolt/build [tsafin/msgpack-format L|✚ 1…6⚑ 3] 14:48 $ ./src/tarantool ../../bench-hash-dt.lua date.secs 0.33055567741394 date_double.secs 0.18803310394287 ✔ ~/datetime/tarantoolt/build [tsafin/msgpack-format L|✚ 1…6⚑ 3] 14:48 $ ./src/tarantool ../../bench-hash-dt.lua date.secs 0.29576468467712 date_double.secs 0.15472149848938 ✔ ~/datetime/tarantoolt/build [tsafin/msgpack-format L|✚ 1…6⚑ 3] 14:48 $ ./src/tarantool ../../bench-hash-dt.lua date.secs 0.2386200428009 date_double.secs 0.1287829875946 ``` jit.on() ======== ``` ✔ ~/datetime/tarantoolt/build [tsafin/msgpack-format L|✚ 1…6⚑ 3] 14:46 $ ./src/tarantool ../../bench-hash-dt.lua date.secs 0.0029795169830322 date_double.secs 0.00043296813964844 ✔ ~/datetime/tarantoolt/build [tsafin/msgpack-format L|✚ 1…6⚑ 3] 14:47 $ ./src/tarantool ../../bench-hash-dt.lua date.secs 0.00053858757019043 date_double.secs 0.00038242340087891 ✔ ~/datetime/tarantoolt/build [tsafin/msgpack-format L|✚ 1…6⚑ 3] 14:47 $ ./src/tarantool ../../bench-hash-dt.lua date.secs 0.00038433074951172 date_double.secs 0.00033235549926758 ✔ ~/datetime/tarantoolt/build [tsafin/msgpack-format L|✚ 1…6⚑ 3] 14:47 $ ./src/tarantool ../../bench-hash-dt.lua date.secs 0.0003509521484375 date_double.secs 0.00031447410583496 ``` i.e. for jit.on() mode there is no much difference in timings of boxed (ffi.int64_t) or unboxed (double) types used. Igor assumes that type narrowing was working well in this case. But with jit.off() we have dramatically diferent picture - double field is clearly faster than int64_t field. Upto 2x faster. So it was interesting to see how it looks from the other side - if we compare C code with structures using double instead of int64_t. Please see (shocking for me) results here - https://gist.github.com/tsafin/618fbf847d258f6e7f5a75fdf9ea945b Here is excerpt from result for SSE2 mode (which is default): | Test Name | Median | slower/faster | |---------------------------------------|---------|-----| | `DateTime_Assign70` | 5254 ns | - | | `DateTime_Assign70` | 4948 ns | + | | `DateTime_AssignCompare70` | 4948 ns | - | | `DateTime_AssignCompare70` | 4784 ns | + | | `DateTime_Compare20` | 4 ns | - | | `DateTime_Compare20` | 3 ns | + | | `DateTime_ToString1` | 515 ns | + | | `DateTime_ToString1` | 522 ns | - | Comparison shows that double provides similar or sometimes better timings, despite our prior experience with older x86 processors. (Be it SSE2, AVX1 or AVX2 code). The only benchmark which has been constantly slower with doubles - was stringization `DatetTime_ToString` which is consistently showing worse numbers with doubles than with int64s. But difference is very small and negligible in a longer run. So, I'm finally convinced (data-driven decision!) that double might be a good type for using within `datetime.secs`, noth from LuaJIT and C point of view. If you are curious enough, please see below the additional patch with benchmark code, which I've pushed to the branch... ----------------------------------------------------------------- commit f2080cc7df2567cf2ec66d8ad341572ae76a77a4 Author: Timur Safin Date: Sun Aug 15 17:19:26 2021 +0300 datetime: perf test for datetime parser 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. 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..efd0dba0e --- /dev/null +++ b/perf/datetime-compare.cc @@ -0,0 +1,212 @@ +#include "dt.h" +#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(); ----------------------------------------------------------------- Running to switch to doubles in all references to `struct datetime` now... Thanks, Timur