# Table vs. ConstantArray efficiency with compilation

I've been trying to optimize a bit of code the past couple of days, but think I might be misunderstanding how compilation on certain target functions works. As a most barebones example, I've attached a screenshot below of the speeds for array creation using ConstantArray, Table, and the compiled versions of each.

We see that ConstantArray is nearly 10x faster than Table in this most simple case (perhaps not surprisingly given that simplicity). When we try to compile, ConstantArray cannot be compiled, and so the time taken just increases because of overhead, I assume. However, the compiled Table call also increases in time. This is unexpected to me, so I figure I must be doing something wrong in this comparison. Any thoughts on how to yield a speed increase from compiling Table, if it is possible in such a situation? I should also note that using Array is categorically slower than both other options, for both uncompiled, and compiled.

• Not quite related to the discussion but it's just funny: Compile[{}, Table[1., 10000000]] is fully compiled, but Compile[{}, Table[1., 10^7]] isn't. One has to use the old syntax Compile[{}, Table[1., {10^7}]]. Tested in 12.1.0 for Linux x86 (64-bit) (March 4, 2020) (Wolfram Cloud). – xzczd Mar 31 at 6:05
• Great question (+1). Also in version 12 for macos, even Compile[{}, Table[1., {10000000}], CompilationTarget -> "C", RuntimeOptions -> "Speed"] takes four times longer than ConstantArray[1., {10000000}]. Hard to say what goes wrong there. – Henrik Schumacher Mar 31 at 9:05
• There are a few functions like this. With the old compiler I found that functions like NestWhileList and Erf (to 10 thousand digits precision) were slower with the compiler. A very senior programmer at WRI explained to me that this was because the symbolic nature of NestWhileList and the symbolic processing done internally for Erf are so efficient that it beats compilation. Haven't tried it on the new compiler yet, though. – Andreas Lauschke May 13 at 19:38

There are a few additional options that improve performance with the functions in the Code Compilation guide, In version 12.1 these are experimental functions so they may change in future versions.

I'll give timings of c1 and c2 on my ancient laptop for reference.

c1 = Compile[{}, ConstantArray[1., 10000000]];
c2 = Compile[{}, Table[1., 10000000]];
a3 = c1[]; // RepeatedTiming
a4 = c2[]; // RepeatedTiming

{0.344, Null}
{0.584, Null}


With FunctionCompile and a Typed argument we get a boost to both functions.

c3 = FunctionCompile[Function[Typed[n, "Integer64"], ConstantArray[1., n]]];
c4 = FunctionCompile[Function[Typed[n, "Integer64"], Table[1., n]]];
a5 = c3[10000000]; // RepeatedTiming
a6 = c4[10000000]; // RepeatedTiming

{0.24, Null}
{0.240, Null}


We can boost performance even more with KernelFunction on the Wolfram Language functions and TypeSpecifier on their result. However, there is a regression with Table which may explain why these compile functions are still experimental.

c5 = FunctionCompile[
Function[
Typed[n, "Integer64"],
Typed[
KernelFunction[ConstantArray], {"Real64", "Integer64"} ->
TypeSpecifier["PackedArray"]["Real64", 1]
][1., n]
]];

c6 = FunctionCompile[
Function[
Typed[n, "Integer64"],
Typed[
KernelFunction[Table], {"Real64", "Integer64"} ->
TypeSpecifier["PackedArray"]["Real64", 1]
][1., n]
]];

a7 = c5[10000000]; // RepeatedTiming
a8 = c6[10000000]; // RepeatedTiming

{0.10, Null}
{0.75, Null}


So although these experimental functions still need some work in some cases they are giving a glimpse of the improvements that can be expected over Compile once they are finalised. In this example ConstantArray dropped from 0.344 with Compile down to 0.10 with the FunctionCompile - KernelFunction combo. While Table dropped from 0.584 with Compile to 0.24 with FunctionCompile.

Hope this helps.

• Thanks for the reply! It's a bit disheartening that to achieve even modest boosts so much cumbersome code is required... – KHAAAAAAAAN Apr 2 at 2:41
• @KHAAAAAAAAN I think it is because Wolfram Language is dynamically typed so to get around all of the options a function can take you need to specify exactly the option you are targeting to make the compiled code faster. – Edmund Apr 2 at 2:49