Skip to content

REF: Localizer class to de-duplicate tzconversion code #46397

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

Merged
merged 40 commits into from
Apr 18, 2022

Conversation

jbrockmendel
Copy link
Member

@jbrockmendel jbrockmendel commented Mar 16, 2022

Second attempt at #46246. Perf impact isn't as bad, but is still ugly, see asvs posted below.

The lines:

        if info.use_utc:
            local_val = stamps[i]
        elif info.use_tzlocal:
            local_val = tz_convert_utc_to_tzlocal(stamps[i], tz)
        elif info.use_fixed:
            local_val = stamps[i] + info.delta
        else:
            pos = bisect_right_i8(tdata, stamps[i], info.ntrans) - 1
            local_val = stamps[i] + info.deltas[pos]

are repeated 5 times in this file, once more with small edits in tzconversion.pyx, and potentially a couple others. So I'd really like to turn them into either a Localizer method or just a module-level function. Doing so causes even bigger performance hits.

Also these lines are liable to change as we add zoneinfo and non-nano support, so making them a function would be helpful for keeping them in sync.

The only idea I have for troubleshooting perf is to move the info.use_foo checks (or the info.delta and info.deltas lookups) outside of the loop, but that doesn't seem to make any difference.

Asking for help from the big guns cc @jreback @WillAyd @realead @da-woods

time asv continuous -E virtualenv -f 1.01 main HEAD -b tslibs.normalize -b TimeDT64ArrToPeriodArr -b TimeIntsToPydatetime -b TimeResolution --record-samples --append-samples
[...]
       before           after         ratio
     [49937b93]       [c1085df7]
     <perf-gb-mask>       <ref-localizer-2>
+     1.62±0.05μs      2.45±0.03μs     1.51  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 5000, datetime.timezone.utc)
+     1.89±0.02ms       2.68±0.4ms     1.41  tslibs.normalize.Normalize.time_is_date_array_normalized(1000000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      4.19±0.1μs       5.70±0.6μs     1.36  tslibs.resolution.TimeResolution.time_get_resolution('us', 0, datetime.timezone(datetime.timedelta(seconds=3600)))
+      4.57±0.1μs       6.12±0.2μs     1.34  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 3000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      1.90±0.1μs       2.51±0.3μs     1.32  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 7000, None)
+     4.11±0.06μs      5.42±0.07μs     1.32  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, datetime.timezone(datetime.timedelta(seconds=3600)))
+     4.00±0.09μs       5.23±0.2μs     1.31  tslibs.resolution.TimeResolution.time_get_resolution('us', 1, datetime.timezone(datetime.timedelta(seconds=3600)))
+     4.04±0.09μs       5.27±0.5μs     1.30  tslibs.resolution.TimeResolution.time_get_resolution('m', 0, datetime.timezone(datetime.timedelta(seconds=3600)))
+        974±10ns       1.26±0.1μs     1.29  tslibs.resolution.TimeResolution.time_get_resolution('ns', 0, tzlocal())
+      4.76±0.1μs       6.13±0.4μs     1.29  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2011, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.76±0.08μs       2.26±0.1μs     1.28  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, tzlocal())
+     1.77±0.02μs       2.26±0.3μs     1.28  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 6000, tzlocal())
+     1.67±0.03μs      2.13±0.01μs     1.28  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4006, None)
+     2.12±0.07μs      2.70±0.05μs     1.27  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.70±0.05μs      2.16±0.02μs     1.27  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 3000, tzlocal())
+     1.64±0.01μs      2.07±0.04μs     1.27  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 7000, datetime.timezone.utc)
+     2.27±0.04μs      2.87±0.03μs     1.27  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        951±10ns      1.20±0.01μs     1.26  tslibs.resolution.TimeResolution.time_get_resolution('us', 1, datetime.timezone.utc)
+        244±20μs         308±30μs     1.26  tslibs.resolution.TimeResolution.time_get_resolution('us', 10000, datetime.timezone.utc)
+      8.57±0.2μs       10.8±0.6μs     1.26  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 4000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+         923±7ns      1.16±0.04μs     1.26  tslibs.resolution.TimeResolution.time_get_resolution('s', 0, datetime.timezone.utc)
+     1.70±0.03μs      2.14±0.03μs     1.26  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 8000, None)
+     4.40±0.05μs       5.53±0.2μs     1.26  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 4000, None)
+     1.67±0.05μs      2.10±0.01μs     1.26  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, None)
+     1.53±0.04μs       1.92±0.1μs     1.26  tslibs.resolution.TimeResolution.time_get_resolution('ns', 1, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     2.31±0.03μs      2.90±0.03μs     1.25  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 5000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.42±0.03μs      1.78±0.04μs     1.25  tslibs.resolution.TimeResolution.time_get_resolution('us', 1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.64±0.02μs      2.05±0.03μs     1.25  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.97±0.07μs       2.47±0.3μs     1.25  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, datetime.timezone.utc)
+     7.16±0.05μs       8.95±0.6μs     1.25  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 3000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     2.28±0.04μs       2.85±0.2μs     1.25  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 7000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     4.94±0.08μs       6.15±0.2μs     1.25  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 7000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      1.03±0.1μs      1.28±0.01μs     1.25  tslibs.resolution.TimeResolution.time_get_resolution('us', 1, None)
+     2.08±0.04μs      2.58±0.02μs     1.24  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 5000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+         286±9μs          355±4μs     1.24  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 9000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.67±0.04μs      2.07±0.03μs     1.24  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4006, datetime.timezone.utc)
+     1.03±0.04μs         1.28±0μs     1.24  tslibs.resolution.TimeResolution.time_get_resolution('s', 0, None)
+      4.96±0.1μs       6.14±0.1μs     1.24  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     2.10±0.03μs      2.60±0.05μs     1.24  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4006, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     4.15±0.07μs      5.13±0.03μs     1.24  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 6000, None)
+      4.68±0.1μs       5.78±0.1μs     1.24  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      4.95±0.1μs       6.12±0.1μs     1.23  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4006, datetime.timezone(datetime.timedelta(seconds=3600)))
+        674±20μs         832±70μs     1.23  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 9000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      4.84±0.1μs       5.96±0.2μs     1.23  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     6.37±0.09μs       7.83±0.2μs     1.23  tslibs.resolution.TimeResolution.time_get_resolution('m', 100, datetime.timezone(datetime.timedelta(seconds=3600)))
+     5.13±0.04μs       6.29±0.2μs     1.23  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      5.00±0.4μs       6.11±0.1μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 8000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     3.67±0.07ms      4.49±0.07ms     1.22  tslibs.resolution.TimeResolution.time_get_resolution('s', 100, tzlocal())
+     1.92±0.09μs      2.35±0.02μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 9000, None)
+     1.55±0.03μs      1.89±0.03μs     1.22  tslibs.resolution.TimeResolution.time_get_resolution('m', 0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      4.92±0.3μs       6.02±0.2μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 5000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.06±0.03μs       1.30±0.1μs     1.22  tslibs.resolution.TimeResolution.time_get_resolution('m', 0, None)
+         208±4μs        254±0.9μs     1.22  tslibs.resolution.TimeResolution.time_get_resolution('m', 10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     9.37±0.08μs      11.4±0.06μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 5000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+        27.7±2ms       33.8±0.2ms     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 7000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     2.32±0.03μs       2.83±0.1μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 8000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     2.30±0.03μs      2.81±0.03μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 6000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      5.41±0.2μs       6.60±0.6μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 7000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.77±0.06μs      2.16±0.02μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 7000, tzlocal())
+     1.45±0.01μs      1.76±0.05μs     1.22  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     4.02±0.02μs       4.89±0.5μs     1.22  tslibs.resolution.TimeResolution.time_get_resolution('D', 1, datetime.timezone(datetime.timedelta(seconds=3600)))
+     2.46±0.08μs      2.98±0.09μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     2.11±0.07μs       2.56±0.2μs     1.22  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 7000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     2.97±0.02μs      3.61±0.06μs     1.21  tslibs.resolution.TimeResolution.time_get_resolution('m', 100, None)
+     2.05±0.03μs      2.48±0.08μs     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 3000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      4.87±0.2μs       5.91±0.2μs     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 6000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      7.60±0.4μs       9.21±0.2μs     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 11000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.01±0.01μs      1.22±0.03μs     1.21  tslibs.resolution.TimeResolution.time_get_resolution('us', 0, None)
+      7.10±0.2μs       8.61±0.2μs     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1011, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.73±0.04μs      2.09±0.02μs     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, datetime.timezone.utc)
+      4.11±0.2μs       4.98±0.2μs     1.21  tslibs.resolution.TimeResolution.time_get_resolution('D', 0, datetime.timezone(datetime.timedelta(seconds=3600)))
+        69.1±3ms         83.7±5ms     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 4006, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      64.8±0.6ms         78.5±4ms     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 8000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     4.02±0.05μs      4.86±0.09μs     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1011, datetime.timezone.utc)
+      1.85±0.2μs      2.24±0.04μs     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1000, None)
+      28.1±0.6ms         34.0±2ms     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+         205±4μs          248±1μs     1.21  tslibs.resolution.TimeResolution.time_get_resolution('m', 10000, None)
+      5.13±0.2μs       6.18±0.3μs     1.21  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     4.17±0.06μs       5.03±0.5μs     1.20  tslibs.resolution.TimeResolution.time_get_resolution('ns', 1, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.77±0.03μs      2.13±0.02μs     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 5000, tzlocal())
+         551±5μs         663±40μs     1.20  tslibs.resolution.TimeResolution.time_get_resolution('s', 10000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.72±0.04μs      2.07±0.01μs     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 5000, None)
+     5.08±0.06μs      6.11±0.04μs     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 9000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.76±0.04μs      2.11±0.02μs     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 6000, None)
+     2.55±0.07μs       3.06±0.3μs     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        24.9±1μs       29.9±0.7μs     1.20  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 100, datetime.timezone.utc)
+      2.29±0.1μs      2.75±0.07μs     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 3000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      4.52±0.3μs      5.43±0.06μs     1.20  tslibs.resolution.TimeResolution.time_get_resolution('h', 1, datetime.timezone(datetime.timedelta(seconds=3600)))
+     3.56±0.06ms       4.27±0.2ms     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 11000, tzlocal())
+     1.35±0.02μs      1.62±0.01μs     1.20  tslibs.resolution.TimeResolution.time_get_resolution('s', 0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      23.6±0.5ms       28.4±0.5ms     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 2000, datetime.timezone.utc)
+      3.84±0.2μs      4.61±0.07μs     1.20  tslibs.resolution.TimeResolution.time_get_resolution('m', 100, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      28.5±0.9ms         34.1±3ms     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 4000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      26.4±0.3ms         31.6±2ms     1.20  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 11000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     5.27±0.09μs       6.29±0.4μs     1.19  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 12000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      21.9±0.5μs       26.2±0.6μs     1.19  tslibs.normalize.Normalize.time_is_date_array_normalized(10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      2.37±0.1μs       2.82±0.1μs     1.19  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 2000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      7.79±0.5μs       9.28±0.1μs     1.19  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 7000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      25.6±0.2μs       30.5±0.6μs     1.19  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 100, None)
+     2.31±0.08μs       2.75±0.1μs     1.19  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     3.38±0.02ms       4.01±0.1ms     1.19  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 10000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      27.4±0.6ms         32.5±2ms     1.19  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 6000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      5.16±0.2μs       6.13±0.1μs     1.19  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 12000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.77±0.04μs      2.11±0.01μs     1.19  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4006, tzlocal())
+     2.07±0.05μs       2.45±0.2μs     1.19  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.60±0.01μs      1.90±0.03μs     1.19  tslibs.resolution.TimeResolution.time_get_resolution('us', 0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+         209±4μs          248±5μs     1.19  tslibs.resolution.TimeResolution.time_get_resolution('ns', 10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.75±0.06μs      2.07±0.02μs     1.19  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 3000, datetime.timezone.utc)
+      2.24±0.2μs      2.65±0.02μs     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 10000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      5.42±0.1μs       6.42±0.7μs     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4006, datetime.timezone(datetime.timedelta(seconds=3600)))
+      3.64±0.2ms      4.31±0.04ms     1.18  tslibs.resolution.TimeResolution.time_get_resolution('m', 100, tzlocal())
+     2.24±0.07μs       2.65±0.2μs     1.18  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.78±0.03μs       2.10±0.2μs     1.18  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     2.02±0.02μs       2.39±0.2μs     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2011, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+        66.3±2ms         78.4±1ms     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 11000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      2.26±0.1μs       2.67±0.1μs     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      26.2±0.7μs       30.9±0.3μs     1.18  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 100, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      1.89±0.1μs      2.23±0.03μs     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 5000, None)
+         340±6ms         401±20ms     1.18  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 1000000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      25.3±0.4ms       29.9±0.5ms     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 6000, datetime.timezone.utc)
+        65.1±1ms       76.8±0.5ms     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 6000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     2.07±0.03μs       2.44±0.1μs     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 11000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      3.05±0.1μs      3.60±0.04μs     1.18  tslibs.resolution.TimeResolution.time_get_resolution('m', 100, datetime.timezone.utc)
+        67.4±1ms         79.3±3ms     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 7000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      1.46±0.1μs      1.72±0.07μs     1.18  tslibs.resolution.TimeResolution.time_get_resolution('h', 0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.73±0.04μs       2.03±0.1μs     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 7000, None)
+     1.46±0.03μs      1.72±0.04μs     1.18  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 0, None)
+     1.88±0.09μs       2.21±0.1μs     1.18  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 9000, tzlocal())
+     5.28±0.02μs      6.20±0.08μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 4000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      2.40±0.1μs      2.82±0.07μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4006, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      5.14±0.2μs      6.04±0.05μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 11000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     2.13±0.02μs       2.50±0.1μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 8000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.70±0.08μs      1.99±0.04μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 9000, tzlocal())
+      1.81±0.2μs      2.13±0.05μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 7000, datetime.timezone.utc)
+     1.75±0.02μs       2.05±0.2μs     1.17  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 1, None)
+         990±9ns      1.16±0.07μs     1.17  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, datetime.timezone.utc)
+     3.57±0.04ms       4.18±0.3ms     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 10000, tzlocal())
+        207±10μs          242±3μs     1.17  tslibs.resolution.TimeResolution.time_get_resolution('m', 10000, datetime.timezone.utc)
+      8.14±0.2μs       9.52±0.3μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1011, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     2.24±0.05μs       2.62±0.2μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 9000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      1.74±0.1μs      2.03±0.08μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 12000, datetime.timezone.utc)
+     1.05±0.04μs      1.23±0.02μs     1.17  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, None)
+     8.17±0.04μs       9.52±0.4μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 5000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      2.42±0.1μs      2.82±0.03μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 3000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+         360±4ms         420±20ms     1.17  tslibs.resolution.TimeResolution.time_get_resolution('h', 10000, tzlocal())
+        615±10μs         715±10μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 1011, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.01±0.06μs      1.17±0.03μs     1.16  tslibs.resolution.TimeResolution.time_get_resolution('D', 0, tzlocal())
+     1.82±0.04μs      2.11±0.01μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 11000, None)
+      29.8±0.5μs       34.6±0.7μs     1.16  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 100, datetime.timezone(datetime.timedelta(seconds=3600)))
+         243±4μs         283±40μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 1000, datetime.timezone.utc)
+      5.12±0.3μs       5.95±0.1μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     4.51±0.03μs       5.24±0.1μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 4006, None)
+      7.70±0.2μs       8.94±0.5μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 4000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      2.27±0.2μs      2.63±0.07μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      7.95±0.1μs       9.22±0.6μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2011, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.03±0.01μs      1.19±0.03μs     1.16  tslibs.resolution.TimeResolution.time_get_resolution('D', 0, None)
+     1.72±0.03μs       2.00±0.1μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 9000, None)
+        912±30ns      1.06±0.09μs     1.16  tslibs.resolution.TimeResolution.time_get_resolution('m', 0, datetime.timezone.utc)
+     4.26±0.08μs       4.94±0.1μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1011, None)
+      2.22±0.2μs      2.57±0.08μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 12000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      2.51±0.2μs      2.91±0.07μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     4.98±0.08μs       5.76±0.2μs     1.16  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 3000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        37.1±2μs       42.9±0.3μs     1.15  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 100, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      25.5±0.8ms       29.4±0.5ms     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 6000, None)
+      25.5±0.9ms       29.4±0.2ms     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 1011, datetime.timezone(datetime.timedelta(seconds=3600)))
+         207±6μs         238±10μs     1.15  tslibs.resolution.TimeResolution.time_get_resolution('h', 10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     4.42±0.03μs       5.10±0.4μs     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 4000, datetime.timezone.utc)
+     4.13±0.09μs      4.76±0.02μs     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 3000, datetime.timezone.utc)
+      4.16±0.2μs      4.79±0.05μs     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2011, datetime.timezone.utc)
+        25.8±1ms       29.7±0.2ms     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 2000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        967±10ns      1.11±0.03μs     1.15  tslibs.resolution.TimeResolution.time_get_resolution('ns', 1, None)
+     1.75±0.04μs       2.02±0.1μs     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 8000, tzlocal())
+      1.84±0.1μs      2.11±0.02μs     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 12000, None)
+        21.8±1ms       25.0±0.3ms     1.15  tslibs.resolution.TimeResolution.time_get_resolution('m', 1000000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.81±0.05μs       2.08±0.2μs     1.15  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, datetime.timezone.utc)
+     3.71±0.02ms       4.26±0.1ms     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1000, tzlocal())
+        38.3±1μs         44.0±1μs     1.15  tslibs.resolution.TimeResolution.time_get_resolution('D', 1, tzlocal())
+        231±10μs          265±2μs     1.15  tslibs.resolution.TimeResolution.time_get_resolution('m', 10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.85±0.08μs      2.13±0.03μs     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 3000, None)
+      1.50±0.1μs      1.72±0.03μs     1.15  tslibs.resolution.TimeResolution.time_get_resolution('h', 1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+         373±8ms         427±10ms     1.15  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 10000, tzlocal())
+        57.5±2ms         65.8±1ms     1.14  tslibs.resolution.TimeResolution.time_get_resolution('s', 1000000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      7.52±0.4μs       8.60±0.7μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 8000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     4.90±0.07μs      5.59±0.03μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      4.84±0.1μs       5.52±0.5μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 11000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      25.4±0.3ms         28.9±2ms     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 9000, datetime.timezone(datetime.timedelta(seconds=3600)))
+         203±5μs          231±6μs     1.14  tslibs.resolution.TimeResolution.time_get_resolution('D', 10000, None)
+     1.10±0.09μs      1.25±0.01μs     1.14  tslibs.resolution.TimeResolution.time_get_resolution('s', 0, tzlocal())
+      4.54±0.4μs      5.17±0.03μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 9000, None)
+     3.26±0.04μs       3.72±0.3μs     1.14  tslibs.resolution.TimeResolution.time_get_resolution('us', 100, None)
+     6.28±0.07μs       7.16±0.5μs     1.14  tslibs.resolution.TimeResolution.time_get_resolution('us', 100, datetime.timezone(datetime.timedelta(seconds=3600)))
+        25.1±1ms       28.6±0.4ms     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 1000, None)
+      26.2±0.3ms       29.9±0.8ms     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 11000, datetime.timezone.utc)
+      4.22±0.2ms      4.80±0.01ms     1.14  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      5.37±0.2μs       6.11±0.1μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4000, datetime.timezone(datetime.timedelta(seconds=3600)))
+         354±9ms         403±20ms     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 9000, tzlocal())
+        26.7±1ms       30.4±0.9ms     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 6000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.69±0.02μs       1.92±0.1μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 10000, datetime.timezone.utc)
+         266±5μs         302±10μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 1011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+         239±8μs         271±10μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 1011, datetime.timezone.utc)
+     4.25±0.07ms       4.82±0.1ms     1.13  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 10000, datetime.timezone.utc)
+         202±2μs         229±10μs     1.13  tslibs.resolution.TimeResolution.time_get_resolution('s', 10000, datetime.timezone.utc)
+      3.83±0.1ms       4.33±0.2ms     1.13  tslibs.resolution.TimeResolution.time_get_resolution('ns', 100, tzlocal())
+     1.74±0.04μs      1.97±0.04μs     1.13  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 6000, datetime.timezone.utc)
+     1.82±0.02μs      2.06±0.05μs     1.13  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 1, datetime.timezone.utc)
+      6.80±0.5μs       7.69±0.2μs     1.13  tslibs.resolution.TimeResolution.time_get_resolution('h', 100, datetime.timezone(datetime.timedelta(seconds=3600)))
+     2.61±0.09μs      2.95±0.05μs     1.13  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+         195±3μs          220±9μs     1.13  tslibs.resolution.TimeResolution.time_get_resolution('D', 10000, datetime.timezone.utc)
+     2.12±0.07μs       2.39±0.2μs     1.13  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, None)
+      1.84±0.1μs       2.07±0.2μs     1.13  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 6000, datetime.timezone.utc)
+      2.39±0.1μs       2.69±0.2μs     1.13  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 2011, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      5.14±0.3μs      5.77±0.07μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        61.1±3ms       68.6±0.3ms     1.12  tslibs.resolution.TimeResolution.time_get_resolution('m', 1000000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.88±0.06μs      2.11±0.03μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 11000, None)
+      1.99±0.2μs      2.23±0.03μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 2011, tzlocal())
+        39.6±1μs       44.4±0.6μs     1.12  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, tzlocal())
+         450±3ms         504±20ms     1.12  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 1000000, datetime.timezone.utc)
+      1.90±0.1μs      2.13±0.02μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4000, None)
+         442±3ms         495±30ms     1.12  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 1000000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     3.57±0.02ms       3.99±0.2ms     1.12  tslibs.resolution.TimeResolution.time_get_resolution('h', 100, tzlocal())
+      1.90±0.2μs      2.12±0.03μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1000, tzlocal())
+     2.48±0.02μs      2.77±0.02μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     2.40±0.09μs      2.68±0.06μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4006, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      5.21±0.1μs       5.80±0.4μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 9000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        42.4±2μs       47.2±0.3μs     1.11  tslibs.resolution.TimeResolution.time_get_resolution('s', 1, tzlocal())
+      3.79±0.1ms      4.21±0.01ms     1.11  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 100, tzlocal())
+     4.46±0.04μs       4.96±0.2μs     1.11  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 0, datetime.timezone(datetime.timedelta(seconds=3600)))
+      5.48±0.4μs      6.09±0.07μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 3000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      1.88±0.1μs      2.09±0.03μs     1.11  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, None)
+      5.27±0.2μs       5.86±0.2μs     1.11  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, datetime.timezone(datetime.timedelta(seconds=3600)))
+         223±2μs         248±20μs     1.11  tslibs.resolution.TimeResolution.time_get_resolution('s', 10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     3.94±0.06μs      4.38±0.06μs     1.11  tslibs.resolution.TimeResolution.time_get_resolution('h', 100, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      1.83±0.1μs      2.03±0.02μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4000, datetime.timezone.utc)
+      5.97±0.4μs       6.63±0.6μs     1.11  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.34±0.03μs      1.49±0.03μs     1.11  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 0, datetime.timezone.utc)
+     1.94±0.04μs      2.15±0.02μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4006, None)
+     2.33±0.09μs      2.58±0.04μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.84±0.08μs      2.04±0.09μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1000, datetime.timezone.utc)
+     2.59±0.05μs      2.86±0.02μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 12000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      6.05±0.1μs       6.69±0.2μs     1.11  tslibs.resolution.TimeResolution.time_get_resolution('ns', 100, datetime.timezone(datetime.timedelta(seconds=3600)))
+      7.79±0.7μs      8.60±0.03μs     1.10  tslibs.resolution.TimeResolution.time_get_resolution('h', 100, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      28.3±0.7ms       31.3±0.5ms     1.10  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      1.77±0.2μs      1.95±0.04μs     1.10  tslibs.resolution.TimeResolution.time_get_resolution('h', 1, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      5.52±0.4μs       6.06±0.1μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 11000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.88±0.03μs      2.07±0.04μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 11000, tzlocal())
+         605±8μs         664±10μs     1.10  tslibs.resolution.TimeResolution.time_get_resolution('m', 10000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+        949±20ns      1.04±0.05μs     1.10  tslibs.resolution.TimeResolution.time_get_resolution('us', 0, datetime.timezone.utc)
+      1.95±0.1μs      2.14±0.04μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4000, tzlocal())
+      5.58±0.2μs      6.12±0.05μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        919±30ns      1.01±0.03μs     1.10  tslibs.resolution.TimeResolution.time_get_resolution('ns', 1, datetime.timezone.utc)
+      4.77±0.1μs       5.21±0.3μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 9000, datetime.timezone(datetime.timedelta(seconds=3600)))
+         328±3ms          358±5ms     1.09  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 1000000, None)
+      3.69±0.1ms       4.03±0.2ms     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2011, tzlocal())
+     2.07±0.01μs       2.26±0.1μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 9000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+         335±5ms         365±30ms     1.09  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 1000000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        63.2±2ms       68.9±0.9ms     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 1000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      2.56±0.1μs      2.79±0.04μs     1.09  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      1.90±0.1μs      2.07±0.03μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 2000, datetime.timezone.utc)
+     1.97±0.03μs      2.14±0.07μs     1.08  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 1, None)
+     2.03±0.09μs      2.20±0.08μs     1.08  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     7.09±0.06μs       7.66±0.4μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.58±0.06μs      1.70±0.05μs     1.08  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 0, tzlocal())
+      35.2±0.8μs       38.0±0.7μs     1.08  tslibs.normalize.Normalize.time_is_date_array_normalized(10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        21.8±1ms       23.5±0.2ms     1.08  tslibs.resolution.TimeResolution.time_get_resolution('D', 1000000, None)
+      2.01±0.1μs      2.17±0.09μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 2000, tzlocal())
+     1.39±0.02μs      1.50±0.06μs     1.08  tslibs.resolution.TimeResolution.time_get_resolution('ns', 1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     2.61±0.08μs      2.81±0.04μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 3000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+         338±4ms         363±20ms     1.07  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 1000000, datetime.timezone.utc)
+        549±30ns         590±20ns     1.07  tslibs.normalize.Normalize.time_is_date_array_normalized(1, None)
+      2.44±0.1μs      2.62±0.02μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 11000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.94±0.08μs      2.08±0.04μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4006, datetime.timezone.utc)
+      5.17±0.3μs      5.52±0.04μs     1.07  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      3.40±0.1μs      3.63±0.02μs     1.07  tslibs.resolution.TimeResolution.time_get_resolution('h', 100, None)
+      2.67±0.1ms      2.83±0.02ms     1.06  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, datetime.timezone.utc)
+      2.52±0.1μs      2.67±0.02μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 11000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.51±0.05μs      1.57±0.02μs     1.04  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 0, tzlocal())
+      43.7±0.2μs         45.5±2μs     1.04  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 100, datetime.timezone(datetime.timedelta(seconds=3600)))
-     3.53±0.03ms      3.36±0.01ms     0.95  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, datetime.timezone(datetime.timedelta(seconds=3600)))
-     1.34±0.04ms      1.27±0.02ms     0.95  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('date', 10000, None)
-        279±20ms          265±4ms     0.95  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 1000000, datetime.timezone(datetime.timedelta(seconds=3600)))
-         276±9ms          261±2ms     0.95  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 1000000, datetime.timezone.utc)
-         151±3ms          142±2ms     0.94  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('date', 1000000, None)
-      2.29±0.1μs      2.09±0.04μs     0.91  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-     1.57±0.03μs      1.41±0.09μs     0.90  tslibs.resolution.TimeResolution.time_get_resolution('ns', 0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-      3.54±0.2ms      3.07±0.05ms     0.87  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 10000, datetime.timezone.utc)

@jreback
Copy link
Contributor

jreback commented Mar 16, 2022

can u make just a function to avoid repeating w/o creating the localizer obj?

@jbrockmendel
Copy link
Member Author

can u make just a function to avoid repeating w/o creating the localizer obj?

similar perf hit

@WillAyd
Copy link
Member

WillAyd commented Mar 17, 2022

Any insights in checking the generated C code before/after? My best guess is that Cython may be generating extra checks for class attribute access beyond what normal struct member access would do

@realead
Copy link
Contributor

realead commented Mar 18, 2022

This is the generated C-code for the old version:

    /* "pandas/_libs/tslibs/vectorized.pyx":137
 *             continue
 * 
 *         if use_utc:             # <<<<<<<<<<<<<<
 *             local_val = value
 *         elif use_tzlocal:
 */
    __pyx_t_9 = (__pyx_v_use_utc != 0);
    if (__pyx_t_9) {

      /* "pandas/_libs/tslibs/vectorized.pyx":138
 * 
 *         if use_utc:
 *             local_val = value             # <<<<<<<<<<<<<<
 *         elif use_tzlocal:
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 */
      __pyx_v_local_val = __pyx_v_value;

      /* "pandas/_libs/tslibs/vectorized.pyx":137
 *             continue
 * 
 *         if use_utc:             # <<<<<<<<<<<<<<
 *             local_val = value
 *         elif use_tzlocal:
 */
      goto __pyx_L15;
    }

    /* "pandas/_libs/tslibs/vectorized.pyx":139
 *         if use_utc:
 *             local_val = value
 *         elif use_tzlocal:             # <<<<<<<<<<<<<<
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif use_fixed:
 */
    __pyx_t_9 = (__pyx_v_use_tzlocal != 0);
    if (__pyx_t_9) {

      /* "pandas/_libs/tslibs/vectorized.pyx":140
 *             local_val = value
 *         elif use_tzlocal:
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)             # <<<<<<<<<<<<<<
 *         elif use_fixed:
 *             local_val = value + delta
 */
      __pyx_t_24 = __pyx_f_6pandas_5_libs_6tslibs_12tzconversion_tz_convert_utc_to_tzlocal(__pyx_v_value, __pyx_v_tz, NULL); if (unlikely(__pyx_t_24 == ((__pyx_t_5numpy_int64_t)-1LL) && PyErr_Occurred())) __PYX_ERR(0, 140, __pyx_L1_error)
      __pyx_v_local_val = __pyx_t_24;

      /* "pandas/_libs/tslibs/vectorized.pyx":139
 *         if use_utc:
 *             local_val = value
 *         elif use_tzlocal:             # <<<<<<<<<<<<<<
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif use_fixed:
 */
      goto __pyx_L15;
    }

    /* "pandas/_libs/tslibs/vectorized.pyx":141
 *         elif use_tzlocal:
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif use_fixed:             # <<<<<<<<<<<<<<
 *             local_val = value + delta
 *         else:
 */
    __pyx_t_9 = (__pyx_v_use_fixed != 0);
    if (__pyx_t_9) {

      /* "pandas/_libs/tslibs/vectorized.pyx":142
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif use_fixed:
 *             local_val = value + delta             # <<<<<<<<<<<<<<
 *         else:
 *             pos = bisect_right_i8(tdata, value, ntrans) - 1
 */
      __pyx_v_local_val = (__pyx_v_value + __pyx_v_delta);

      /* "pandas/_libs/tslibs/vectorized.pyx":141
 *         elif use_tzlocal:
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif use_fixed:             # <<<<<<<<<<<<<<
 *             local_val = value + delta
 *         else:
 */
      goto __pyx_L15;
    }

    /* "pandas/_libs/tslibs/vectorized.pyx":144
 *             local_val = value + delta
 *         else:
 *             pos = bisect_right_i8(tdata, value, ntrans) - 1             # <<<<<<<<<<<<<<
 *             local_val = value + deltas[pos]
 * 
 */
    /*else*/ {
      __pyx_v_pos = (__pyx_f_6pandas_5_libs_6tslibs_12tzconversion_bisect_right_i8(__pyx_v_tdata, __pyx_v_value, __pyx_v_ntrans) - 1);

      /* "pandas/_libs/tslibs/vectorized.pyx":145
 *         else:
 *             pos = bisect_right_i8(tdata, value, ntrans) - 1
 *             local_val = value + deltas[pos]             # <<<<<<<<<<<<<<
 * 
 *             if use_pytz:
 */
      if (unlikely(!__pyx_v_deltas.memview)) { __Pyx_RaiseUnboundLocalError("deltas"); __PYX_ERR(0, 145, __pyx_L1_error) }
      __pyx_t_19 = __pyx_v_pos;
      __pyx_v_local_val = (__pyx_v_value + (*((__pyx_t_5numpy_int64_t *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_int64_t *) __pyx_v_deltas.data) + __pyx_t_19)) ))));

and this is the generated C-code for the new version:

   /* "pandas/_libs/tslibs/vectorized.pyx":168
 *             continue
 * 
 *         if info.use_utc:             # <<<<<<<<<<<<<<
 *             local_val = value
 *         elif info.use_tzlocal:
 */
    __pyx_t_7 = (__pyx_v_info->use_utc != 0);
    if (__pyx_t_7) {

      /* "pandas/_libs/tslibs/vectorized.pyx":169
 * 
 *         if info.use_utc:
 *             local_val = value             # <<<<<<<<<<<<<<
 *         elif info.use_tzlocal:
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 */
      __pyx_v_local_val = __pyx_v_value;

      /* "pandas/_libs/tslibs/vectorized.pyx":168
 *             continue
 * 
 *         if info.use_utc:             # <<<<<<<<<<<<<<
 *             local_val = value
 *         elif info.use_tzlocal:
 */
      goto __pyx_L8;
    }

    /* "pandas/_libs/tslibs/vectorized.pyx":170
 *         if info.use_utc:
 *             local_val = value
 *         elif info.use_tzlocal:             # <<<<<<<<<<<<<<
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif info.use_fixed:
 */
    __pyx_t_7 = (__pyx_v_info->use_tzlocal != 0);
    if (__pyx_t_7) {

      /* "pandas/_libs/tslibs/vectorized.pyx":171
 *             local_val = value
 *         elif info.use_tzlocal:
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)             # <<<<<<<<<<<<<<
 *         elif info.use_fixed:
 *             local_val = value + info.delta
 */
      __pyx_t_14 = __pyx_f_6pandas_5_libs_6tslibs_12tzconversion_tz_convert_utc_to_tzlocal(__pyx_v_value, __pyx_v_tz, NULL); if (unlikely(__pyx_t_14 == ((__pyx_t_5numpy_int64_t)-1LL) && PyErr_Occurred())) __PYX_ERR(1, 171, __pyx_L1_error)
      __pyx_v_local_val = __pyx_t_14;

      /* "pandas/_libs/tslibs/vectorized.pyx":170
 *         if info.use_utc:
 *             local_val = value
 *         elif info.use_tzlocal:             # <<<<<<<<<<<<<<
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif info.use_fixed:
 */
      goto __pyx_L8;
    }

    /* "pandas/_libs/tslibs/vectorized.pyx":172
 *         elif info.use_tzlocal:
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif info.use_fixed:             # <<<<<<<<<<<<<<
 *             local_val = value + info.delta
 *         else:
 */
    __pyx_t_7 = (__pyx_v_info->use_fixed != 0);
    if (__pyx_t_7) {

      /* "pandas/_libs/tslibs/vectorized.pyx":173
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif info.use_fixed:
 *             local_val = value + info.delta             # <<<<<<<<<<<<<<
 *         else:
 *             pos = bisect_right_i8(tdata, value, info.ntrans) - 1
 */
      __pyx_v_local_val = (__pyx_v_value + __pyx_v_info->delta);

      /* "pandas/_libs/tslibs/vectorized.pyx":172
 *         elif info.use_tzlocal:
 *             local_val = tz_convert_utc_to_tzlocal(value, tz)
 *         elif info.use_fixed:             # <<<<<<<<<<<<<<
 *             local_val = value + info.delta
 *         else:
 */
      goto __pyx_L8;
    }

    /* "pandas/_libs/tslibs/vectorized.pyx":175
 *             local_val = value + info.delta
 *         else:
 *             pos = bisect_right_i8(tdata, value, info.ntrans) - 1             # <<<<<<<<<<<<<<
 *             local_val = value + info.deltas[pos]
 * 
 */
    /*else*/ {
      __pyx_v_pos = (__pyx_f_6pandas_5_libs_6tslibs_12tzconversion_bisect_right_i8(__pyx_v_tdata, __pyx_v_value, __pyx_v_info->ntrans) - 1);

      /* "pandas/_libs/tslibs/vectorized.pyx":176
 *         else:
 *             pos = bisect_right_i8(tdata, value, info.ntrans) - 1
 *             local_val = value + info.deltas[pos]             # <<<<<<<<<<<<<<
 * 
 *             if info.use_pytz:
 */
      if (unlikely(!__pyx_v_info->deltas.memview)) {PyErr_SetString(PyExc_AttributeError,"Memoryview is not initialized");__PYX_ERR(1, 176, __pyx_L1_error)}
      __pyx_t_12 = __pyx_v_pos;
      __pyx_v_local_val = (__pyx_v_value + (*((__pyx_t_5numpy_int64_t const  *) ( /* dim=0 */ ((char *) (((__pyx_t_5numpy_int64_t const  *) __pyx_v_info->deltas.data) + __pyx_t_12)) ))));

Nothing that would catch my eye... Do we know which of the branches is responsible for the slowdown or do all of them become slower?

@jbrockmendel
Copy link
Member Author

Do we know which of the branches is responsible for the slowdown or do all of them become slower?

I don't see any clear pattern, no.

@WillAyd
Copy link
Member

WillAyd commented Mar 24, 2022

Yea that makes sense to me. Unless it gets optimized away (compiler will warn for this as unused variable)

@jbrockmendel
Copy link
Member Author

jbrockmendel commented Mar 25, 2022

@WillAyd

Yea that makes sense to me.

I don't suppose that was in reference to my "compiler takes the checks outside the loop hypothesis?"

OK, I tried just creating Localizer but never actually using it. Recorded samples over 4 runs to try to de-noise the whole thing.

time asv continuous -E virtualenv -f 1.01 main HEAD -b tslibs.normalize --record-samples --append-samples
[...]
     [c3ab0c99]       [8580f2e9]
     <enh-nano-ts>       <localizer-3>
+      3.57±0.3μs       7.03±0.5μs     1.97  tslibs.normalize.Normalize.time_is_date_array_normalized(1, datetime.timezone(datetime.timedelta(seconds=3600)))
+      3.78±0.3μs       6.57±0.5μs     1.74  tslibs.normalize.Normalize.time_is_date_array_normalized(0, datetime.timezone(datetime.timedelta(seconds=3600)))
+      5.23±0.3μs       8.83±0.2μs     1.69  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.14±0.09μs      1.90±0.04μs     1.66  tslibs.normalize.Normalize.time_is_date_array_normalized(0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      4.52±0.3μs       7.32±0.2μs     1.62  tslibs.normalize.Normalize.time_is_date_array_normalized(100, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.16±0.06μs      1.85±0.04μs     1.60  tslibs.normalize.Normalize.time_is_date_array_normalized(1, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        906±60ns       1.38±0.1μs     1.52  tslibs.normalize.Normalize.time_is_date_array_normalized(0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+        867±50ns      1.31±0.01μs     1.51  tslibs.normalize.Normalize.time_is_date_array_normalized(1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      5.50±0.5μs       8.26±0.2μs     1.50  tslibs.normalize.Normalize.time_normalize_i8_timestamps(0, datetime.timezone(datetime.timedelta(seconds=3600)))
+      5.61±0.4μs       8.27±0.4μs     1.47  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, datetime.timezone(datetime.timedelta(seconds=3600)))
+      2.05±0.1μs       2.90±0.2μs     1.42  tslibs.normalize.Normalize.time_is_date_array_normalized(100, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        524±30ns         716±20ns     1.37  tslibs.normalize.Normalize.time_is_date_array_normalized(1, None)
+        535±30ns         710±30ns     1.33  tslibs.normalize.Normalize.time_is_date_array_normalized(1, tzlocal())
+        589±40ns         738±50ns     1.25  tslibs.normalize.Normalize.time_is_date_array_normalized(0, tzlocal())
+      2.41±0.1μs       2.96±0.1μs     1.23  tslibs.normalize.Normalize.time_normalize_i8_timestamps(0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      2.74±0.2μs      3.31±0.05μs     1.21  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      2.90±0.1μs      3.48±0.08μs     1.20  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      2.62±0.2μs       3.13±0.1μs     1.19  tslibs.normalize.Normalize.time_normalize_i8_timestamps(0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+        561±50ns         665±50ns     1.19  tslibs.normalize.Normalize.time_is_date_array_normalized(0, None)
+        35.0±2μs         41.2±1μs     1.18  tslibs.normalize.Normalize.time_normalize_i8_timestamps(10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      2.46±0.1μs       2.83±0.2μs     1.15  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+        488±30ns         556±20ns     1.14  tslibs.normalize.Normalize.time_is_date_array_normalized(0, datetime.timezone.utc)
+      4.55±0.4μs       5.10±0.2μs     1.12  tslibs.normalize.Normalize.time_is_date_array_normalized(100, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+        496±30ns         547±10ns     1.10  tslibs.normalize.Normalize.time_is_date_array_normalized(1, datetime.timezone.utc)
+        41.2±2μs         44.7±1μs     1.09  tslibs.normalize.Normalize.time_normalize_i8_timestamps(10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      2.87±0.2ms       3.10±0.2ms     1.08  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, datetime.timezone(datetime.timedelta(seconds=3600)))
-        86.5±5μs         78.3±1μs     0.91  tslibs.normalize.Normalize.time_is_date_array_normalized(10000, datetime.timezone.utc)
-        310±10μs         268±10μs     0.86  tslibs.normalize.Normalize.time_normalize_i8_timestamps(10000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-        31.6±1ms       27.2±0.8ms     0.86  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)

We should be seeing pure O(1) overhead, so in this exercise the most-affected cases should be the one with small N, which is almost exactly what we're seeing. We don't see any changes of more than 10 microseocnds until the last 4 entries, and of those 3 are clearly-just-noise speedups. Disregarding those last 4, the mean slowdown was 1394ns.

@WillAyd
Copy link
Member

WillAyd commented Mar 27, 2022

One last approach I can think of would be to scrap the class-based approach and go purely functional, with something taking pointer arguments to the use_utc and use_tzlocal and simply updating them in the function body

Struct based approach looks pretty hard given the amount of Python objects that are currently in the mix, including the return value of get_dst_info

@jreback jreback added the Timezones Timezone data dtype label Mar 28, 2022
@jbrockmendel
Copy link
Member Author

Struct based approach looks pretty hard given the amount of Python objects that are currently in the mix, including the return value of get_dst_info

I've thought of this w/r/t get_dst_info and think we may get some improvement by returning a struct instead of a string for the 3rd return value (since we later do e.g. if typ not in ["pytz", "dateutil"]:... but I agree with your more general point that we're not going to get much mileage at the later stages.

Aside from trial/error and "just look at the generated code", are there any profiling tools that would help identify the problem(s)?

Other alternatives that come to mind:

  • tempita
  • implement some/all of the relevant code directly in C
  • choose to be OK with code duplication
  • choose to be OK with a perf hit

@jreback
Copy link
Contributor

jreback commented Mar 28, 2022

choose to be OK with a perf hit

so the question here is sure some of these microbenchmarks are slower, but can you show the effect of a real operation (e.g. localizing a sizable DTI). what kind of perf is lost there?

@jbrockmendel
Copy link
Member Author

localizing a sizable DTI

localizing doesn't use the path(s) in this PR yet.

Observation someone may be able to make something of. The most-affected benchmark (from a few minutes ago) was time_normalize_i8_timestamps. normalize_i8_timestamps uses a cimported normalize_i8_timesstamp, which itself is declared as nogil. Since normalize_i8_timestamp is essentially a 1-liner, I tried inlining it into normalize_i8_timestamps (this required adding @cython.cdivision(False) to normalize_i8_timestamps).

With this change, the formerly-worst benchmark is now at 1.09x, and the new worst one is 536ns vs 592ns.

Dies this produce an "a-ha!" moment for anyone on the thread?

@jbrockmendel
Copy link
Member Author

Marking as ready for review, the perf impact is now an improvement for big-array cases and a sub-microsecond hit for the super-small cases:

time asv continuous -E virtualenv -f 1.01 main HEAD -b tslibs.normalize -b TimeDT64ArrToPeriodArr -b TimeIntsToPydatetime -b TimeResolution --record-samples --append-samples
[...]
       before           after         ratio
     [c90294db]       [ba9f3a8e]
     <perf-gb-mask>       <ref-localizer-2>
+        430±20ns         517±60ns     1.20  tslibs.normalize.Normalize.time_is_date_array_normalized(1, datetime.timezone.utc)
+        512±10ns         614±50ns     1.20  tslibs.normalize.Normalize.time_is_date_array_normalized(1, tzlocal())
+        498±20ns         593±40ns     1.19  tslibs.normalize.Normalize.time_is_date_array_normalized(1, None)
+        441±50ns         524±40ns     1.19  tslibs.normalize.Normalize.time_is_date_array_normalized(0, datetime.timezone.utc)
+      1.69±0.1μs       1.98±0.1μs     1.17  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, tzlocal())
+        502±30ns         586±40ns     1.17  tslibs.normalize.Normalize.time_is_date_array_normalized(0, None)
+        79.4±5μs         92.7±8μs     1.17  tslibs.normalize.Normalize.time_is_date_array_normalized(10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        87.7±3μs          102±9μs     1.17  tslibs.normalize.Normalize.time_is_date_array_normalized(10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     2.07±0.08μs       2.37±0.1μs     1.15  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     4.73±0.05μs       5.41±0.5μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.55±0.02μs      1.78±0.05μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 12000, datetime.timezone.utc)
+        515±20ns         589±30ns     1.14  tslibs.normalize.Normalize.time_is_date_array_normalized(0, tzlocal())
+     1.74±0.09μs       1.98±0.2μs     1.14  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, None)
+      9.04±0.5ms       10.2±0.5ms     1.13  tslibs.normalize.Normalize.time_is_date_array_normalized(1000000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      3.81±0.1μs       4.31±0.2μs     1.13  tslibs.resolution.TimeResolution.time_get_resolution('us', 0, datetime.timezone(datetime.timedelta(seconds=3600)))
+        901±40ns      1.02±0.02μs     1.13  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, datetime.timezone.utc)
+      3.37±0.1μs       3.79±0.2μs     1.13  tslibs.normalize.Normalize.time_is_date_array_normalized(1, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.69±0.05μs       1.89±0.1μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 6000, tzlocal())
+     1.60±0.04μs      1.79±0.08μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 8000, datetime.timezone.utc)
+     1.70±0.03μs       1.90±0.2μs     1.12  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 7000, None)
+      4.20±0.1μs       4.69±0.3μs     1.12  tslibs.normalize.Normalize.time_is_date_array_normalized(100, datetime.timezone(datetime.timedelta(seconds=3600)))
+     2.09±0.04μs       2.33±0.2μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 7000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      6.90±0.3μs       7.68±0.4μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 3000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.73±0.09μs      1.91±0.07μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 8000, None)
+      4.61±0.1μs       5.10±0.1μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 9000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      4.67±0.2μs       5.16±0.2μs     1.11  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 2000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        985±30ns      1.09±0.02μs     1.11  tslibs.resolution.TimeResolution.time_get_resolution('D', 1, None)
+     1.66±0.02μs      1.84±0.09μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2011, None)
+     1.68±0.05μs       1.85±0.1μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1000, None)
+     1.60±0.04μs       1.76±0.1μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 9000, datetime.timezone.utc)
+     1.78±0.05μs       1.96±0.2μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1011, tzlocal())
+     1.01±0.03μs      1.11±0.05μs     1.10  tslibs.resolution.TimeResolution.time_get_resolution('us', 1, None)
+     1.59±0.02μs      1.75±0.09μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 9000, datetime.timezone.utc)
+     1.61±0.05μs       1.77±0.2μs     1.10  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2011, datetime.timezone.utc)
+        922±30ns      1.01±0.03μs     1.10  tslibs.resolution.TimeResolution.time_get_resolution('D', 1, datetime.timezone.utc)
+      4.60±0.2μs      5.03±0.05μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 6000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        832±20ns         909±50ns     1.09  tslibs.normalize.Normalize.time_is_date_array_normalized(1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     2.06±0.06μs       2.25±0.2μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.58±0.04μs      1.73±0.06μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 10000, datetime.timezone.utc)
+        879±20ns         959±20ns     1.09  tslibs.resolution.TimeResolution.time_get_resolution('D', 0, datetime.timezone.utc)
+        981±30ns      1.07±0.03μs     1.09  tslibs.resolution.TimeResolution.time_get_resolution('h', 0, tzlocal())
+     4.27±0.07μs       4.65±0.4μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 4000, datetime.timezone.utc)
+        831±30ns         904±90ns     1.09  tslibs.normalize.Normalize.time_is_date_array_normalized(0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      3.48±0.3μs       3.78±0.3μs     1.09  tslibs.normalize.Normalize.time_is_date_array_normalized(0, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.60±0.03μs      1.74±0.06μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2000, datetime.timezone.utc)
+     2.02±0.08μs       2.20±0.1μs     1.09  tslibs.normalize.Normalize.time_is_date_array_normalized(100, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.34±0.05μs      1.46±0.02μs     1.09  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 0, datetime.timezone.utc)
+     1.72±0.08μs       1.86±0.1μs     1.09  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 9000, tzlocal())
+      4.77±0.1μs       5.17±0.3μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4006, datetime.timezone(datetime.timedelta(seconds=3600)))
+        960±30ns      1.04±0.03μs     1.08  tslibs.resolution.TimeResolution.time_get_resolution('ns', 0, None)
+     1.58±0.03μs      1.72±0.06μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 7000, datetime.timezone.utc)
+     1.68±0.06μs       1.82±0.1μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 12000, None)
+     1.57±0.05μs      1.70±0.03μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1000, datetime.timezone.utc)
+      1.04±0.1μs      1.13±0.08μs     1.08  tslibs.normalize.Normalize.time_is_date_array_normalized(0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      7.19±0.3μs       7.78±0.2μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 10000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      4.81±0.3μs       5.20±0.4μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 4000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.69±0.05μs       1.82±0.1μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 11000, tzlocal())
+     1.74±0.03μs       1.88±0.2μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 9000, None)
+     1.43±0.04μs      1.55±0.05μs     1.08  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 0, tzlocal())
+     1.58±0.05μs      1.70±0.06μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1000, datetime.timezone.utc)
+     1.68±0.06μs      1.82±0.03μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1011, None)
+     3.97±0.03μs       4.28±0.1μs     1.08  tslibs.resolution.TimeResolution.time_get_resolution('D', 1, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.73±0.01μs      1.87±0.06μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 11000, tzlocal())
+     1.72±0.03μs       1.86±0.1μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 2000, tzlocal())
+      4.82±0.1μs       5.19±0.2μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.67±0.06μs      1.80±0.08μs     1.08  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, None)
+        968±20ns      1.04±0.01μs     1.08  tslibs.resolution.TimeResolution.time_get_resolution('D', 0, None)
+     1.58±0.03μs      1.71±0.03μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 11000, datetime.timezone.utc)
+     1.67±0.04μs       1.80±0.1μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4006, tzlocal())
+     1.73±0.04μs      1.86±0.08μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 8000, tzlocal())
+     1.32±0.03μs      1.42±0.04μs     1.08  tslibs.resolution.TimeResolution.time_get_resolution('s', 0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     4.81±0.05μs       5.17±0.4μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 12000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        931±20ns      1.00±0.01μs     1.08  tslibs.resolution.TimeResolution.time_get_resolution('us', 1, datetime.timezone.utc)
+     1.40±0.04μs      1.51±0.02μs     1.08  tslibs.resolution.TimeResolution.time_get_resolution('D', 1, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.58±0.05μs      1.70±0.05μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 3000, datetime.timezone.utc)
+     1.72±0.08μs      1.85±0.08μs     1.08  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 12000, tzlocal())
+      3.89±0.1μs      4.18±0.08μs     1.08  tslibs.resolution.TimeResolution.time_get_resolution('h', 0, datetime.timezone(datetime.timedelta(seconds=3600)))
+      7.77±0.4ms       8.36±0.3ms     1.08  tslibs.normalize.Normalize.time_is_date_array_normalized(1000000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        988±30ns      1.06±0.03μs     1.07  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, None)
+     1.71±0.06μs       1.84±0.1μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 2000, tzlocal())
+     1.68±0.04μs      1.80±0.05μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 10000, None)
+     2.30±0.07μs      2.47±0.08μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 9000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      4.71±0.3μs       5.05±0.3μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 12000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.68±0.04μs      1.80±0.06μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 11000, None)
+        981±40ns      1.05±0.03μs     1.07  tslibs.resolution.TimeResolution.time_get_resolution('us', 0, None)
+      4.71±0.1μs      5.04±0.06μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 11000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     2.26±0.09μs       2.41±0.1μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 12000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+      7.39±0.3μs       7.90±0.4μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 12000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      7.10±0.2μs       7.58±0.1μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 6000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.62±0.04μs      1.72±0.09μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 8000, datetime.timezone.utc)
+     1.69±0.04μs      1.81±0.04μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1000, None)
+      6.97±0.3μs       7.43±0.2μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.56±0.02μs      1.66±0.03μs     1.07  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 5000, datetime.timezone.utc)
+     2.83±0.07μs      3.01±0.04μs     1.06  tslibs.resolution.TimeResolution.time_get_resolution('D', 100, datetime.timezone.utc)
+     1.51±0.03μs      1.61±0.02μs     1.06  tslibs.resolution.TimeResolution.time_get_resolution('D', 0, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.69±0.04μs      1.80±0.03μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 3000, None)
+     4.09±0.08μs      4.35±0.09μs     1.06  tslibs.resolution.TimeResolution.time_get_resolution('m', 1, datetime.timezone(datetime.timedelta(seconds=3600)))
+      6.92±0.3μs       7.35±0.5μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      7.04±0.2μs       7.48±0.5μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2011, datetime.timezone(datetime.timedelta(seconds=3600)))
+     4.15±0.06μs      4.40±0.07μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 10000, datetime.timezone.utc)
+      6.02±0.2μs      6.38±0.08μs     1.06  tslibs.resolution.TimeResolution.time_get_resolution('m', 100, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.70±0.04μs       1.80±0.1μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1011, None)
+     1.71±0.04μs       1.81±0.2μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1000, tzlocal())
+     1.02±0.03μs      1.09±0.02μs     1.06  tslibs.resolution.TimeResolution.time_get_resolution('D', 0, tzlocal())
+     1.60±0.06μs      1.69±0.08μs     1.06  tslibs.normalize.Normalize.time_normalize_i8_timestamps(0, datetime.timezone.utc)
+     1.73±0.04μs      1.83±0.07μs     1.06  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, tzlocal())
+        989±40ns      1.05±0.06μs     1.06  tslibs.resolution.TimeResolution.time_get_resolution('m', 0, tzlocal())
+     1.04±0.05μs      1.10±0.07μs     1.06  tslibs.normalize.Normalize.time_is_date_array_normalized(1, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     3.85±0.07μs       4.07±0.2μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1011, datetime.timezone.utc)
+      7.22±0.2μs       7.63±0.2μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 7000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      4.70±0.1μs       4.96±0.2μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 5000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     2.06±0.05μs      2.17±0.07μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 12000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.71±0.06μs       1.80±0.1μs     1.06  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 7000, tzlocal())
+     2.29±0.07μs      2.41±0.07μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 3000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     3.97±0.07μs       4.19±0.3μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 1000, None)
+      7.93±0.2μs       8.36±0.1μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 5000, datetime.timezone(datetime.timedelta(seconds=3600)))
+        898±20ns         945±30ns     1.05  tslibs.resolution.TimeResolution.time_get_resolution('ns', 0, datetime.timezone.utc)
+     2.08±0.05μs      2.19±0.04μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 3000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     4.01±0.09μs       4.22±0.2μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 2011, None)
+     4.41±0.08μs       4.64±0.3μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 4000, None)
+     1.33±0.03μs      1.40±0.04μs     1.05  tslibs.resolution.TimeResolution.time_get_resolution('D', 0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+      7.34±0.2μs       7.72±0.2μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 9000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      4.81±0.2μs       5.05±0.3μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 8000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      4.85±0.2μs       5.09±0.3μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 1000, datetime.timezone(datetime.timedelta(seconds=3600)))
+      3.99±0.1μs      4.19±0.09μs     1.05  tslibs.resolution.TimeResolution.time_get_resolution('us', 1, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.77±0.08μs      1.86±0.02μs     1.05  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 1, None)
+     1.69±0.03μs      1.77±0.03μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 1000, tzlocal())
+        907±30ns         950±20ns     1.05  tslibs.resolution.TimeResolution.time_get_resolution('us', 0, datetime.timezone.utc)
+     1.71±0.04μs       1.79±0.1μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 5000, tzlocal())
+     1.61±0.05μs      1.69±0.09μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 4006, datetime.timezone.utc)
+     2.06±0.06μs      2.15±0.08μs     1.05  tslibs.normalize.Normalize.time_normalize_i8_timestamps(0, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
+     1.70±0.06μs       1.78±0.1μs     1.05  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 12000, None)
+     4.02±0.09μs      4.21±0.09μs     1.05  tslibs.resolution.TimeResolution.time_get_resolution('ns', 0, datetime.timezone(datetime.timedelta(seconds=3600)))
+      18.8±0.4ms       19.6±0.3ms     1.05  tslibs.resolution.TimeResolution.time_get_resolution('ns', 1000000, None)
+     1.74±0.07μs      1.81±0.09μs     1.04  tslibs.normalize.Normalize.time_normalize_i8_timestamps(0, tzlocal())
+     1.61±0.06μs      1.68±0.04μs     1.04  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1, datetime.timezone.utc)
+         266±4μs         277±20μs     1.04  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 9000, datetime.timezone(datetime.timedelta(seconds=3600)))
+     1.69±0.04μs      1.76±0.04μs     1.04  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(0, 5000, None)
+      2.36±0.1μs       2.45±0.2μs     1.04  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1, 11000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
+     1.30±0.03μs      1.34±0.09μs     1.03  tslibs.normalize.Normalize.time_is_date_array_normalized(100, None)
+     5.07±0.08μs      5.21±0.06μs     1.03  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(100, 11000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-         324±7ms          318±3ms     0.98  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 1000000, datetime.timezone.utc)
-         247±7μs          243±3μs     0.98  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 3000, datetime.timezone.utc)
-         325±7ms          319±2ms     0.98  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 1000000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-      27.4±0.4ms       26.8±0.3ms     0.98  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 12000, None)
-         429±8ms          420±5ms     0.98  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 1000000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-        25.8±1ms       25.2±0.3ms     0.98  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 11000, datetime.timezone.utc)
-      14.4±0.3μs       14.0±0.1μs     0.98  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('date', 100, None)
-      3.07±0.2ms      3.00±0.04ms     0.98  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('datetime', 10000, None)
-         353±5ms          345±2ms     0.98  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('time', 10000, tzlocal())
-         267±6μs          258±5μs     0.97  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 11000, datetime.timezone.utc)
-      26.0±0.5ms       25.1±0.2ms     0.97  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 11000, None)
-      25.3±0.9ms       24.4±0.4ms     0.96  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 2011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-      20.3±0.7ms       19.5±0.3ms     0.96  tslibs.resolution.TimeResolution.time_get_resolution('ns', 1000000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-        24.2±1ms       23.2±0.4ms     0.96  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 1000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-      24.5±0.6ms       23.4±0.4ms     0.96  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 3000, datetime.timezone.utc)
-      24.2±0.6ms       23.1±0.3ms     0.96  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 1011, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-        61.5±3ms       58.8±0.6ms     0.96  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 3000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-        69.4±2ms       66.3±0.7ms     0.95  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 4006, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-        69.8±2ms       66.6±0.6ms     0.95  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 4000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-        290±10μs          277±4μs     0.95  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 4006, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-         248±5μs          236±2μs     0.95  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 1000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-     1.29±0.05ms      1.23±0.01ms     0.95  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('date', 10000, None)
-         144±5ms          138±1ms     0.95  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('date', 1000000, None)
-      27.2±0.9ms       25.9±0.3ms     0.95  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 6000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-        28.8±1ms       27.3±0.3ms     0.95  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 4006, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-        27.3±1ms       25.9±0.1ms     0.95  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 7000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-      11.9±0.5ms       11.3±0.4ms     0.95  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-      5.86±0.3μs       5.53±0.2μs     0.94  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-        25.4±3ms       24.0±0.3ms     0.94  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 2000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-         126±8μs          118±5μs     0.94  tslibs.normalize.Normalize.time_normalize_i8_timestamps(10000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-        620±30μs         583±10μs     0.94  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(10000, 3000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-     2.71±0.09μs       2.54±0.2μs     0.94  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, None)
-        452±20ms          421±2ms     0.93  tslibs.tslib.TimeIntsToPydatetime.time_ints_to_pydatetime('timestamp', 1000000, datetime.timezone(datetime.timedelta(seconds=3600)))
-     2.60±0.07μs      2.41±0.07μs     0.93  tslibs.normalize.Normalize.time_normalize_i8_timestamps(100, datetime.timezone.utc)
-        384±20μs         356±10μs     0.93  tslibs.normalize.Normalize.time_normalize_i8_timestamps(10000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-        38.3±1ms         35.3±2ms     0.92  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
-      29.8±0.7ms       27.3±0.3ms     0.92  tslibs.period.TimeDT64ArrToPeriodArr.time_dt64arr_to_periodarr(1000000, 4000, tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
-        77.7±3μs         69.4±5μs     0.89  tslibs.normalize.Normalize.time_is_date_array_normalized(10000, datetime.timezone.utc)
-      8.00±0.3ms       6.95±0.2ms     0.87  tslibs.normalize.Normalize.time_is_date_array_normalized(1000000, None)
-        80.2±3μs         69.7±3μs     0.87  tslibs.normalize.Normalize.time_is_date_array_normalized(10000, None)
-      8.07±0.4ms       6.96±0.3ms     0.86  tslibs.normalize.Normalize.time_is_date_array_normalized(1000000, datetime.timezone.utc)
-      10.4±0.5ms       7.82±0.8ms     0.75  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, datetime.timezone.utc)
-      10.4±0.4ms       7.75±0.4ms     0.74  tslibs.normalize.Normalize.time_normalize_i8_timestamps(1000000, None)
-         109±5μs         80.6±5μs     0.74  tslibs.normalize.Normalize.time_normalize_i8_timestamps(10000, None)
-         107±3μs         79.6±3μs     0.74  tslibs.normalize.Normalize.time_normalize_i8_timestamps(10000, datetime.timezone.utc)

@jbrockmendel jbrockmendel marked this pull request as ready for review April 12, 2022 21:34
@jbrockmendel jbrockmendel changed the title REF/WIP: Localizer class to de-duplicate tzconversion code REF: Localizer class to de-duplicate tzconversion code Apr 12, 2022
@mroeschke mroeschke added this to the 1.5 milestone Apr 16, 2022
Copy link
Member

@mroeschke mroeschke left a comment

Choose a reason for hiding this comment

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

LGTM. I think this is a good consolidation of logic despite the small perf hit.

Also this might set up for abstracting library dependent tz offset implementations?

@jbrockmendel
Copy link
Member Author

Also this might set up for abstracting library dependent tz offset implementations?

Not sure what you have in mind here.

@mroeschke
Copy link
Member

mroeschke commented Apr 16, 2022

I remember a some feature discussion a while back about user's being able to provide their own timezone offset implementations. This Localizer class looks to be conducive to do something like

class PytzLocalizer(Localizer):
    def __cinit__(self, tzinfo tz):
        if tz is pytz.utc or tz is None:
             ....

    
class DatetulLocalizer(Localizer):
    def __cinit__(self, tzinfo tz):
        if tz is dateutil.tz.utc() or tz is None:
             ....

class MyTZLocalizer(Localizer):
    def __cinit__(self, tzinfo tz):
        if tz is ...:
             ....

in the future?

@jbrockmendel
Copy link
Member Author

This Localizer class looks to be conducive to do something like [...]

So the two main possibilities for next steps either look like a) what you wrote with pytz/dateutil/zoneinfo/fallback-specific implementations or b) GH#46511. My initial intuition was that the subclass-specific route would be more performant, but the last time I experimented with that it didn't pan out that way. It'll be worth re-checking.

user's being able to provide their own timezone offset implementations

if you mean support for arbitrary tzinfo subclasses, then absolutely. That actually becomes really easy as a follow-up to #46425, as the general case will end up going through the tzlocal/zoneinfo path.

If you mean allowing users to implement Localizer subclasses, that sounds like a pretty big hassle.

@mroeschke
Copy link
Member

Cool. Yeah having user implementing Localizer seems a bit heavy handed as hopefully there should be a single standard path to do tz arithmetic with integers, so generally the direction sounds good.

@jreback jreback added the Refactor Internal refactoring of code label Apr 18, 2022
@jreback jreback merged commit 9797c89 into pandas-dev:main Apr 18, 2022
@jreback
Copy link
Contributor

jreback commented Apr 18, 2022

very nice @jbrockmendel

@jbrockmendel jbrockmendel deleted the ref-localizer-2 branch April 18, 2022 23:33
yehoshuadimarsky pushed a commit to yehoshuadimarsky/pandas that referenced this pull request Jul 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Refactor Internal refactoring of code Timezones Timezone data dtype
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants