mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:47:45 +00:00
AK+Tests: Replace years_to_days_since_epoch by near-instant function
This solves half the problem of #12729. Note that the inverse function time_to_tm() in LibC/time.cpp still uses a slow for-loop. See also #13138
This commit is contained in:
parent
60ee695287
commit
7a69219a35
2 changed files with 103 additions and 8 deletions
44
AK/Time.h
44
AK/Time.h
|
@ -66,14 +66,46 @@ constexpr int days_in_year(int year)
|
||||||
return 365 + (is_leap_year(year) ? 1 : 0);
|
return 365 + (is_leap_year(year) ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Detail {
|
||||||
|
// Integer division rounding towards negative infinity.
|
||||||
|
// TODO: This feels like there should be an easier way to do this.
|
||||||
|
template<int divisor>
|
||||||
|
constexpr int floor_div_by(int dividend)
|
||||||
|
{
|
||||||
|
static_assert(divisor >= 1);
|
||||||
|
int is_negative = dividend < 0;
|
||||||
|
return (dividend + is_negative) / divisor - is_negative;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counts how many integers n are in the interval [begin, end) with n % positive_mod == 0.
|
||||||
|
// NOTE: "end" is not considered to be part of the range, hence "[begin, end)".
|
||||||
|
template<int positive_mod>
|
||||||
|
constexpr int mod_zeros_in_range(int begin, int end)
|
||||||
|
{
|
||||||
|
return floor_div_by<positive_mod>(end - 1) - floor_div_by<positive_mod>(begin - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constexpr int years_to_days_since_epoch(int year)
|
constexpr int years_to_days_since_epoch(int year)
|
||||||
{
|
{
|
||||||
int days = 0;
|
int begin_year, end_year, leap_sign;
|
||||||
for (int current_year = 1970; current_year < year; ++current_year)
|
if (year < 1970) {
|
||||||
days += days_in_year(current_year);
|
begin_year = year;
|
||||||
for (int current_year = year; current_year < 1970; ++current_year)
|
end_year = 1970;
|
||||||
days -= days_in_year(current_year);
|
leap_sign = -1;
|
||||||
return days;
|
} else {
|
||||||
|
begin_year = 1970;
|
||||||
|
end_year = year;
|
||||||
|
leap_sign = +1;
|
||||||
|
}
|
||||||
|
// This duplicates the logic of 'is_leap_year', with the advantage of not needing any loops.
|
||||||
|
// Given that the definition of leap years is not expected to change, this should be a good trade-off.
|
||||||
|
int days = 365 * (year - 1970);
|
||||||
|
int extra_leap_days = 0;
|
||||||
|
extra_leap_days += Detail::mod_zeros_in_range<4>(begin_year, end_year);
|
||||||
|
extra_leap_days -= Detail::mod_zeros_in_range<100>(begin_year, end_year);
|
||||||
|
extra_leap_days += Detail::mod_zeros_in_range<400>(begin_year, end_year);
|
||||||
|
return days + extra_leap_days * leap_sign;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int days_since_epoch(int year, int month, int day)
|
constexpr int days_since_epoch(int year, int month, int day)
|
||||||
|
|
|
@ -317,8 +317,7 @@ TEST_CASE(years_to_days_since_epoch_points)
|
||||||
|
|
||||||
BENCHMARK_CASE(years_to_days_since_epoch_benchmark)
|
BENCHMARK_CASE(years_to_days_since_epoch_benchmark)
|
||||||
{
|
{
|
||||||
// This benchmark takes consistently about 295±1 ms on Linux, and roughly 2300 ms on Serenity.
|
// This benchmark takes consistently "0ms" on Linux, and "0ms" on Serenity.
|
||||||
// TODO: Computing the amount of days should never take dozens of milliseconds.
|
|
||||||
for (size_t i = 0; i < 100; ++i) {
|
for (size_t i = 0; i < 100; ++i) {
|
||||||
int actual_days = years_to_days_since_epoch(-5877640);
|
int actual_days = years_to_days_since_epoch(-5877640);
|
||||||
(void)actual_days;
|
(void)actual_days;
|
||||||
|
@ -326,6 +325,70 @@ BENCHMARK_CASE(years_to_days_since_epoch_benchmark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(div_floor_by)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(-5), -2);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(-4), -1);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(-3), -1);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(-2), -1);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(-1), -1);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+0), +0);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+1), +0);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+2), +0);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+3), +0);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+4), +1);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+5), +1);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+6), +1);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+7), +1);
|
||||||
|
EXPECT_EQ(AK::Detail::floor_div_by<4>(+8), +2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(mod_zeros_in_range)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(0, 0), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(0, 1), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(0, 2), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(0, 3), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(0, 4), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(0, 5), 2);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(0, 6), 2);
|
||||||
|
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(1, 1), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(1, 2), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(1, 3), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(1, 4), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(1, 5), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(1, 6), 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(2, 2), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(2, 3), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(2, 4), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(2, 5), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(2, 6), 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(3, 3), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(3, 4), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(3, 5), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(3, 6), 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(4, 4), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(4, 5), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(4, 6), 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(5, 5), 0);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(5, 6), 0);
|
||||||
|
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(6, 6), 0);
|
||||||
|
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(-5, 3), 2);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(-4, 3), 2);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(-3, 3), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(-2, 3), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(-1, 3), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(-0, 3), 1);
|
||||||
|
EXPECT_EQ(AK::Detail::mod_zeros_in_range<4>(+1, 3), 0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE(years_to_days_since_epoch_span)
|
TEST_CASE(years_to_days_since_epoch_span)
|
||||||
{
|
{
|
||||||
auto test_data_start_year = 1900;
|
auto test_data_start_year = 1900;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue