PERF: optimize algos.take for repeated calls
This PR optimizes our internal `take` algorithm, with the following changes:

- Cache the expensive parts that are independent of the actual array values but only dependent on the dtypes / dimension / axis / fill_value (which is often the same for repeated calls), i.e. `maybe_promote` and `_get_take_nd_function`
- The above is done in the generic `take_nd` function as well, but in addition I also added a `take_1d_array` specialized version that assumes the input is already an array and only deals with 1D. This gives a small further speed-up.

An example use case where the effect of this optimization can be noticed nicely is `unstack` in case of the ArrayManager. This is typically a case where we do multiple `take` calls for 1 column (to split it into multiple columns) and thus call it many times for the same dtype/fill_value.

Using the example from the `reshape.py::SimpleReshape` ASV benchmark (this is an example of homogeneous dtype with a single block, so which is fast with the BlockManager using the `_can_fast_transpose` fastpath in `_unstack_frame`):

```python
arrays = [np.arange(100).repeat(100), np.roll(np.tile(np.arange(100), 100), 25)]
index = pd.MultiIndex.from_arrays(arrays)
df = pd.DataFrame(np.random.randn(10000, 4), index=index)
df_am = df._as_manager("array")
```

Master:
```
In [2]: %timeit df.unstack()
1.79 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [3]: %timeit df_am.unstack()
15.1 ms ± 1.3 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
```

PR:

```
In [2]: %timeit df.unstack()
1.81 ms ± 160 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [3]: %timeit df_am.unstack()
3.44 ms ± 69.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
```

So for the BlockManager this doesn't matter (it does only a single call to `take_nd` for the column values), but for the ArrayManager version this gives a nice speedup and brings it within 2x of the BlockManager version (for this benchmark).