If something like what I show below were done (which I think is how denoms should be modified to work) then I don't think it will be more expensive. There is a lot of analysis going on when `as_numer_denom` is run, especially when there are lots of terms involved.  It's hard for me to anticipate what sorts of denominators might cancel out with `as_numer_denom` and since you care about such things in this PR, I think the "right way" is to make sure you've found all denominators.

```
def dens(e):
    pot = preorder_traversal(e)
    saw = set()
    for p in pot:
        den =  denom(p)
        for d in den.as_coeff_mul()[1]:
            saw.add(d)
    return saw

expression from issue 978

>>> n=1/x-1
>>> z=n/x/(-n)**2-1/n/x
>>> z.expand()
1/(x**2 - 2*x + 1) - 1/(x - 2 + 1/x) - 1/(-x + 1)
>>> z.diff(x).expand()
-2/(x**4 - 3*x**3 + 3*x**2 - x) + 2/(x**3 - 3*x**2 + 3*x - 1) - 3/(x**3 - 2*x**2
 + x) + 1/(x**2 - 2*x + 1) + 1/(-x**2 + x)
>>> eq=_
>>> dens(eq)
set([x - 1, x, x**2 - 2*x + 1, x**3 - 3*x**2 + 3*x - 1])
>>> eq.as_numer_denom()[1]
(-x**2 + x)*(x**2 - 2*x + 1)*(x**3 - 2*x**2 + x)*(x**3 - 3*x**2 + 3*x - 1)*(x**4
 - 3*x**3 + 3*x**2 - x)

>>> def do():
...  for s in var('a:1000'):j=eq.xreplace({x:s}).as_numer_denom()
...
>>> t=time();do();time()-t
10.255000114440918
>>> def do():
...  for s in var('b:1000'):j=dens(eq.xreplace({x:s}))
...
>>> t=time();do();time()-t
7.428000211715698
```

btw, the above will return all denominators, even the numeric ones. If any numeric denominator is zero (as can happen when solving symbolic matrices as in issue 978) then the expression is already "bad".