### Why is there no equivalent of `Total`

for products?

There isn't an equivalent of `Total`

for products, and I believe that there is a reason.

The high performance of `Total`

(compared to `Plus`

) becomes especially important for large arrays. Adding together lots of numbers is usually straightforward. But multiplying them has a high risk of over- or underflow.

Example:

```
Times @@ RandomReal[1, 10000]
(* 3.49264640062689*10^-4292 *)
Times @@ RandomReal[10, 10000]
(* 1.26694123858894*10^5708 *)
```

These results are well under and over the smallest and largest representable machine numbers:

```
{$MinMachineNumber, $MaxMachineNumber}
(* {2.22507*10^-308, 1.79769*10^308} *)
```

Mathematica *can* deal with such huge and tiny numbers, but it must use arbitrary precision arithmetic, which is slow anyway.

We must keep this in mind in the experiments we will do below.

### What are fast ways to multiply numbers in a list?

To minimize the risk of under/overflow, let us use the following array for benchmarking:

```
arr = RandomReal[E, 100000];
```

The simplest way is `Times @@ arr`

, but this is not particularly fast. The reason is unpacking.

```
Times @@ arr // RepeatedTiming
(* {0.014, 2.55876*10^191} *)
```

Several of the tricks shown in this thread, like `Tr[arr, Times]`

, are basically the same thing.

The simplest fast way is

```
Product[a, {a, arr}] // RepeatedTiming
(* {0.0014, 2.55876*10^191} *)
```

I generally recommend this approach.

The fastest way, at the cost of accuracy, is transforming the product to a sum using logarithms:

```
Exp@Total@Log[arr] // RepeatedTiming
(* {0.000106, 2.55876*10^191} *)
```

This approach makes sense given the nature of the operation: the results will typically span orders of magnitude. The under/overflow is delayed until the final `Exp`

. The thing to watch out for is precision and the presence of negative values.

What about compilation?

Direct procedural approaches with `Compile`

are going to be equivalent to (and perform similarly with)

```
cf = Compile[{{arr, _Real, 1}},
Product[a, {a, arr}]
]
```

Benchmarking shows that this is no better than a naked `Product`

. Perhaps `Product`

already auto-compiled?

```
cf[arr] // RepeatedTiming
(* {0.0014, 2.55876*10^191} *)
```

It does becomes faster if we compile to C:

```
cf = Compile[{{arr, _Real, 1}},
Product[a, {a, arr}],
CompilationTarget -> "C"
]
cf[arr] // RepeatedTiming
(* {0.00014, 2.55876*10^191} *)
```

The important thing to pay attention to here is under- and overflow. Because compiled functions operate with machine numbers, under- or overflow will not only affect performance. It will also affect the result.

Observe:

```
arr = RandomReal[2, 100000];
cf[arr]
(* 0. *)
```

The result is not really zero. The function returns zero only because of underflow. The true result is

```
Product[a, {a, arr}] // RepeatedTiming
(* {0.023, 1.87228152952573*10^-13556} *)
```

Notice that after underflow, `Product`

becomes much slower, even slower than `Times @@ ...`

. This is due to having to switch to arbitrary precision arithmetic. But it does give us the correct result.

The fix is to explicitly instruct `Compile`

to handle under/overflows:

```
cf = Compile[{{arr, _Real, 1}},
Product[a, {a, arr}],
CompilationTarget -> "C",
RuntimeOptions -> {"CatchMachineUnderflow" -> True, "CatchMachineOverflow" -> True}
]
cf[arr] // RepeatedTiming
During evaluation of CompiledFunction::cfne: Numerical error encountered; proceeding with uncompiled evaluation.
During evaluation of CompiledFunction::cfne: Numerical error encountered; proceeding with uncompiled evaluation.
(* {0.024, 1.87228152952573*10^-13556} *)
```

Now for *this array*, the compiled function is not any faster than the plain `Product`

, because it must resort to uncompiled evaluation anyway. But it does return the correct result, and the compiled function still has the potential to be fast for other arrays. Unfortunately, under- and overflow checking in `Compile`

does seem to introduce a performance penalty.

### To sum up

- The fastest way is always transforming to a sum:
`Exp@Total@Log[arr]`

. This may deteriorate accuracy and will not work for negative numbers.
- The fastest
*direct* way is `Product[a, {a, arr}]`

.
- If you compile solution (2), expect a speedup only with
`CompilationTarget -> "C"`

, and remember to turn on catching under- and overflows using `RuntimeOptions -> {"CatchMachineUnderflow" -> True, "CatchMachineOverflow" -> True}`

.

7I'm curious, what is it you don't like about

`Times @@ list`

? – RunnyKine – 2014-10-13T06:56:18.8602

`Tr[{1, 2, 3, 4}, Times]`

works too, but is not really better than using`Apply`

. – Yves Klett – 2014-10-13T06:59:04.523