PERF: Don't copy if already sorted in DataSplitter
- [ ] closes #51077  (Replace xxxx with the GitHub issue number)
- [ ] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature
- [ ] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [ ] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [ ] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.

```
       before           after         ratio
     [2a983bb1]       [2a980997]
     <datasplitter-ordered-nocopy~1>       <datasplitter-ordered-nocopy>
+      14.1±0.4ms       32.8±0.3ms     2.32  groupby.ApplyDictReturn.time_groupby_apply_dict_return
-     3.56±0.03ms      3.01±0.07ms     0.85  groupby.Apply.time_sorted_scalar_function_single_col(5)
-            236M             197M     0.83  groupby.Apply.peakmem_sorted_scalar_function_multi_col(6)
-            220M             182M     0.83  groupby.Apply.peakmem_sorted_scalar_function_single_col(6)
-        51.4±4ms       34.1±0.5ms     0.66  groupby.Apply.time_sorted_scalar_function_single_col(6)
```
Benchmarks.
The regression in apply_dict_return is real, and I think the ``.copy()`` method has a lot of overhead(both the finalize + name validation I think).
No memory improvement is showing up there since the size isn't big enough.

would #51109 address some or all of the finalize/name slowdown you mentioned? Profiling this with

```
from asv_bench.benchmarks.groupby import *

cls = ApplyDictReturn
self = cls()
self.setup()

%prun -s cumtime for n in range(100): self.time_groupby_apply_dict_return()
```

main
```
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    [...]
      100    0.291    0.003    4.381    0.044 ops.py:1000(apply_groupwise)
   100100    0.101    0.000    2.755    0.000 ops.py:1252(__iter__)
   100000    0.255    0.000    2.633    0.000 ops.py:1274(_chop)
   100200    0.399    0.000    1.005    0.000 generic.py:5985(__finalize__)
   100000    0.403    0.000    0.945    0.000 managers.py:1990(get_slice)
   100000    0.121    0.000    0.945    0.000 groupby.py:68(<lambda>)
   200000    0.083    0.000    0.823    0.000 series.py:672(values)
   200000    0.228    0.000    0.740    0.000 managers.py:2016(external_values)
   200500    0.200    0.000    0.713    0.000 series.py:667(name)
```

branch
```
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    [...]
      100    0.368    0.004    9.000    0.090 ops.py:1000(apply_groupwise)
   100100    0.267    0.000    7.213    0.000 ops.py:1252(__iter__)
   100000    0.267    0.000    4.328    0.000 generic.py:6394(copy)
   100000    0.273    0.000    2.614    0.000 ops.py:1281(_chop)
   200100    0.820    0.000    1.934    0.000 generic.py:5985(__finalize__)
   100000    0.260    0.000    1.836    0.000 managers.py:620(copy)
   200200    0.263    0.000    1.628    0.000 series.py:373(__init__)
   400300    0.397    0.000    1.358    0.000 series.py:667(name)
```

Looks like some of the slowdown is caused by extra calls to `__finalize__` which add up.  (#51109 or #51784 might help, #51280 definitely would once enforced)

This makes me want to revisit optimizing get_slice and external_values, though that won't do much good here. Closes #43011? > Closes #43011?

For the non CoW case, yes. This is already optimized for CoW (since a no-op take there will return a shallow copy). This pull request is stale because it has been open for thirty days with no activity. Please [update](https://pandas.pydata.org/pandas-docs/stable/development/contributing.html#updating-your-pull-request) and respond to this comment if you're still interested in working on this. Closing to clear the queue for now. Feel free to reopen when you have time to revisit