# Why is Compile + RandomReal faster than random real?

In the process of playing around with Compile, I discovered something surprising. RandomReal[{-1, 1}, 3] is a lot slower than Compile[{}, RandomReal[{-1, 1}, 3]]. What could account for this? I would have imagined the two would have similar speeds.

Here's exactly what I did. First a simple timer:

compareTiming[f1_, f2_, its_] :=
With[{
tf1 = First@AbsoluteTiming[Scan[f1, Range[its]]],
tf2 = First@AbsoluteTiming[Scan[f2, Range[its]]]},
(tf2 - tf1)/Max@{tf1, tf2}
]


then:

In[115]:= randomReal3 = Compile[{}, RandomReal[{-1, 1}, 3]];
randomReal3U = Function[RandomReal[{-1, 1}, 3]];
compareTiming[randomReal3[] &, randomReal3U, 10^6]

Out[117]= 0.131774


So the compiled form is 13% faster than the uncompiled form? But RandomReal is built-in and so I would have assumed is already implemented at the C level. What could be causing this?

I understand that I can, of course, generate a huge list of random vectors using RandomReal[{-1, 1}, {10^8, 3}] or something but it would be nice to know why, when I need to get a large number of random vectors one at a time (absent something like storing $10^8$ of then and pulling randomly) the fastest way to do this is with a compiled version of a built-in function.

• Compile does some argument checking during the compilation, which RandomReal[] might do at run-time. Also, getting just 3 random numbers means the overhead is a higher proportion. With a sample size of 300000, I get that the uncompiled version is faster. – Michael E2 Jan 7 '17 at 23:18
• I get a different results if I use Do instead of Scan, too. – Michael E2 Jan 7 '17 at 23:20
• The first comment makes me feel better. I was expecting the user-compiled version to be slower. The second is just fascinating. What do you think is causing that? – b3m2a1 Jan 7 '17 at 23:21
• You mean Do[i, {i, Range[10^6]}], yes? But I too get that it's much faster to use the Do imp. – b3m2a1 Jan 7 '17 at 23:29
• I don't have a good idea....Yeah I was just going to fix that...It's not clear that different overheads explain which method is faster, though. – Michael E2 Jan 7 '17 at 23:30

For 10^6 calls, the difference of RandomReal and the compiled version is 0.279 s on my machine. That makes a difference of 279 ns per call which is really next to nothing.

To show you, where the difference comes from, consider the following completely unrelated example

compareTiming[f1_, f2_, its_] :=
With[{
tf1 = First@RepeatedTiming[Do[f1[], {its}]],
tf2 = First@RepeatedTiming[Do[f2[], {its}]]},
(tf2 - tf1)/Max[tf2, tf1]
]

testC = Compile[{}, 0];
test[] := 0;

compareTiming[testC, test, 10^6]
(* -0.2 *)


Here, the compiled version is slower. Now, we throw the overhead of another high-level function call in:

f[] := 0;
test[] := f[];

compareTiming[testC, test, 10^6]
(* 0.27 *)


This suggests that the difference in runtime does not come from creating the random vector, but it comes from the interpreter evaluating the function f (or RandomReal) as it has to access the DownValues of this function

• The thing is, I'm intentionally not using any functions with DownValues. In fact the compiled form I supplied should be even slower for the fact that I wrapped it in a Function call so it's calling Function then CompiledFunction at each step. I agree it's unlikely that the issue comes from actually generating the vector. I assume those functions are the same. – b3m2a1 Jan 8 '17 at 4:50
• But your definition of randomReal3U contains a Function as well. So this has to evaluate Function and then RandomReal. Have you tried what you get with compareTiming[randomReal3[] &[] &, randomReal3U, 10^6] which adds another layer of Function to the compiled call? – halirutan Jan 8 '17 at 5:02