In Mathematica a vector (or rectangular array) containing all machine size integers or floats may be stored in a packed array. These objects take less memory, and some operations are much faster on them.
RandomReal
produces a packed array when possible. A packed array can be unpacked with the Developer
function FromPackedArray
Consider these timings
lst = RandomReal[1, 5000000];
Total[lst] // Timing
Plus @@ lst // Timing
lst = Developer`FromPackedArray[lst];
Total[lst] // Timing
Plus @@ lst // Timing
Out[1]= {0.016, 2.50056*10^6}
Out[2]= {0.859, 2.50056*10^6}
Out[3]= {0.625, 2.50056*10^6}
Out[4]= {0.64, 2.50056*10^6}
Therefore, in the case of a packed array, Total
is many times faster than Plus @@
but about the same for a non-packed array. Note that Plus @@
is actually a little slower on the packed array.
Now consider
lst = RandomReal[100, 5000000];
Times @@ lst // Timing
lst = Developer`FromPackedArray[lst];
Times @@ lst // Timing
Out[1]= {0.875, 5.8324791357*10^7828854}
Out[1]= {0.625, 5.8324791357*10^7828854}
Finally, my question: is there a fast method in Mathematica for the list product of a packed array, analogous to Total
?
I suspect that this may not be possible because of the way that numerical errors compound with multiplication. Also, the function will need to be able to return non-machine floats to be useful.
First, to avoid confusion, take a look at an example whose results are all representable as hardware machine precision numbers, which must all be less than
Your Total example already had this nice (and fast) property. Here is a variant on your Times example using machine numbers:
Now we can use Compile to make a compiled function to perform this operation efficiently:
It's much faster:
Assuming you have a C compiler and Mathematica 8, you can also automatically compile all the way to C code. A temporary DLL is created and linked back into Mathematica at run-time.
This gives performance not much different to that which a built-in Mathematica function would have:
Note that if your product really will go beyond $MaxMachineNumber (or $MinMachineNumber), then you are better off sticking with
Apply[Times, list]
. The same comment applies to Total, if your results can get that big:Simon's method is fast, but it fails on negative values. Combining it with his answer to my other question, here is a fast solution that handles negatives. Thanks, Simon.
Function
Testing
I've also wondered if there was a multiplicative equivalent to
Total
.A
reallynot so bad solution isAs long as the numbers are positive and aren't too big or small, then the rounding errors aren't too bad. A guess as to what might be happening during evaluation is that: (1) Provided the numbers are positive floats, the
Log
operation can be quickly applied to the packed array. (2) The numbers can then be quickly added usingTotal
's packed array method. (3) Then it's only the final step where a non-machine sized float need arise.See this SO answer for a solution that works for both positive and negative floats.
Let's quickly check that this solution works with floats that yield a non-machine sized answer. Compare with Andrew's (much faster)
compiledListProduct
:If you choose larger (
>1
) reals, thencompiledListProduct
will yield the warningCompiledFunction::cfne: Numerical error encountered; proceeding with uncompiled evaluation.
and will take some time to give a result...One curio is that both
Sum
andProduct
can take arbitrary lists.Sum
works finebut for long
PackedArray
s, such as the test examples here,Product
fails since the automatically compiled code (in version 8.0) does not catch underflows/overflows properly:The work around supplied by the helpful WRI tech support is to turn off the product compilation using
SetSystemOptions["CompileOptions" -> {"ProductCompileLength" -> Infinity}]
. Another option is to uselst=Developer`FromPackedArray[lst]
.