REF/PERF: PeriodDtype decouple from DateOffset
ATM we define PeriodDtype in terms of DateOffsets, but this is a misnomer.  In fact, virtually every Period/PeriodArray method has to start off by taking its `.freq` and finding the corresponding integer code.  This makes the integer code itself into a dtype.  We lose a little bit of ground on the constructor, then make it back up in subsequent calls.

```
In [2]: per = pd.Period("2016Q1")                                                                                                                                                                   

In [3]: %timeit per.year
556 ns ± 13.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)    # <-- master
94.5 ns ± 1.36 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)  # <-- PR

In [4]: %timeit pd.Period("2016Q1")                   
21.2 µs ± 176 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)  # <-- master
25.8 µs ± 457 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)  # <-- PR
```

The constructor perf I think we can improve by eventually cutting the DateOffset out of the process altogether.

We'll also be able to de-duplicate a _bunch_ of other stuff: FreqGroup can be defined in terms of PeriodDypeCode, Resolution can be defined in terms of FreqGroup (xref #34462), we can avoid redundant definitions of the dtype codes in period.pyx, and a lot of the rest of libfrequencies becomes unnecessary.

In a follow-up I plan to mix the cython-space PeriodDtype into the core.dtypes PeriodDtype and we can get the same perf improvements in the PeriodArray methods.