# Compile vs FunctionCompile

I wanted to look at the pros & cons of Compile & FunctionCompile. This code below is from Neat Examples in FunctionCompile documentation.

restrictFlow[ x_, y_]:=Module[{z},
z=1-1/(x+I y)^2;
{{x,y},{Re[z],-Im[z]}}
];

decl=FunctionDeclaration[restrictFlow, Typed[{"Real64","Real64"}->"PackedArray"::["Real64",2]]@DownValuesFunction[restrictFlow]];

func=Function[{Typed[x0,"Real64"],Typed[x1,"Real64"],Typed[y0,"Real64"], Typed[y1,"Real64"]},
Table[Flatten[ Table[{restrictFlow[x+d/4,y], restrictFlow[x+d/4,-y]},{x,x0,x1},{y,y0,y1}],2] ,{d, 0,3, 0.05}]
];

usingFunctionCompile=FunctionCompile[decl, func];


Next I try to do the same thing using Compile.

usingCompiled=Compile[{{x0,_Real},{x1,_Real},{y0,_Real},{y1,_Real}},
Module[{zP,zM},
Table[Flatten[ Table[
zP=1-1/(x+d/4+I y)^2;
zM=1-1/(x+d/4-I y)^2;
{{{x,y},{Re[zP],-Im[zP]}}, {{x,-y},{Re[zM],-Im[zM]}}},
{x,x0,x1},{y,y0,y1}],2] ,{d, 0,3, 0.05}
]
]];


I find that the above implementations return nearly the same results. Any ideas how we can make them return exactly the same results? Now look at the timing comparison.

RepeatedTiming[compileResult = usingCompiled[-7,5,0.5, 3];]
(*  {0.00125593, Null} *)


and

RepeatedTiming[functionCompileResult = usingFunctionCompile[-7,5,0.5, 3];]
(* {0.00273739, Null} *)


The CompiledCodeFunction that takes over twice as long to evaluate than the version written with Compile. Coding with Compile is much easier than coding with FunctionCompile. Compile[] evaluates almost instantly, but FunctionCompile[] takes a while to compile. Am I right that we should only use FunctionCompile when we can't use Compile?

On my M1 Max processor and with Mathematica 13.2.1.0, the speed differences are even more severe:

RepeatedTiming[
functionCompileResult = usingFunctionCompile[-7, 5, 0.5, 3];
]

RepeatedTiming[
compileResult = usingCompiled[-7, 5, 0.5, 3];
]


{0.00305243, Null}

{0.000576201, Null}

By using CompilationTarget -> "C" and by performing some simplifications on compile time, I can squeeze out even some more performance from Compile:

usingCompiled2 = Block[{zP, zM, x, y, d},

zP = 1 - 1/(x + d + I y)^2;
zM = 1 - 1/(x + d - I y)^2;

With[{code =
N@ComplexExpand[{{{x, y}, {Re[zP], -Im[zP]}}, {{x, -y}, {Re[zM], -Im[zM]}}}]},

Compile[{{x0, _Real}, {x1, _Real}, {y0, _Real}, {y1, _Real}},
Table[code, {d, 0., 0.25 3., 0.25 0.05}, {x, x0, x1}, {y, y0, y1}]
, CompilationTarget -> "C"
]

]
];


Here the timing:

RepeatedTiming[  compileResult2 = usingCompiled2[-7, 5, 0.5, 3];  ]


{0.0000944208, Null}

And by leaving out Flatten and by doing the rearrangement with ArrayFlatten outside the compiled function, I get even a tiny bit more:

usingCompiled3 = Block[{zP, zM, x, y, d},

zP = 1 - 1/(x + d + I y)^2;
zM = 1 - 1/(x + d - I y)^2;

With[{code = N@ComplexExpand[{{{x, y}, {Re[zP], -Im[zP]}}, {{x, -y}, {Re[zM], -Im[zM]}}}]},

Compile[{{x0, _Real}, {x1, _Real}, {y0, _Real}, {y1, _Real}},
Table[code, {d, 0., 0.25 3., 0.25 0.05}, {x, x0, x1}, {y, y0, y1}]
, CompilationTarget -> "C"
]
]
];


And here the experiment:

RepeatedTiming[
compileResult3 = usingCompiled3[-7, 5, 0.5, 3];
dim = Dimensions[compileResult3];
compileResult3 = ArrayReshape[compileResult3, {dim[[1]], dim[[2]] dim[[3]] 2, 2, 2}];
]


{0.0000802671, Null}

This is 38 times faster than FunctionCompile. And it needs only 0.45 seconds to compile, while FunctionCompile needs 20 seconds.

So, if Compile solves your problem, is easier to code with, compiles faster, and produces this much faster code, then it is really hard to make the case for FunctionCompile...

To be fair, we have to note that FunctionCompile is marked as experimental. So it is still in development. And it can also do a couple of things that Compile cannot.