PERF: enhance MultiIndex.remove_unused_levels when no levels are unused
State: closed

- [x] closes #19289
- [x] tests passed
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry

This uses ``np.bincount`` rather than ``algos.unique`` to check _if_ there are unused levels: since levels indexes are adjacent integers, the former is faster (~50% in my tests). It then fallbacks to ``algos.unique`` if there are unused levels.

The result is that asv tests on ``--bench reshape`` give
```
       before           after         ratio
     [d5d5a718]       [499ae6bf]
-     1.43±0.01ms      1.19±0.01ms     0.83  reshape.SparseIndex.time_unstack
```

There is a penalty (of the same order of magnitude) for cases in which _there are_ unused levels, but I think this is worth because:
- there is room for cythonizing and bringing this penalty close to zero in the average case (see comment in code)
- I think the case of no unused level is much more frequent
- if people want to avoid this penalty they can just clean their index from unused levels once for all
- in any case, asv tests on ``--bench multiindex_object`` (where the index in ``Duplicates`` always has unused levels) do not find any change in performance

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

Comments:
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/20703?src=pr&el=h1) Report
> Merging [#20703](https://codecov.io/gh/pandas-dev/pandas/pull/20703?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/d5d5a718254f45b4bdc386c360c830df395ec02a?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `100%`.

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

```diff
@@            Coverage Diff             @@
##           master   #20703      +/-   ##
==========================================
- Coverage   91.84%   91.83%   -0.01%     
==========================================
  Files         153      153              
  Lines       49277    49279       +2     
==========================================
  Hits        45257    45257              
- Misses       4020     4022       +2
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `90.22% <100%> (-0.01%)` | :arrow_down: |
| #single | `41.9% <0%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/20703?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/indexes/multi.py](https://codecov.io/gh/pandas-dev/pandas/pull/20703/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9tdWx0aS5weQ==) | `95.06% <100%> (ø)` | :arrow_up: |
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/20703/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `84.38% <0%> (-0.21%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/20703?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/20703?src=pr&el=footer). Last update [d5d5a71...c61dd9a](https://codecov.io/gh/pandas-dev/pandas/pull/20703?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

thanks @toobaz I closed the issue, but I suppose can revisit a re-factorting of unstack and/or cythonize removing unused levels at some point.