-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Describe the bug
I used abseil for timezone code and wanted to switch to standard std::chrono that supports timezones now. Code works, but some time later I've noticed perf issues. After debugging, it appears that time_zone::get_into is more than 100x slower in MS chrono::timezone code compared to abseil or chrono with g++.
sample code bellow. Note, there is _putenv("TZDIR=...") for abseil, adjust it for your absl location. Also, if easier, just comment out abseil and use chrono code with ms and g++ compilers to compare timing.
#include <stdint.h>
#include <vector>
#include <iostream>
#include <absl/time/time.h>
void compare_absl_chrono()
{
std::vector<int64_t> absl_result;
std::vector<int64_t> chrono_result;
int64_t ms_chrono, ms_absl;
if (1) // measure chrono perf
{
using namespace std::chrono;
const auto tz = locate_zone("America/New_York");
auto t0 = std::chrono::high_resolution_clock::now();
auto dateFrom = sys_days{year{1900} / January / 1};
auto info = tz->get_info(dateFrom);
auto offset = info.offset;
auto save = info.save;
for (int y = 0; y < 140; ++y)
{
for (int i = 0; i < 365; ++i)
{
dateFrom += days(1);
info = tz->get_info(dateFrom);
if (offset != info.offset || save != info.save)
{
offset = info.offset;
save = info.save;
chrono_result.push_back(sys_seconds(dateFrom).time_since_epoch().count());
}
}
}
auto t1 = std::chrono::high_resolution_clock::now();
ms_chrono = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count();
}
if (1) // measure absl perf
{
_putenv("TZDIR=C:\\work\\abseil-cpp\\absl\\time\\internal\\cctz\\testdata\\zoneinfo");
absl::TimeZone nyc;
if (!absl::LoadTimeZone("America/New_York", &nyc))
throw std::runtime_error("failed to load NY timezone info. Verify that TZDIR is set");
auto t0 = std::chrono::high_resolution_clock::now();
absl::CivilSecond ct(1900);
absl::Time startTime1 = absl::FromCivil(ct, absl::UTCTimeZone());
int offset = nyc.At(startTime1).offset;
bool is_dst = nyc.At(startTime1).is_dst;
for (int y = 0; y < 140; ++y)
{
for (int i = 0; i < 365; ++i)
{
startTime1 += absl::Seconds(60 * 60 * 24);
absl::TimeZone::CivilInfo ci = nyc.At(startTime1);
if (offset != ci.offset || is_dst != ci.is_dst)
{
offset = ci.offset;
is_dst = ci.is_dst;
auto t = (startTime1 - absl::UnixEpoch()) / absl::Seconds(1);
absl_result.push_back(t);
}
}
}
auto t1 = std::chrono::high_resolution_clock::now();
ms_absl = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count();
}
printf("data same:%d\n", absl_result == chrono_result);
printf("absl_result size:%zu, time:%lldms\n", absl_result.size(), ms_absl);
printf("chrono_result size:%zu, time:%lldms\n", chrono_result.size(), ms_chrono);
}
This code simply walks 140 years day by day and asks for timezone info for each of these days.
When timezone info changes, it's stores day of the change as a unix timestamp. This part is simply to verify that the results from absl timezone and chono timezone are the same. When I run x64 release build on Win11 I get this output:
data same:1
absl_result size:238, time:6ms
chrono_result size:238, time:1904ms
basically absl and chrono produce the same result, but it takes 400x more time to run chrono timezone code. Not sure what the reason is, but it certainly shouldn't be this slow. It's not even a caching issue imo. IANA timezone db is only like 500KB (and I access only EST timezone, which is perhaps less than 10KB total).
When I run this code on my PC compiled with mingw64 (version 14.2.0) I for chrono I get this output:
it's 1000x faster on mingw64
