Skip to content

Calling System.TimeZoneInfo.GetSystemTimeZones() is unacceptable slow calling it the first time. #8050

Closed
dotnet/runtime
#88368

Description

@JeroenBer

Android application type

.NET Android (net7.0-android, etc.)

Affected platform version

VS 2022 17.5.4

Description

After converting our code from Classic Xamarin to .NET Android we noticed that calling
System.TimeZoneInfo.GetSystemTimeZones() is unacceptable slow the first time. The second time it's fast, probably uses some internal caching.

E.g in fully optimized release mode:
Xamarin: 68ms
.NET Android (.net7.0): 670ms Almost 10 times slower!

In Debug mode the call can even take 4500ms the first time!

This is a problem since we do a lot of timezone conversion also directly after startup and you really notice this hickup from the first call.

Tested it on real device and Emulator Android 13.

Steps to Reproduce

Create a .NET Android app and call:

System.TimeZoneInfo.GetSystemTimeZones()

Notice how slow it is the first time.

Did you find any workaround?

No

Relevant log output

No response

Activity

  1. added
    Area: Mono RuntimeMono-related issues: BCL bugs, AOT issues, etc.
    and removed
    needs-triageIssues that need to be assigned.
    on May 18, 2023
  2. mdh1418 commented on Jun 21, 2023

    @mdh1418
    Member

    Hi @JeroenBer, when seeing the 4500ms, does that happen on both device and emulator? Could I get the specifications of the real device that the app is being ran on?

    Running in Debug on Android 13 emulator I want to try to reproduce the 4500ms timing, but so far, I've ran the .NET Android 7.0.100 workload and traced GetSystemTimeZones first two times at 1200ms, and the last 6 times at 700ms~600ms. I'll start my investigation off of what I'm seeing, but if the 4500ms timing happens to be a different configuration (for example, maybe using Debug with Hot Reload, which would run interpreter instead of JIT compilation) that would be helpful for me to reproduce locally.

    Are you grabbing the perf timing using dotnet dsrouter and dotnet trace or something else?

  3. mdh1418 commented on Jun 21, 2023

    @mdh1418
    Member

    Ignore, double posted by accident

  4. JeroenBer commented on Jun 21, 2023

    @JeroenBer
    Author

    Thanks for you interest, I will have to re-test and send you the info tomorrow or the day after.

  5. JeroenBer commented on Jun 22, 2023

    @JeroenBer
    Author

    Hi @mdh1418

    I used my own test program to reproduce it, you can find it here:
    https://github.com/JeroenBer/TestTimezonePerformance
    The solution has a Net7 and a Xamarin project for testing.

    I ran some tests again and below are the results I get, for first time I run test after startup. The Debug configuration I just start from VS in Debug mode.

    Device Configuration Xamarin AndroidAppNet 7
    Simulator Android 13 on AMD Ryzen Threadripper 3960X Debug 450ms 3200ms
    Simulator Android 13 on AMD Ryzen Threadripper 3960X Release 700ms 3200ms
    Google Pixel 6a Android UpsideDownCake Release 65ms 680ms
    Google Pixel 4 Android 13 Release 120ms 1100ms

    Notice on real devices in release mode .NET 7 has become 10x slower than Xamarin and it takes a huge time.

    I hope this helps ? Just let me know if you need more info.

  6. tarekgh commented on Jul 5, 2023

    @tarekgh
    Member

    This is a problem since we do a lot of timezone conversion also directly after startup and you really notice this hickup from the first call.

    Why are you enumerating all time zones to use for conversion in the first place? Could you please talk more about your scenario to understand what exactly you are doing? Usually, GetSystemTimeZones is costly but apps pay the cost one time. I don't even expect the apps to call this API more than once.

  7. JeroenBer commented on Jul 5, 2023

    @JeroenBer
    Author

    Yes we have a calendar app so we need to display appointments for different timezones at screen after startup. Since every Android / iOS version has a different list of supported timezones we need to create a mapping table at startup, for mapping application timezones (OS / device independent) to supported device timezones. Of course we only call this once during the application lifetime. Note that calling it a second time it is not slow anymore. It's only slow calling it the first time anyway (GetSystemTimeZones).

    But the point is that with the migration from Xamarin to .NET 7 something costly has suddenly become 10x times more costly at runtime and now is a noticeable hickup for users, so we see this as a problem. Have you been able to reproduce it yet ?

  8. tarekgh commented on Jul 5, 2023

    @tarekgh
    Member

    Since every Android / iOS version has a different list of supported timezones we need to create a mapping table at startup, for mapping application timezones (OS / device independent) to supported device timezones.

    Can you please give more details about how do you do the mapping using enumerating all system time zones?

  9. JeroenBer commented on Jul 5, 2023

    @JeroenBer
    Author

    We have a table with about 250 internal application timezones, we need this since our application runs on different platforms Windows, iOS, Android, MacOS. For each of these 250 timezones there is a list of possible Olson timezone ID's. For each we need to map it to check if it's available on the device, if none are available for a internal application timezone we need to remove the internal application timezones since it's not available on the specific device. If one is available we need to keep it and remember the mapping for further usage. From then on we only use our application timezones.
    Basically we build a Dictionary for fast mapping of application timezones to device timezones. For this procedure calling GetSystemTimeZones once is the fastest option, checking each timezone separately is much worse.

    Have you been able to reproduce the slow performance ?

  10. tarekgh commented on Jul 5, 2023

    @tarekgh
    Member

    Have you been able to reproduce the slow performance?

    It is expected to have perf cost especially on Linux/Mac based OSs because we must enumerate and open all time zone files on that device in addition doing some more initialization for other stuff related to the enumerated time zones.

    In your scenario, do you offer listing all available time zones to the user in the UI? or you are using your mapped dictionary when you need to use it? I am asking because you may delay validating the time zone till it gets used. and at that time you need to validate only one time zone instead of the whole list.

  11. JeroenBer commented on Jul 5, 2023

    @JeroenBer
    Author

    Well we need it for both so that's not really an option.

    I understand it's not lightning fast but how come it's 10x slower then it used to be ?

  12. mdh1418 commented on Jul 6, 2023

    @mdh1418
    Member

    Hi @JeroenBer, I haven't been able to procure a slow enough device, but I investigated the bottleneck areas for the current implementation. It boils down to GetSystemTimeZones() calling the unix constructor for every single timezone (424 on my emulator), within which will access the timezone database and attempt to populate on the fly the internal fields used as DisplayName, StandardName, and DaylightName values.

    I attempted to improve this perf slowdown by lazy initializing these properties in dotnet/runtime#88368, but unfortunately, GetSystemTimeZones() mandates an ordered sort based on DisplayName (which is the most costly of the three and takes ~90% of the constructor's time).

    I believe Classic Xamarin was not doing any of that, but populating the information from the timezone files directly.
    When you grab all system timezones, do you need the DisplayName? Or would Id/other fields suffice?

  13. JeroenBer commented on Jul 8, 2023

    @JeroenBer
    Author

    Hi @mdh1418 , thanks for looking into it. In our case we don't need DisplayName, StandardName or DaylightName.

    It's a shame they need to be sorted. The official doc doesn't specify on which field they would be sorted: https://learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo.getsystemtimezones?view=net-7.0
    Isn't it better to sort on Id ?

  14. tarekgh commented on Jul 9, 2023

    @tarekgh
    Member

    It's a shame they need to be sorted. The official doc doesn't specify on which field they would be sorted: learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo.getsystemtimezones?view=net-7.0

    The doc is clearly saying the following:

    The collection returned by this method is sorted by UTC offset and, for time zones that have the same UTC offset, by the display name using the current culture. For information about the display name, see [DisplayName](https://learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo.displayname?view=net-7.0).
    

    Isn't it better to sort on Id ?

    No, it is not better. Most common scenario using this API is to enumerate the time zones and display the list to the users in the UI. sorting with the display name is correct.

  15. locked and limited conversation to collaborators on Jan 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Area: Mono RuntimeMono-related issues: BCL bugs, AOT issues, etc.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions