PERF: avoid copies if possible in fill_binop
This has a small perf bump, but more importantly is necessary for the blockwise frame-with-frame PR that is coming up.

```
import pandas as pd
from pandas.core.ops import *

arr = np.arange(10**6)
df = pd.DataFrame({"A": arr})
ser = df["A"]

%timeit result = df.add(df, fill_value=4)
7.77 ms ± 20.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)  <-- master
5.46 ms ± 36.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)  <-- PR

%timeit result = ser.add(ser, fill_value=1)
6.79 ms ± 56.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)  <-- master
4.65 ms ± 82.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)  <-- PR
```

> Not being super familiar here, is there a way to avoid copy without having to special case for the mask presence?

Not sure I understand the question.  Can you give an example? > Not sure I understand the question.

@jbrockmendel : I believe @WillAyd is trying to determine if there is a faster way to figure out if we need to `.copy` or not.  I don't think it's a blocker, but if we could, then all the better.

`.any` is still `O(n)`, so the run-time is theoretically unchanged with this PR. > determine if there is a faster way to figure out if we need to .copy or not

Well, we could pre-check for dtypes that can't hold NAs and short-circuit in those cases.

Other than that, we might be able to do 1 O(n) pass instead of 2-3 if we did it all in cython, but i dont think thats worthwhile at this point  this is fine. pls add asvs when you can.