While I have seen a lot of documentation regarding the inefficiencies of loops in Mathematica, I am still curious as to why-- while I know it is intentionally developed this way to allow for improved functionality elsewhere, what in the barebones of the language actually causes the loop inefficiencies?
1 Answers
I'll try to list some reasons, though the following will not be exhaustive.
Mathematica being a high-level interpreted language, Mathematica code is generally slower than what is possible e.g. in C.
Writing something as a loop means that the loop body must be repeatedly executed by the Mathematica interpreter. If the same operation can be expressed as a single function call, there is a possibility to implement in (internally) in a lower-level language and allow it to run much faster.
Example:
Table[x^2, {x, 1, 10000000}]; // RepeatedTiming
(* {0.168, Null} *)
Range[1, 10000000]^2; // RepeatedTiming
(* {0.029, Null} *)
Doing arithmetic on an entire array, as above, is called vectorization. The internal implementation of such operations will often make use of multiple CPU cores as well as SIMD instructions, both of which would be basically impossible when the loop body can be arbitrary Mathematica code.
Along the same line of thought, using a construct that involves more pieces of Mathematica code being evaluated will be slower.
Example:
AbsoluteTiming[
arr = ConstantArray[0, 100000];
For[i = 1, i <= Length[arr], ++i,
arr[[i]] = i^2
];
]
(* {0.136787, Null} *)
AbsoluteTiming[
arr = ConstantArray[0, 100000];
Do[
arr[[i]] = i^2,
{i, Length[arr]}
];
]
(* {0.086007, Null} *)
Observe that the For needs to evaluate more pieces of Mathematica code. Setting i=1 is explicit, incrementing it is explicit, comparing it to Length[arr] is also explicitly. Some of these are done internally in Do so they can be faster.
Arguably Table is also a looping construct, but it is of a different sort. The body of the Table does not need to have side effects. Setting a value, as in arr[[i]] = ..., is a side effect of evaluating =. Thus an operation like the above requires side effects with Do and For, but not with Table.
Functional construct with no side effects are much more amenable to automatic optimization. Table in particular can automatically Compile its body to allow it to run faster. This is why we get so much better performance with the following:
Table[i^2, {i, 100000}]; // RepeatedTiming
(* {0.0015, Null} *)
Such constructs are also very straightforward to parallelize. Just switch the Table out for a ParallelTable.
The general idea is that if you use programming constructs that express what you are doing in a more specific way, the system has more freedom to choose the fastest way to run it.
Writing code in terms of loops basically means spelling out each small step of the algorithm. It does not convey your high-level goal in a manner that a computer can understand—instead it focuses on the details. Compare "take the square of each element of this array" to the same written as a For loop. The For-based version will necessarily specify details such as initialize an array, initialize an iterator, increment an iterator, compare the iterator to a value and decide what to do based on the result, etc. The very specific algorithm that you express with a typical For loop is not how the faster alternatives (such as Table or vectorized Power) work internally. By specifying each step, you forbid the system for doing anything else.
The takeaway is that when programming in high-level languages, it is better to express our algorithms with higher-level constructs. These higher level constructs can have very efficient internal implementations.
- 234,956
- 30
- 623
- 1,263
Forloops specifically. Most of the other explicit loop constructs (WhileandDo) would be most effective in such situations as well. However, because of the guarantees made by the stricter iteration constructs (e.g.Map), much more can be automatically performed to make them faster overall.Forloops, on the other hand, can't be guaranteed to terminate at all for all interior expressions, much less parallelize. – eyorble Apr 08 '19 at 17:07