Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use std::shared_mutex in time_zone_impl.cc #304

Closed

Conversation

hengfengli
Copy link

We have observed high lock contention issues (consume a lot of CPUs) when calling absl::LoadTimeZone. We wonder if it is better to use std::shared_mutex to allow multiple readers to
access the time zone map concurrently. The entries in the time zone map are never modified after they are initialized.

@hengfengli
Copy link
Author

hengfengli commented Nov 20, 2024

I just saw the thread in #212 so I guess the suggestion is still to use std::lock_guard and the application should build its own cache to avoid too many absl::LoadTimeZone calls, is this right?

@devbww
Copy link
Contributor

devbww commented Nov 20, 2024

The entries in the time zone map are never modified after they are initialized.

Right. That's why you can use the returned absl::TimeZone without further synchronization.

I just saw the thread in #212 so I guess the suggestion is still to use std::lock_guard and the application should build its own cache to avoid too many absl::LoadTimeZone calls, is this right?

Yes. But you might also consider why you're calling absl::LoadTimeZone() so much.

// This mutex is intentionally "leaked" to avoid the static deinitialization
// order fiasco (std::mutex's destructor is not trivial on many platforms).
static std::mutex* time_zone_mutex = new std::mutex;
static std::shared_mutex* time_zone_mutex = new std::shared_mutex;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, but requiring C++17 for CCTZ is a non-starter (at least in the medium term).

@@ -58,7 +59,7 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {

// Check whether the time zone has already been loaded.
{
std::lock_guard<std::mutex> lock(TimeZoneMutex());
std::shared_lock<std::shared_mutex> lock(TimeZoneMutex());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are seeing contention on this mutex, which is only held over an unordered_map lookup, then I can only suggest that you are calling cctz::load_time_zone() way too much. Typically you would only load a time zone once, and then use the returned cctz::time_zone value after that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that our functions will take an optional timezone parameter (string) from the user input. We need to have a mechanism to load it once and store it somewhere (we probably want to avoid using lock again on a map). I am thinking to have a static map <name, timezone> to pre-load all time zones which is not ideal and seems to waste space since most timezones are not used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that our functions will take an optional timezone parameter (string) from the user input. We need to have a mechanism to load it once and store it somewhere (we probably want to avoid using lock again on a map).

My suggestion would be to change that type from a string to a cctz::time_zone or absl::TimeZone as they give you exactly that mechanism. Then the user would be able to load their timezone(s) of interest only once.

I am thinking to have a static map <name, timezone> to pre-load all time zones which is not ideal and seems to waste space since most timezones are not used.

Alternately, introduce your own "timezone" type and factory (if you don't want cctz/absl types in your vocabulary). The type would have an accessor for its held cctz/absl zone, and the by-string factory would maintain the map and only load requested zones, not all of them. (Note that "all time zones" is not even a supported concept.)

Or, you could keep your string parameter, but have some variety of internal static map, which, again, is not pre-loaded but rather filled on demand. In each case, the thread safety requirements of the map would be determined by the thread safety of your interfaces.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for suggestions. I will think a bit more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion would be to change that type from a string to a cctz::time_zone or absl::TimeZone

Thanks a lot for suggestions. I will think a bit more.

Not only is that simpler and gives the user control over when zones are loaded, it would also save you from having to deal with load failures.

Good luck.

@hengfengli
Copy link
Author

Close this. As suggested, we should avoid calling absl::LoadTimeZone() too much.

@hengfengli hengfengli closed this Nov 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants