Skip to main content

performance tuning - Removing calls to MainEvalute when using inlined compiled closures


This question is tightly related to the answer Shaving the last 50 ms off NMinimize.


There @OleksandR shows how inlined closures can be used to eliminate calls to MainEvaluate. This is crucial for my application, since I need every last drop of performance mma can offer. Here is an extremely simplified working example (before you run this code, make sure to evaluate this package, containing the NelderMeadMinimize code):


Needs["CompiledFunctionTools`"]

With[{minimizer =
NelderMeadMinimize`Dump`CompiledNelderMead[
Function[{a, b, c}, (a - d1)^2 + (b - d2)^2 + (c - d3)^2], {a, b, c},
"ReturnValues" -> "OptimizedParameters"],
epsilon = $MachineEpsilon},
orgFitter =
Compile[{{d1, _Real, 0}, {d2, _Real, 0}, {d3, _Real, 0}},
minimizer[RandomReal[{0, 1}, {3 + 1, 3}], epsilon, -1],
CompilationOptions -> {"InlineCompiledFunctions" -> True},
RuntimeOptions -> {"Speed", "EvaluateSymbolically" -> False}]];

StringMatchQ[CompilePrint[orgFitter], "*MainEvaluate*"]
(* -> False *)
orgFitter[12, 2, 3]

Now, if I change (a - d1)^2 + (b - d2)^2 + (c - d3)^2 to calling a compiled function the calls to MainEvalute are not eliminated! Please, instead of the simple myHi2 imagine a monster of a compiled procedure containing several hundred lines.


myHi2 = Compile[{{a, _Real, 0}, {b, _Real, 0}, {c, _Real, 
0}, {d1, _Real, 0}, {d2, _Real, 0}, {d3, _Real,
0}}, (a - d1)^2 + (b - d2)^2 + (c - d3)^2];
With[{minimizer =
NelderMeadMinimize`Dump`CompiledNelderMead[

Function[{a, b, c}, myHi2[a, b, c, d1, d2, d3]], {a, b, c},
"ReturnValues" -> "OptimizedParameters"],
epsilon = $MachineEpsilon},
orgFitter =
Compile[{{d1, _Real, 0}, {d2, _Real, 0}, {d3, _Real, 0}},
minimizer[RandomReal[{0, 1}, {3 + 1, 3}], epsilon, -1],
CompilationOptions -> {"InlineCompiledFunctions" -> True,
"InlineExternalDefinitions" -> True},
RuntimeOptions -> {"Speed", "EvaluateSymbolically" -> False}]];
StringMatchQ[CompilePrint[orgFitter], "*MainEvaluate*"]

(* -> True *)
orgFitter[12., 2., 3.]

How can I eliminate this calls to MainEvaluate?


Also, could someone help me understand, why this does not compile:


orgFitter = Compile[{{d1, _Real, 0}, {d2, _Real, 0}, {d3, _Real, 0}},
With[{minimizer =
NelderMeadMinimize`Dump`CompiledNelderMead[
Function[{a, b, c}, (a - d1)^2 + (b - d2)^2 + (c - d3)^2], {a,
b, c}, "ReturnValues" -> "OptimizedParameters"],

epsilon = $MachineEpsilon},
minimizer[RandomReal[{0, 1}, {3 + 1, 3}], epsilon, -1]],
CompilationOptions -> {"InlineCompiledFunctions" -> True,
"InlineExternalDefinitions" -> True},
RuntimeOptions -> {"Speed", "EvaluateSymbolically" -> False}];

CompilePrint[orgFitter]

Answer



This is actually not specific to the NelderMeadMinimize` package but rather a limitation of the behaviour of Compile with respect to CompilationOptions -> {"InlineCompiledFunctions" -> True}. Essentially, compiled functions will only be inlined (in most cases) if they appear lexically in the input to Compile. You can ensure that in three main ways:


Nested With



This is my preferred approach of the three shown here (although Leonid's LetL macro would be even more preferable). Unfortunately it's a little difficult to demonstrate it without copying your code out in full, but hopefully this abbreviated version is clear enough. Instead of writing


myHi2 = Compile[...];
With[{minimizer = ..., ...}, ...]

you can use


With[{myHi2 = Compile[...]},
With[{minimizer = ..., ...}, ...]
]

which ensures that myHi2 is never encountered by Compile as an opaque function call, but rather as a compiled function. It is then inlined correctly.



"RuntimeOptions" -> {"EvaluateSymbolically" -> False} and Evaluate


Simply define


myHi2 = Compile[
{{a, _Real, 0}, {b, _Real, 0}, {c, _Real, 0},
{d1, _Real, 0}, {d2, _Real, 0}, {d3, _Real, 0}},
(a - d1)^2 + (b - d2)^2 + (c - d3)^2,
"RuntimeOptions" -> {"EvaluateSymbolically" -> False}
];

and you can then use



With[{minimizer = ... Evaluate@myHi2[...] ..., ...}, ...]

If you weren't to use "RuntimeOptions" -> {"EvaluateSymbolically" -> False}, you would see a message and the code may possibly be compiled incorrectly, especially if you have stray values in your session or the real myHi2 contains procedural code.


Manual inlining


Well, I don't mean completely manually. Here is a function that does what CompilationOptions -> {"InlineCompiledFunctions" -> True} does, without requiring that option (this was very useful prior to version 8, and indeed you can probably see that this code is of a certain vintage).


ClearAll[EvaluatePattern];
EvaluatePattern[expr_, patt_, opts___] :=
Module[{holdTemporary},
If[Hold /. {opts} /. Options[EvaluatePattern, Hold],
SetAttributes[holdTemporary, HoldAll]

];
Identity @@ ReplaceRepeated[
holdTemporary[expr],
p : patt :> With[{eval = p}, eval /; True],
MaxIterations -> (
MaxIterations /. {opts} /. Options[EvaluatePattern, MaxIterations]
)
]
];
Attributes[EvaluatePattern] = {HoldFirst};

Options[EvaluatePattern] = {MaxIterations -> $IterationLimit, Hold -> False};

That was just a wrapper that gives more control over the Trott-Strzebonski device. It can be done more concisely (and, I think, clearly) using RuleCondition directly, but I didn't know about RuleCondition when I wrote the above, and as it's undocumented others may not recognise it either. Here comes the function we require in the present context:


ClearAll[InlineFunctionCalls];
InlineFunctionCalls[expr_, opts___] :=
EvaluatePattern[
expr,
sym_Symbol /; MemberQ[{Function, CompiledFunction, LibraryFunction}, Head[sym]],
Sequence @@ Thread[
{MaxIterations, Hold} -> (

{MaxIterations, Hold} /. {opts} /.
Options[InlineFunctionCalls, {MaxIterations, Hold}]
)
]
];
Attributes[InlineFunctionCalls] = {HoldFirst};
Options[InlineFunctionCalls] = {MaxIterations -> $IterationLimit, Hold -> True};

Now we can just write


myHi2 = Compile[...];

With[{minimizer = ... // InlineFunctionCalls, ...}, ...]

which will catch and inline all external Function, CompiledFunction, and LibraryFunction objects (and more reliably so than Compile itself).



I forgot to mention why the final example doesn't compile. The reason is that Compile is HoldAll and it doesn't do a lot of analysis of its argument to find out whether it is compilable or not--if it's not on the list, it's not coming in. In this case Compile can't tell that NelderMeadMinimize`Dump`CompiledNelderMead produces a compiled function, so here it's your responsibility to generate the closure first:


orgFitter = With[{
minimizer = NelderMeadMinimize`Dump`CompiledNelderMead[
Function[{a, b, c}, (a - d1)^2 + (b - d2)^2 + (c - d3)^2], {a, b, c},
"ReturnValues" -> "OptimizedParameters"
],

epsilon = $MachineEpsilon
},
Compile[{{d1, _Real, 0}, {d2, _Real, 0}, {d3, _Real, 0}},
minimizer[RandomReal[{0, 1}, {3 + 1, 3}], epsilon, -1],
CompilationOptions -> {
"InlineCompiledFunctions" -> True, "InlineExternalDefinitions" -> True
},
RuntimeOptions -> {"Speed", "EvaluateSymbolically" -> False}
]
];

Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

equation solving - Invert and fit implicitly defined curve

I need to fit an implicitly defined curve. I thought I could get some data out of Solve , and then using FindFit . Therefore, I would like to find the relation the parametric curve defined by $F(x,y)=0$: Solve[-(1/2) + 1/2 (0.41202 BesselK[0, 0.1 Sqrt[x^2 + y^2]] + (0.101483 x BesselK[1, 0.1 Sqrt[x^2 + y^2]])/Sqrt[x^2 + y^2]) == 0, y] But I can't get an output: Solve was unable to solve the system with inexact coefficients or the system obtained by direct rationalization of inexact numbers present in the system. Since many of the methods used by Solve require exact input, providing Solve with an exact version of the system may help. >> Edit: In particular, I would like to fit the data coming from the curve with the expression of another curve, and not with a function $f(x)$. In particular, since this clearly looks like a cardioid , I would like it to fit to something like it. What other strategies could I try?

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...