diff options
Diffstat (limited to 'absl/time')
-rw-r--r-- | absl/time/BUILD.bazel | 20 | ||||
-rw-r--r-- | absl/time/clock_benchmark.cc | 72 | ||||
-rw-r--r-- | absl/time/duration_benchmark.cc | 361 | ||||
-rw-r--r-- | absl/time/duration_test.cc | 20 | ||||
-rw-r--r-- | absl/time/format_benchmark.cc | 63 | ||||
-rw-r--r-- | absl/time/internal/cctz/src/time_zone_format_test.cc | 13 | ||||
-rw-r--r-- | absl/time/internal/cctz/src/time_zone_lookup_test.cc | 17 | ||||
-rw-r--r-- | absl/time/time.h | 88 | ||||
-rw-r--r-- | absl/time/time_benchmark.cc | 316 |
9 files changed, 899 insertions, 71 deletions
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index 64cb99f7..fe55fe1f 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -93,3 +93,23 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "time_benchmark", + srcs = [ + "clock_benchmark.cc", + "duration_benchmark.cc", + "format_benchmark.cc", + "time_benchmark.cc", + ], + copts = ABSL_TEST_COPTS, + tags = [ + "benchmark", + ], + deps = [ + ":test_util", + ":time", + "//absl/base", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/absl/time/clock_benchmark.cc b/absl/time/clock_benchmark.cc new file mode 100644 index 00000000..3d3cd9d5 --- /dev/null +++ b/absl/time/clock_benchmark.cc @@ -0,0 +1,72 @@ +// Copyright 2018 The Abseil Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/time/clock.h" + +#if !defined(_WIN32) +#include <sys/time.h> +#endif // _WIN32 +#include <cstdio> + +#include "absl/base/internal/cycleclock.h" +#include "benchmark/benchmark.h" + +namespace { + +void BM_Clock_Now_AbslTime(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::Now()); + } +} +BENCHMARK(BM_Clock_Now_AbslTime); + +void BM_Clock_Now_GetCurrentTimeNanos(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::GetCurrentTimeNanos()); + } +} +BENCHMARK(BM_Clock_Now_GetCurrentTimeNanos); + +void BM_Clock_Now_AbslTime_ToUnixNanos(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToUnixNanos(absl::Now())); + } +} +BENCHMARK(BM_Clock_Now_AbslTime_ToUnixNanos); + +void BM_Clock_Now_CycleClock(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::base_internal::CycleClock::Now()); + } +} +BENCHMARK(BM_Clock_Now_CycleClock); + +#if !defined(_WIN32) +static void BM_Clock_Now_gettimeofday(benchmark::State& state) { + struct timeval tv; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(gettimeofday(&tv, nullptr)); + } +} +BENCHMARK(BM_Clock_Now_gettimeofday); + +static void BM_Clock_Now_clock_gettime(benchmark::State& state) { + struct timespec ts; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(clock_gettime(CLOCK_REALTIME, &ts)); + } +} +BENCHMARK(BM_Clock_Now_clock_gettime); +#endif // _WIN32 + +} // namespace diff --git a/absl/time/duration_benchmark.cc b/absl/time/duration_benchmark.cc new file mode 100644 index 00000000..54f89a1f --- /dev/null +++ b/absl/time/duration_benchmark.cc @@ -0,0 +1,361 @@ +// Copyright 2018 The Abseil Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <ctime> +#include <string> + +#include "absl/time/time.h" +#include "benchmark/benchmark.h" + +namespace { + +// +// Factory functions +// + +void BM_Duration_Factory_Nanoseconds(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::Nanoseconds(1)); + } +} +BENCHMARK(BM_Duration_Factory_Nanoseconds); + +void BM_Duration_Factory_Microseconds(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::Microseconds(1)); + } +} +BENCHMARK(BM_Duration_Factory_Microseconds); + +void BM_Duration_Factory_Milliseconds(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::Milliseconds(1)); + } +} +BENCHMARK(BM_Duration_Factory_Milliseconds); + +void BM_Duration_Factory_Seconds(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::Seconds(1)); + } +} +BENCHMARK(BM_Duration_Factory_Seconds); + +void BM_Duration_Factory_Minutes(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::Minutes(1)); + } +} +BENCHMARK(BM_Duration_Factory_Minutes); + +void BM_Duration_Factory_Hours(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::Hours(1)); + } +} +BENCHMARK(BM_Duration_Factory_Hours); + +// +// Arithmetic +// + +void BM_Duration_Addition(benchmark::State& state) { + absl::Duration d = absl::Nanoseconds(1); + absl::Duration step = absl::Milliseconds(1); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(d += step); + } +} +BENCHMARK(BM_Duration_Addition); + +void BM_Duration_Subtraction(benchmark::State& state) { + absl::Duration d = absl::Seconds(std::numeric_limits<int64_t>::max()); + absl::Duration step = absl::Milliseconds(1); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(d -= step); + } +} +BENCHMARK(BM_Duration_Subtraction); + +void BM_Duration_Multiplication_Fixed(benchmark::State& state) { + absl::Duration d = absl::Milliseconds(1); + absl::Duration s; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(s += d * (i + 1)); + ++i; + } +} +BENCHMARK(BM_Duration_Multiplication_Fixed); + +void BM_Duration_Multiplication_Double(benchmark::State& state) { + absl::Duration d = absl::Milliseconds(1); + absl::Duration s; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(s += d * (i + 1.0)); + ++i; + } +} +BENCHMARK(BM_Duration_Multiplication_Double); + +void BM_Duration_Division_Fixed(benchmark::State& state) { + absl::Duration d = absl::Seconds(1); + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(d /= i + 1); + ++i; + } +} +BENCHMARK(BM_Duration_Division_Fixed); + +void BM_Duration_Division_Double(benchmark::State& state) { + absl::Duration d = absl::Seconds(1); + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(d /= i + 1.0); + ++i; + } +} +BENCHMARK(BM_Duration_Division_Double); + +void BM_Duration_FDivDuration_Nanoseconds(benchmark::State& state) { + double d = 1; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize( + d += absl::FDivDuration(absl::Milliseconds(i), absl::Nanoseconds(1))); + ++i; + } +} +BENCHMARK(BM_Duration_FDivDuration_Nanoseconds); + +void BM_Duration_IDivDuration_Nanoseconds(benchmark::State& state) { + int64_t a = 1; + absl::Duration ignore; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(a += + absl::IDivDuration(absl::Nanoseconds(i), + absl::Nanoseconds(1), &ignore)); + ++i; + } +} +BENCHMARK(BM_Duration_IDivDuration_Nanoseconds); + +void BM_Duration_IDivDuration_Microseconds(benchmark::State& state) { + int64_t a = 1; + absl::Duration ignore; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(a += absl::IDivDuration(absl::Microseconds(i), + absl::Microseconds(1), + &ignore)); + ++i; + } +} +BENCHMARK(BM_Duration_IDivDuration_Microseconds); + +void BM_Duration_IDivDuration_Milliseconds(benchmark::State& state) { + int64_t a = 1; + absl::Duration ignore; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(a += absl::IDivDuration(absl::Milliseconds(i), + absl::Milliseconds(1), + &ignore)); + ++i; + } +} +BENCHMARK(BM_Duration_IDivDuration_Milliseconds); + +void BM_Duration_IDivDuration_Seconds(benchmark::State& state) { + int64_t a = 1; + absl::Duration ignore; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize( + a += absl::IDivDuration(absl::Seconds(i), absl::Seconds(1), &ignore)); + ++i; + } +} +BENCHMARK(BM_Duration_IDivDuration_Seconds); + +void BM_Duration_IDivDuration_Minutes(benchmark::State& state) { + int64_t a = 1; + absl::Duration ignore; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize( + a += absl::IDivDuration(absl::Minutes(i), absl::Minutes(1), &ignore)); + ++i; + } +} +BENCHMARK(BM_Duration_IDivDuration_Minutes); + +void BM_Duration_IDivDuration_Hours(benchmark::State& state) { + int64_t a = 1; + absl::Duration ignore; + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize( + a += absl::IDivDuration(absl::Hours(i), absl::Hours(1), &ignore)); + ++i; + } +} +BENCHMARK(BM_Duration_IDivDuration_Hours); + +void BM_Duration_ToInt64Nanoseconds(benchmark::State& state) { + absl::Duration d = absl::Seconds(100000); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToInt64Nanoseconds(d)); + } +} +BENCHMARK(BM_Duration_ToInt64Nanoseconds); + +void BM_Duration_ToInt64Microseconds(benchmark::State& state) { + absl::Duration d = absl::Seconds(100000); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToInt64Microseconds(d)); + } +} +BENCHMARK(BM_Duration_ToInt64Microseconds); + +void BM_Duration_ToInt64Milliseconds(benchmark::State& state) { + absl::Duration d = absl::Seconds(100000); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToInt64Milliseconds(d)); + } +} +BENCHMARK(BM_Duration_ToInt64Milliseconds); + +void BM_Duration_ToInt64Seconds(benchmark::State& state) { + absl::Duration d = absl::Seconds(100000); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToInt64Seconds(d)); + } +} +BENCHMARK(BM_Duration_ToInt64Seconds); + +void BM_Duration_ToInt64Minutes(benchmark::State& state) { + absl::Duration d = absl::Seconds(100000); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToInt64Minutes(d)); + } +} +BENCHMARK(BM_Duration_ToInt64Minutes); + +void BM_Duration_ToInt64Hours(benchmark::State& state) { + absl::Duration d = absl::Seconds(100000); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToInt64Hours(d)); + } +} +BENCHMARK(BM_Duration_ToInt64Hours); + +// +// To/FromTimespec +// + +void BM_Duration_ToTimespec_AbslTime(benchmark::State& state) { + absl::Duration d = absl::Seconds(1); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToTimespec(d)); + } +} +BENCHMARK(BM_Duration_ToTimespec_AbslTime); + +ABSL_ATTRIBUTE_NOINLINE timespec DoubleToTimespec(double seconds) { + timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = (seconds - ts.tv_sec) * (1000 * 1000 * 1000); + return ts; +} + +void BM_Duration_ToTimespec_Double(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(DoubleToTimespec(1.0)); + } +} +BENCHMARK(BM_Duration_ToTimespec_Double); + +void BM_Duration_FromTimespec_AbslTime(benchmark::State& state) { + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 0; + while (state.KeepRunning()) { + if (++ts.tv_nsec == 1000 * 1000 * 1000) { + ++ts.tv_sec; + ts.tv_nsec = 0; + } + benchmark::DoNotOptimize(absl::DurationFromTimespec(ts)); + } +} +BENCHMARK(BM_Duration_FromTimespec_AbslTime); + +ABSL_ATTRIBUTE_NOINLINE double TimespecToDouble(timespec ts) { + return ts.tv_sec + (ts.tv_nsec / (1000 * 1000 * 1000)); +} + +void BM_Duration_FromTimespec_Double(benchmark::State& state) { + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 0; + while (state.KeepRunning()) { + if (++ts.tv_nsec == 1000 * 1000 * 1000) { + ++ts.tv_sec; + ts.tv_nsec = 0; + } + benchmark::DoNotOptimize(TimespecToDouble(ts)); + } +} +BENCHMARK(BM_Duration_FromTimespec_Double); + +// +// String conversions +// + +const char* const kDurations[] = { + "0", // 0 + "123ns", // 1 + "1h2m3s", // 2 + "-2h3m4.005006007s", // 3 + "2562047788015215h30m7.99999999975s", // 4 +}; +const int kNumDurations = sizeof(kDurations) / sizeof(kDurations[0]); + +void BM_Duration_FormatDuration(benchmark::State& state) { + const std::string s = kDurations[state.range(0)]; + state.SetLabel(s); + absl::Duration d; + absl::ParseDuration(kDurations[state.range(0)], &d); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::FormatDuration(d)); + } +} +BENCHMARK(BM_Duration_FormatDuration)->DenseRange(0, kNumDurations - 1); + +void BM_Duration_ParseDuration(benchmark::State& state) { + const std::string s = kDurations[state.range(0)]; + state.SetLabel(s); + absl::Duration d; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ParseDuration(s, &d)); + } +} +BENCHMARK(BM_Duration_ParseDuration)->DenseRange(0, kNumDurations - 1); + +} // namespace diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index 7918e167..704684ed 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -330,18 +330,10 @@ TEST(Duration, ToChrono) { EXPECT_EQ(hours::max(), absl::ToChronoHours(inf)); } -// Used for testing the factory overloads. -template <typename T> -struct ImplicitlyConvertible { - T n_; - explicit ImplicitlyConvertible(T n) : n_(n) {} - // Marking this conversion operator with 'explicit' will cause the test to - // fail (as desired). - operator T() { return n_; } -}; - TEST(Duration, FactoryOverloads) { + enum E { kOne = 1 }; #define TEST_FACTORY_OVERLOADS(NAME) \ + EXPECT_EQ(1, NAME(kOne) / NAME(kOne)); \ EXPECT_EQ(1, NAME(static_cast<int8_t>(1)) / NAME(1)); \ EXPECT_EQ(1, NAME(static_cast<int16_t>(1)) / NAME(1)); \ EXPECT_EQ(1, NAME(static_cast<int32_t>(1)) / NAME(1)); \ @@ -350,14 +342,6 @@ TEST(Duration, FactoryOverloads) { EXPECT_EQ(1, NAME(static_cast<uint16_t>(1)) / NAME(1)); \ EXPECT_EQ(1, NAME(static_cast<uint32_t>(1)) / NAME(1)); \ EXPECT_EQ(1, NAME(static_cast<uint64_t>(1)) / NAME(1)); \ - EXPECT_EQ(1, NAME(ImplicitlyConvertible<int8_t>(1)) / NAME(1)); \ - EXPECT_EQ(1, NAME(ImplicitlyConvertible<int16_t>(1)) / NAME(1)); \ - EXPECT_EQ(1, NAME(ImplicitlyConvertible<int32_t>(1)) / NAME(1)); \ - EXPECT_EQ(1, NAME(ImplicitlyConvertible<int64_t>(1)) / NAME(1)); \ - EXPECT_EQ(1, NAME(ImplicitlyConvertible<uint8_t>(1)) / NAME(1)); \ - EXPECT_EQ(1, NAME(ImplicitlyConvertible<uint16_t>(1)) / NAME(1)); \ - EXPECT_EQ(1, NAME(ImplicitlyConvertible<uint32_t>(1)) / NAME(1)); \ - EXPECT_EQ(1, NAME(ImplicitlyConvertible<uint64_t>(1)) / NAME(1)); \ EXPECT_EQ(NAME(1) / 2, NAME(static_cast<float>(0.5))); \ EXPECT_EQ(NAME(1) / 2, NAME(static_cast<double>(0.5))); \ EXPECT_EQ(1.5, absl::FDivDuration(NAME(static_cast<float>(1.5)), NAME(1))); \ diff --git a/absl/time/format_benchmark.cc b/absl/time/format_benchmark.cc new file mode 100644 index 00000000..ee53d71c --- /dev/null +++ b/absl/time/format_benchmark.cc @@ -0,0 +1,63 @@ +// Copyright 2018 The Abseil Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cstddef> +#include <string> + +#include "absl/time/internal/test_util.h" +#include "absl/time/time.h" +#include "benchmark/benchmark.h" + +namespace { + +namespace { +const char* const kFormats[] = { + absl::RFC1123_full, // 0 + absl::RFC1123_no_wday, // 1 + absl::RFC3339_full, // 2 + absl::RFC3339_sec, // 3 + "%Y-%m-%dT%H:%M:%S", // 4 + "%Y-%m-%d", // 5 +}; +const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]); +} // namespace + +void BM_Format_FormatTime(benchmark::State& state) { + const std::string fmt = kFormats[state.range(0)]; + state.SetLabel(fmt); + const absl::TimeZone lax = + absl::time_internal::LoadTimeZone("America/Los_Angeles"); + const absl::Time t = + absl::FromDateTime(1977, 6, 28, 9, 8, 7, lax) + absl::Nanoseconds(1); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::FormatTime(fmt, t, lax).length()); + } +} +BENCHMARK(BM_Format_FormatTime)->DenseRange(0, kNumFormats - 1); + +void BM_Format_ParseTime(benchmark::State& state) { + const std::string fmt = kFormats[state.range(0)]; + state.SetLabel(fmt); + const absl::TimeZone lax = + absl::time_internal::LoadTimeZone("America/Los_Angeles"); + absl::Time t = + absl::FromDateTime(1977, 6, 28, 9, 8, 7, lax) + absl::Nanoseconds(1); + const std::string when = absl::FormatTime(fmt, t, lax); + std::string err; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ParseTime(fmt, when, lax, &t, &err)); + } +} +BENCHMARK(BM_Format_ParseTime)->DenseRange(0, kNumFormats - 1); + +} // namespace diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc index 3a5f19ac..7d5b02ad 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -463,13 +463,12 @@ TEST(Format, ExtendedSecondOffset) { EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz)); tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc); - if (tz.lookup(tp).offset == 4 * 60 * 60) { - // We're likely dealing with zoneinfo that doesn't support really old - // timestamps, so Europe/Moscow never looks to be on local mean time. - } else { - TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19"); - TestFormatSpecifier(tp, tz, "%Ez", "+04:31"); - } +#if defined(__ANDROID__) && __ANDROID_API__ < 25 + // Only Android 'N'.1 and beyond have this tz2016g transition. +#else + TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19"); + TestFormatSpecifier(tp, tz, "%Ez", "+04:31"); +#endif tp += seconds(1); TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00"); } diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index f97eab02..06b172a8 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -693,7 +693,14 @@ TEST(TimeZones, LoadZonesConcurrently) { // Allow a small number of failures to account for skew between // the contents of kTimeZoneNames and the zoneinfo data source. +#if defined(__ANDROID__) + // Cater to the possibility of using an even older zoneinfo data + // source when running on Android, where it is difficult to override + // the bionic tzdata provided by the test environment. + const std::size_t max_failures = 20; +#else const std::size_t max_failures = 3; +#endif std::set<std::string> failures; for (const auto& thread_failure : thread_failures) { failures.insert(thread_failure.begin(), thread_failure.end()); @@ -839,7 +846,7 @@ TEST(TimeZoneImpl, LocalTimeInFixed) { const time_zone tz = fixed_time_zone(offset); const auto tp = system_clock::from_time_t(0); ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false, - "UTC-083347"); + "-083347"); EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz)))); } @@ -1098,6 +1105,9 @@ TEST(TimeZoneEdgeCase, PacificApia) { TEST(TimeZoneEdgeCase, AfricaCairo) { const time_zone tz = LoadZone("Africa/Cairo"); +#if defined(__ANDROID__) && __ANDROID_API__ < 21 + // Only Android 'L' and beyond have this tz2014c transition. +#else // An interesting case of midnight not existing. // // 1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET) @@ -1106,11 +1116,15 @@ TEST(TimeZoneEdgeCase, AfricaCairo) { ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET"); tp += seconds(1); ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST"); +#endif } TEST(TimeZoneEdgeCase, AfricaMonrovia) { const time_zone tz = LoadZone("Africa/Monrovia"); +#if defined(__ANDROID__) && __ANDROID_API__ < 26 + // Only Android 'O' and beyond have this tz2017b transition. +#else // Strange offset change -00:44:30 -> +00:00:00 (non-DST) // // 63593069 == Thu, 6 Jan 1972 23:59:59 -0044 (MMT) @@ -1119,6 +1133,7 @@ TEST(TimeZoneEdgeCase, AfricaMonrovia) { ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT"); tp += seconds(1); ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT"); +#endif } TEST(TimeZoneEdgeCase, AmericaJamaica) { diff --git a/absl/time/time.h b/absl/time/time.h index c50d69a5..99c12bbd 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -82,8 +82,15 @@ constexpr Duration MakeDuration(int64_t hi, uint32_t lo); constexpr Duration MakeDuration(int64_t hi, int64_t lo); constexpr int64_t kTicksPerNanosecond = 4; constexpr int64_t kTicksPerSecond = 1000 * 1000 * 1000 * kTicksPerNanosecond; +template <std::intmax_t N> +constexpr Duration FromInt64(int64_t v, std::ratio<1, N>); +constexpr Duration FromInt64(int64_t v, std::ratio<60>); +constexpr Duration FromInt64(int64_t v, std::ratio<3600>); +template <typename T> +using EnableIfIntegral = typename std::enable_if< + std::is_integral<T>::value || std::is_enum<T>::value, int>::type; template <typename T> -using IsFloatingPoint = +using EnableIfFloat = typename std::enable_if<std::is_floating_point<T>::value, int>::type; } // namespace time_internal @@ -178,15 +185,15 @@ inline Duration operator-(Duration lhs, Duration rhs) { return lhs -= rhs; } // Multiplicative Operators template <typename T> -inline Duration operator*(Duration lhs, T rhs) { +Duration operator*(Duration lhs, T rhs) { return lhs *= rhs; } template <typename T> -inline Duration operator*(T lhs, Duration rhs) { +Duration operator*(T lhs, Duration rhs) { return rhs *= lhs; } template <typename T> -inline Duration operator/(Duration lhs, T rhs) { +Duration operator/(Duration lhs, T rhs) { return lhs /= rhs; } inline int64_t operator/(Duration lhs, Duration rhs) { @@ -322,27 +329,27 @@ constexpr Duration Hours(int64_t n); // Example: // auto a = absl::Seconds(1.5); // OK // auto b = absl::Milliseconds(1500); // BETTER -template <typename T, time_internal::IsFloatingPoint<T> = 0> +template <typename T, time_internal::EnableIfFloat<T> = 0> Duration Nanoseconds(T n) { return n * Nanoseconds(1); } -template <typename T, time_internal::IsFloatingPoint<T> = 0> +template <typename T, time_internal::EnableIfFloat<T> = 0> Duration Microseconds(T n) { return n * Microseconds(1); } -template <typename T, time_internal::IsFloatingPoint<T> = 0> +template <typename T, time_internal::EnableIfFloat<T> = 0> Duration Milliseconds(T n) { return n * Milliseconds(1); } -template <typename T, time_internal::IsFloatingPoint<T> = 0> +template <typename T, time_internal::EnableIfFloat<T> = 0> Duration Seconds(T n) { return n * Seconds(1); } -template <typename T, time_internal::IsFloatingPoint<T> = 0> +template <typename T, time_internal::EnableIfFloat<T> = 0> Duration Minutes(T n) { return n * Minutes(1); } -template <typename T, time_internal::IsFloatingPoint<T> = 0> +template <typename T, time_internal::EnableIfFloat<T> = 0> Duration Hours(T n) { return n * Hours(1); } @@ -1154,10 +1161,16 @@ constexpr Duration FromInt64(int64_t v, std::ratio<1, N>) { v / N, v % N * kTicksPerNanosecond * 1000 * 1000 * 1000 / N); } constexpr Duration FromInt64(int64_t v, std::ratio<60>) { - return Minutes(v); + return (v <= std::numeric_limits<int64_t>::max() / 60 && + v >= std::numeric_limits<int64_t>::min() / 60) + ? MakeDuration(v * 60) + : v > 0 ? InfiniteDuration() : -InfiniteDuration(); } constexpr Duration FromInt64(int64_t v, std::ratio<3600>) { - return Hours(v); + return (v <= std::numeric_limits<int64_t>::max() / 3600 && + v >= std::numeric_limits<int64_t>::min() / 3600) + ? MakeDuration(v * 3600) + : v > 0 ? InfiniteDuration() : -InfiniteDuration(); } // IsValidRep64<T>(0) is true if the expression `int64_t{std::declval<T>()}` is @@ -1220,6 +1233,24 @@ T ToChronoDuration(Duration d) { } } // namespace time_internal +constexpr Duration Nanoseconds(int64_t n) { + return time_internal::FromInt64(n, std::nano{}); +} +constexpr Duration Microseconds(int64_t n) { + return time_internal::FromInt64(n, std::micro{}); +} +constexpr Duration Milliseconds(int64_t n) { + return time_internal::FromInt64(n, std::milli{}); +} +constexpr Duration Seconds(int64_t n) { + return time_internal::FromInt64(n, std::ratio<1>{}); +} +constexpr Duration Minutes(int64_t n) { + return time_internal::FromInt64(n, std::ratio<60>{}); +} +constexpr Duration Hours(int64_t n) { + return time_internal::FromInt64(n, std::ratio<3600>{}); +} constexpr bool operator<(Duration lhs, Duration rhs) { return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs) @@ -1261,39 +1292,6 @@ constexpr Duration operator-(Duration d) { time_internal::GetRepLo(d)); } -constexpr Duration Nanoseconds(int64_t n) { - return time_internal::MakeNormalizedDuration( - n / (1000 * 1000 * 1000), - n % (1000 * 1000 * 1000) * time_internal::kTicksPerNanosecond); -} - -constexpr Duration Microseconds(int64_t n) { - return time_internal::MakeNormalizedDuration( - n / (1000 * 1000), - n % (1000 * 1000) * (1000 * time_internal::kTicksPerNanosecond)); -} - -constexpr Duration Milliseconds(int64_t n) { - return time_internal::MakeNormalizedDuration( - n / 1000, n % 1000 * (1000 * 1000 * time_internal::kTicksPerNanosecond)); -} - -constexpr Duration Seconds(int64_t n) { return time_internal::MakeDuration(n); } - -constexpr Duration Minutes(int64_t n) { - return (n <= std::numeric_limits<int64_t>::max() / 60 && - n >= std::numeric_limits<int64_t>::min() / 60) - ? time_internal::MakeDuration(n * 60) - : n > 0 ? InfiniteDuration() : -InfiniteDuration(); -} - -constexpr Duration Hours(int64_t n) { - return (n <= std::numeric_limits<int64_t>::max() / 3600 && - n >= std::numeric_limits<int64_t>::min() / 3600) - ? time_internal::MakeDuration(n * 3600) - : n > 0 ? InfiniteDuration() : -InfiniteDuration(); -} - constexpr Duration InfiniteDuration() { return time_internal::MakeDuration(std::numeric_limits<int64_t>::max(), ~0U); } diff --git a/absl/time/time_benchmark.cc b/absl/time/time_benchmark.cc new file mode 100644 index 00000000..e1009946 --- /dev/null +++ b/absl/time/time_benchmark.cc @@ -0,0 +1,316 @@ +// Copyright 2018 The Abseil Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/time/time.h" + +#if !defined(_WIN32) +#include <sys/time.h> +#endif // _WIN32 +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <cstring> +#include <ctime> +#include <memory> +#include <string> + +#include "absl/time/clock.h" +#include "absl/time/internal/test_util.h" +#include "benchmark/benchmark.h" + +namespace { + +// +// Addition/Subtraction of a duration +// + +void BM_Time_Arithmetic(benchmark::State& state) { + const absl::Duration nano = absl::Nanoseconds(1); + const absl::Duration sec = absl::Seconds(1); + absl::Time t = absl::UnixEpoch(); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(t += nano); + benchmark::DoNotOptimize(t -= sec); + } +} +BENCHMARK(BM_Time_Arithmetic); + +// +// Time difference +// + +void BM_Time_Difference(benchmark::State& state) { + absl::Time start = absl::Now(); + absl::Time end = start + absl::Nanoseconds(1); + absl::Duration diff; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(diff += end - start); + } +} +BENCHMARK(BM_Time_Difference); + +// +// ToDateTime +// +// In each "ToDateTime" benchmark we switch between two instants +// separated by at least one transition in order to defeat any +// internal caching of previous results (e.g., see local_time_hint_). +// +// The "UTC" variants use UTC instead of the Google/local time zone. +// + +void BM_Time_ToDateTime_Absl(benchmark::State& state) { + const absl::TimeZone tz = + absl::time_internal::LoadTimeZone("America/Los_Angeles"); + absl::Time t = absl::FromUnixSeconds(1384569027); + absl::Time t2 = absl::FromUnixSeconds(1418962578); + while (state.KeepRunning()) { + std::swap(t, t2); + t += absl::Seconds(1); + benchmark::DoNotOptimize(t.In(tz)); + } +} +BENCHMARK(BM_Time_ToDateTime_Absl); + +void BM_Time_ToDateTime_Libc(benchmark::State& state) { + // No timezone support, so just use localtime. + time_t t = 1384569027; + time_t t2 = 1418962578; + while (state.KeepRunning()) { + std::swap(t, t2); + t += 1; + struct tm tm; +#if !defined(_WIN32) + benchmark::DoNotOptimize(localtime_r(&t, &tm)); +#else // _WIN32 + benchmark::DoNotOptimize(localtime_s(&tm, &t)); +#endif // _WIN32 + } +} +BENCHMARK(BM_Time_ToDateTime_Libc); + +void BM_Time_ToDateTimeUTC_Absl(benchmark::State& state) { + const absl::TimeZone tz = absl::UTCTimeZone(); + absl::Time t = absl::FromUnixSeconds(1384569027); + while (state.KeepRunning()) { + t += absl::Seconds(1); + benchmark::DoNotOptimize(t.In(tz)); + } +} +BENCHMARK(BM_Time_ToDateTimeUTC_Absl); + +void BM_Time_ToDateTimeUTC_Libc(benchmark::State& state) { + time_t t = 1384569027; + while (state.KeepRunning()) { + t += 1; + struct tm tm; +#if !defined(_WIN32) + benchmark::DoNotOptimize(gmtime_r(&t, &tm)); +#else // _WIN32 + benchmark::DoNotOptimize(gmtime_s(&tm, &t)); +#endif // _WIN32 + } +} +BENCHMARK(BM_Time_ToDateTimeUTC_Libc); + +// +// FromUnixMicros +// + +void BM_Time_FromUnixMicros(benchmark::State& state) { + int i = 0; + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::FromUnixMicros(i)); + ++i; + } +} +BENCHMARK(BM_Time_FromUnixMicros); + +void BM_Time_ToUnixNanos(benchmark::State& state) { + const absl::Time t = absl::UnixEpoch() + absl::Seconds(123); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(ToUnixNanos(t)); + } +} +BENCHMARK(BM_Time_ToUnixNanos); + +void BM_Time_ToUnixMicros(benchmark::State& state) { + const absl::Time t = absl::UnixEpoch() + absl::Seconds(123); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(ToUnixMicros(t)); + } +} +BENCHMARK(BM_Time_ToUnixMicros); + +void BM_Time_ToUnixMillis(benchmark::State& state) { + const absl::Time t = absl::UnixEpoch() + absl::Seconds(123); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(ToUnixMillis(t)); + } +} +BENCHMARK(BM_Time_ToUnixMillis); + +void BM_Time_ToUnixSeconds(benchmark::State& state) { + const absl::Time t = absl::UnixEpoch() + absl::Seconds(123); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToUnixSeconds(t)); + } +} +BENCHMARK(BM_Time_ToUnixSeconds); + +// +// FromDateTime +// +// In each "FromDateTime" benchmark we switch between two YMDhms +// values separated by at least one transition in order to defeat any +// internal caching of previous results (e.g., see time_local_hint_). +// +// The "UTC" variants use UTC instead of the Google/local time zone. +// The "Day0" variants require normalization of the day of month. +// + +void BM_Time_FromDateTime_Absl(benchmark::State& state) { + const absl::TimeZone tz = + absl::time_internal::LoadTimeZone("America/Los_Angeles"); + int i = 0; + while (state.KeepRunning()) { + if ((i & 1) == 0) { + absl::FromDateTime(2014, 12, 18, 20, 16, 18, tz); + } else { + absl::FromDateTime(2013, 11, 15, 18, 30, 27, tz); + } + ++i; + } +} +BENCHMARK(BM_Time_FromDateTime_Absl); + +void BM_Time_FromDateTime_Libc(benchmark::State& state) { + // No timezone support, so just use localtime. + int i = 0; + while (state.KeepRunning()) { + struct tm tm; + if ((i & 1) == 0) { + tm.tm_year = 2014 - 1900; + tm.tm_mon = 12 - 1; + tm.tm_mday = 18; + tm.tm_hour = 20; + tm.tm_min = 16; + tm.tm_sec = 18; + } else { + tm.tm_year = 2013 - 1900; + tm.tm_mon = 11 - 1; + tm.tm_mday = 15; + tm.tm_hour = 18; + tm.tm_min = 30; + tm.tm_sec = 27; + } + tm.tm_isdst = -1; + mktime(&tm); + ++i; + } +} +BENCHMARK(BM_Time_FromDateTime_Libc); + +void BM_Time_FromDateTimeUTC_Absl(benchmark::State& state) { + const absl::TimeZone tz = absl::UTCTimeZone(); + while (state.KeepRunning()) { + FromDateTime(2014, 12, 18, 20, 16, 18, tz); + } +} +BENCHMARK(BM_Time_FromDateTimeUTC_Absl); + +void BM_Time_FromDateTimeDay0_Absl(benchmark::State& state) { + const absl::TimeZone tz = + absl::time_internal::LoadTimeZone("America/Los_Angeles"); + int i = 0; + while (state.KeepRunning()) { + if ((i & 1) == 0) { + absl::FromDateTime(2014, 12, 0, 20, 16, 18, tz); + } else { + absl::FromDateTime(2013, 11, 0, 18, 30, 27, tz); + } + ++i; + } +} +BENCHMARK(BM_Time_FromDateTimeDay0_Absl); + +void BM_Time_FromDateTimeDay0_Libc(benchmark::State& state) { + // No timezone support, so just use localtime. + int i = 0; + while (state.KeepRunning()) { + struct tm tm; + if ((i & 1) == 0) { + tm.tm_year = 2014 - 1900; + tm.tm_mon = 12 - 1; + tm.tm_mday = 0; + tm.tm_hour = 20; + tm.tm_min = 16; + tm.tm_sec = 18; + } else { + tm.tm_year = 2013 - 1900; + tm.tm_mon = 11 - 1; + tm.tm_mday = 0; + tm.tm_hour = 18; + tm.tm_min = 30; + tm.tm_sec = 27; + } + tm.tm_isdst = -1; + mktime(&tm); + ++i; + } +} +BENCHMARK(BM_Time_FromDateTimeDay0_Libc); + +// +// To/FromTimespec +// + +void BM_Time_ToTimespec(benchmark::State& state) { + absl::Time now = absl::Now(); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::ToTimespec(now)); + } +} +BENCHMARK(BM_Time_ToTimespec); + +void BM_Time_FromTimespec(benchmark::State& state) { + timespec ts = absl::ToTimespec(absl::Now()); + while (state.KeepRunning()) { + if (++ts.tv_nsec == 1000 * 1000 * 1000) { + ++ts.tv_sec; + ts.tv_nsec = 0; + } + benchmark::DoNotOptimize(absl::TimeFromTimespec(ts)); + } +} +BENCHMARK(BM_Time_FromTimespec); + +// +// Comparison with InfiniteFuture/Past +// + +void BM_Time_InfiniteFuture(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::InfiniteFuture()); + } +} +BENCHMARK(BM_Time_InfiniteFuture); + +void BM_Time_InfinitePast(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize(absl::InfinitePast()); + } +} +BENCHMARK(BM_Time_InfinitePast); + +} // namespace |