There is something that has been troubling me for a while. At least through version 10.0 the performance of a / b and a - b is not equivalent to, and significantly inferior to, Divide[a, b] and Subtract[a, b], despite the fact that these are treated as equivalent in the documentation. As a terse code fanatic it chafes me to have to write out the longer forms, but that is only the beginning of my concern, because the latter, faster forms are transparently converted into the former, slower forms.
Examples from the documentation for Divide:
x/y or Divide[x,y] is equivalent to x y^-1.
Divide[x,y] can be entered in StandardForm and InputForm as x ÷ y, x EscdivEsc y or x \[Divide] y.
Proof that these statements are false:
{a, b} = List @@ RandomReal[{-50, 50}, {2, 1*^7}];
Do[a/b, {50}] // Timing // First
Do[a b^-1, {50}] // Timing // First
Do[a\[Divide]b, {50}] // Timing // First
Do[Divide[a, b], {50}] // Timing // First
2.2622.231
2.184
0.437
Divide[a, b] is not evaluated in the same manner as the rest. Similarly for subtraction:
Do[a - b, {50}] // Timing // First
Do[a + (-1 b), {50}] // Timing // First
Do[Subtract[a, b], {50}] // Timing // First
3.6513.65
1.404
Although the examples above use packed arrays (see Why does list assignment with a packed array result in unpacked values? for an explanation of List @@) the same performance differential exists with unpacked lists.
One can see with Trace that the short forms induce additional operations:
a = Range[1, 2];
b = Range[3, 4];
a/b // Trace
Divide[a, b] // Trace
{{a,{1,2}}, {{b,{3,4}}, 1/{3,4}, {1/3,1/4}}, {1,2} {1/3,1/4}, {1/3,1/4} {1,2}, 1/3,1/2}}{{a,{1,2}}, {b,{3,4}}, {1,2}/{3,4}, {1/3,1/2}}
a - b // Trace
Subtract[a, b] // Trace
{{a,{1,2}}, {{b,{3,4}}, -{3,4}, {-3,-4}}, {1,2} + {-3,-4}, {-3,-4} + {1,2}, {-2,-2}}{{a,{1,2}}, {b,{3,4}}, {1,2} - {3,4}, {-2,-2}}
Note the false equivalence in this output; the fast Divide and Subtract operations are formatted as {1,2}/{3,4} and {1,2} - {3,4}, yet these slow forms are not part of their evaluation process.
More problematic is when this false equivalence changes evaluation. For example, if you try to work symbolically with Subtract and Divide in an evaluated expression and use the result these faster forms are replaced with the slower ones:
expr = Divide[x, y] + Subtract[x, y];
fn = Function[{x, y}, Evaluate @ expr]
fn // FullForm
Function[{x, y}, x + x/y - y]Function[List[x,y], Plus[x, Times[x, Power[y,-1]], Times[-1,y]]]
Note also that Divide and Subtract are stripped when converting forms in the Front End (menu Cell > Convert To) so even without evaluation these operators may be lost.
Therefore I have these questions:
Why are these operations universally treated as equivalent when they are clearly not programmatically equivalent?
Why does Mathematica not include optimization for cases of numeric division and subtraction to eliminate the additional evaluation steps?
Is there a practical way to add global optimizations to automatically convert these operations to the fast forms before numeric evaluation?
Failing the above, what is the best way to work symbolically with actual division and subtraction operators for the sake of performance?
Is internally representing division and subtraction as multiplication and addition really the only mathematically valid design option? Couldn't these instead be first-class operators that are recognized as equivalent to
TimesandPlusfor the purpose of pattern matching but not converted intoTimesandPlus(which introduces additionalTimesandPoweroperations)?
Trace. – Mr.Wizard Jan 22 '14 at 18:41a-bis treated asa + (-b), hence the additional operations (despite the expressions be mathematically equivalent). But by parsing times I was thinking at the time needed to convert an infix operator into the call to the corresponding compiled function (which should be automatic in case you call it directly in the code). Mine was just a guess. – Peltio Jan 22 '14 at 18:56a/brequire parsing time to be converted toTime[a,b^-1](orTimes[a,1/b], I don't remember) and this form requires more computations thenDivide[a,b]. But the infix form of this latter procedure also require parsing time to be transformed into a call toDivide. When the operation is fast, parsing time could make the difference. Just guessing, eh... – Peltio Jan 22 '14 at 19:13Divide.Do[a*b, {50}] // Timing // First Do[Times[a, b], {50}] // Timing // First Do[a b, {50}] // Timing // First. Also, while running this, more than one core is used. – Ajasja Jan 22 '14 at 20:18Divide[a, b]uses multiple cores. Is that the behavior you are seeing as well? – Mr.Wizard Jan 22 '14 at 20:33Divideat about 70% – Simon Woods Jan 22 '14 at 21:37SubtractandDivide. I actually do that (forDivideonly and for a different purpuse) – Rojo Jan 23 '14 at 13:29SimplifyForPerformancefunction anyway (we should). I also would change the makeboxing ofPower[_, _?Negative]so that it is visually evident when the slow forms are used – Rojo Jan 23 '14 at 13:31Solve? – b3m2a1 Dec 30 '16 at 21:05HoldandHoldReleaseorInactiveandActivatethe expression to keep the auto-conversion from taking place. For example:expr = Hold[a/b] /. (x_/y_) :> Divide[x, y]and thenDo[ReleaseHold@expr, {50}] // Timing // Firstusing the example above. – Mr.Wizard Feb 17 '24 at 18:47