PERF: Fix regression in datetime ops
State: closed

HEAD:

```
In [1]: import pandas as pd; import numpy as np
In [2]: s = pd.Series(pd.to_datetime(np.arange(100000), unit='ms'))
In [3]: %timeit s - s.shift()
2.73 ms ± 30.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
```

0.21.0rc1:

```
527 ms ± 11.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
```

0.20.3

```
2.4 ms ± 57.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
```

xref https://github.com/pandas-dev/pandas/issues/17861

Labels:
name: Performance
description: Memory or execution speed performance

Comments:
This also seems to help with the `timeseries.SeriesArithmetic.time_add_offset` benchmarks:

head:

```
[ 33.33%] ··· Running timeseries.SeriesArithmetic.time_add_offset_delta                                                                               4.30ms
[ 66.67%] ··· Running timeseries.SeriesArithmetic.time_add_offset_fast                                                                                11.0ms
[100.00%] ··· Running timeseries.SeriesArithmetic.time_add_offset_slow                                                                                 703ms
```

master:

```
[ 33.33%] ··· Running timeseries.SeriesArithmetic.time_add_offset_delta                                                                                499ms
[ 66.67%] ··· Running timeseries.SeriesArithmetic.time_add_offset_fast                                                                                 489ms
[100.00%] ··· Running timeseries.SeriesArithmetic.time_add_offset_slow                                                                                 1.05s
```
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=h1) Report
> Merging [#17980](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/36c309ed51b98e97fb64238232ecdc795040ad24?src=pr&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/17980/graphs/tree.svg?src=pr&token=eZ4WkYLtcO&width=650&height=150)](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master   #17980      +/-   ##
==========================================
- Coverage   91.23%   91.22%   -0.02%     
==========================================
  Files         163      163              
  Lines       50113    50113              
==========================================
- Hits        45723    45714       -9     
- Misses       4390     4399       +9
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.03% <100%> (ø)` | :arrow_up: |
| #single | `40.31% <100%> (-0.07%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/ops.py](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvb3BzLnB5) | `91.77% <100%> (ø)` | :arrow_up: |
| [pandas/io/gbq.py](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=tree#diff-cGFuZGFzL2lvL2dicS5weQ==) | `25% <0%> (-58.34%)` | :arrow_down: |
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `97.75% <0%> (-0.1%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=footer). Last update [36c309e...d0ea5dc](https://codecov.io/gh/pandas-dev/pandas/pull/17980?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Cool, this also fixes the slowdown in the stata benchmarks:

head:

```
[  0.00%] ·· Building for existing-py_Users_taugspurger_Envs_pandas-dev_bin_python3.6
[  0.00%] ·· Benchmarking existing-py_Users_taugspurger_Envs_pandas-dev_bin_python3.6
[ 25.00%] ··· Running packers.STATA.time_write_stata                                                                                                  33.2ms
[ 50.00%] ··· Running packers.STATA.time_write_stata_with_validation                                                                                  49.5ms
[ 75.00%] ··· Running packers.packers_read_stata.time_packers_read_stata                                                                              39.6ms
[100.00%] ··· Running packers.packers_read_stata_with_validation.time_packers_read_stata_with_validation                                              48.0ms
```

master:

```
[  0.00%] ·· Building for existing-py_Users_taugspurger_Envs_pandas-dev_bin_python3.6
[  0.00%] ·· Benchmarking existing-py_Users_taugspurger_Envs_pandas-dev_bin_python3.6
[ 25.00%] ··· Running packers.STATA.time_write_stata                                                                                                   502ms
[ 50.00%] ··· Running packers.STATA.time_write_stata_with_validation                                                                                   529ms
[ 75.00%] ··· Running packers.packers_read_stata.time_packers_read_stata                                                                               631ms
[100.00%] ··· Running packers.packers_read_stata_with_validation.time_packers_read_stata_with_validation                                               650ms
```
Are the `isinstance` checks being called zillions of times?  `ABCDateOffset` was introduced recently, and `isinstance(x, ABCFoo)` is about 2x slower than `isinstance(x, Foo)`
> Are the isinstance checks being called zillions of times?

On master they were being called once per row. The change here skips that, since if we have a `datetime64` or `timedelta64` array, then we know none of them are offsets.
Thanks for hunting those down!