Skip to main content

performance tuning - How to compile a diagonal array efficiently?


For example, if we use some functions defined prior to Compile, we usually have the main evaluators in the compiled codes, pointing to the definition of the function.


f[t_] := If[t <= 1., Cos[t]*Sin[t], 0.]

CompilePrint@
Compile[{{t, _Real}}, IdentityMatrix[2] - f[t],
CompilationOptions -> {"InlineCompiledFunctions" -> True,

"InlineExternalDefinitions" -> True}]


      1 argument
1 Integer register
3 Real registers
2 Tensor registers
Underflow checking off
Overflow checking off
Integer overflow checking on

RuntimeAttributes -> {}

R0 = A1
I0 = 2
Result = T(R2)1

1 T(I2)0 = MainEvaluate[ Hold[IdentityMatrix][ I0]]
2 R1 = MainEvaluate[ Hold[f][ R0]]
3 R2 = - R1
4 T(R2)1 = R2 + T(I2)0

5 Return

We can avoid main evaluator by using Evaluate in the function, which specifically substidue the definition of the function. However, sometimes this introduces repeated code in the compiled results. For instance, in the following example, Evaluate simplify expands a number into a matrix and repeatedly calculated this expression for two times. We can see that the 26-50 lines of the compiled code are essentially the same as 1-25 lines.


CompilePrint@
Compile[{{t, _Real}}, Evaluate[IdentityMatrix[2] - f[t + 1./2.]],
CompilationOptions -> {"InlineCompiledFunctions" -> True,
"InlineExternalDefinitions" -> True}]


      1 argument

1 Boolean register
1 Integer register
12 Real registers
3 Tensor registers
Underflow checking off
Overflow checking off
Integer overflow checking on
RuntimeAttributes -> {}

R0 = A1

I0 = 1
R3 = 1.
R4 = 7.
R1 = 0.5
R7 = 0.
Result = T(R2)2

1 R2 = R1 + R0
2 B0 = R2 <= R3 (tol R4)
3 if[ !B0] goto 10

4 R2 = R1 + R0
5 R5 = Cos[ R2]
6 R6 = Sin[ R2]
7 R5 = R5 * R6
8 R6 = R5
9 goto 11
10 R6 = R7
11 R5 = - R6
12 R6 = I0
13 R6 = R6 + R5

14 R5 = R1 + R0
15 B0 = R5 <= R3 (tol R4)
16 if[ !B0] goto 23
17 R5 = R1 + R0
18 R8 = Cos[ R5]
19 R9 = Sin[ R5]
20 R8 = R8 * R9
21 R9 = R8
22 goto 24
23 R9 = R7

24 R8 = - R9
25 T(R1)0 ={ R6, R8 }
26 R6 = R1 + R0
27 B0 = R6 <= R3 (tol R4)
28 if[ !B0] goto 35
29 R6 = R1 + R0
30 R8 = Cos[ R6]
31 R9 = Sin[ R6]
32 R8 = R8 * R9
33 R9 = R8

34 goto 36
35 R9 = R7
36 R8 = - R9
37 R9 = R1 + R0
38 B0 = R9 <= R3 (tol R4)
39 if[ !B0] goto 46
40 R9 = R1 + R0
41 R10 = Cos[ R9]
42 R11 = Sin[ R9]
43 R10 = R10 * R11

44 R11 = R10
45 goto 47
46 R11 = R7
47 R10 = - R11
48 R11 = I0
49 R11 = R11 + R10
50 T(R1)1 ={ R8, R11 }
51 T(R2)2 ={ T(R1)0, T(R1)1 }
52 Return


So is there a way to fix this repeating?


Note that change only the argument of the external function, the behavior changes, why?


CompilePrint@
Compile[{{t, _Real}}, Evaluate[IdentityMatrix[2] - f[t]],
CompilationOptions -> {"InlineCompiledFunctions" -> True,
"InlineExternalDefinitions" -> True}]


      1 argument
1 Boolean register

1 Integer register
6 Real registers
3 Tensor registers
Underflow checking off
Overflow checking off
Integer overflow checking on
RuntimeAttributes -> {}

R0 = A1
I0 = 1

R1 = 1.
R2 = 7.
R5 = 0.
Result = T(R2)2

1 B0 = R0 <= R1 (tol R2)
2 if[ !B0] goto 8
3 R3 = Cos[ R0]
4 R4 = Sin[ R0]
5 R3 = R3 * R4

6 R4 = R3
7 goto 9
8 R4 = R5
9 R3 = - R4
10 R4 = I0
11 R4 = R4 + R3
12 T(R1)0 ={ R4, R3 }
13 T(R1)1 ={ R3, R4 }
14 T(R2)2 ={ T(R1)0, T(R1)1 }
15 Return



Answer



For nicer inlining techniques, see my answer here


In what IdentityMatrix[2] - f[t] evaluates to, there is pretty much four times the same code.


Clear[t];
IdentityMatrix[2] - f[t]


{{1 - If[t <= 1., Cos[t] Sin[t], 0.], -If[t <= 1., Cos[t] Sin[t], 0.]}, {-If[t <= 1., Cos[t] Sin[t], 0.], 1 - If[t <= 1., Cos[t] Sin[t], 0.]}}




I will inline f using DownValues. Throughout this answer, you should read a construction like g@@(Hold[...f[t]...]/.DownValues[f]) as g[...f[t]...], only realising that now f has been inlined. Sadly the syntax highlighter now makes the colours of the variables a bit confusing, but what can you do.


Note that a call to MainEvaluate to compute a big IdentityMatrix is not bad. It is also not so bad to calculate f[t] using MainEvaluate only once if the matrix is very large. But I am not sure if you have large matrices in mind, or if you are interested in generating small matrices quickly (a lot of times?).


Big matrices


The following code makes one call to MainEvaluate to get the IdentitiyMatrix. It only calculates the Sin and the Cos once.


f[t_] := If[t <= 1., Cos[t]*Sin[t], 0.]

cfu =
Function[Null, Compile[{{t, _Real}}, #], HoldAll] @@
(
Hold[

IdentityMatrix[2] - f[t - 0.5]
] /. DownValues[f]
)

You could also write this like this if you prefer


specialReleaseHold[expr_] := Delete[expr, {0, 0}]

cfu2 =
specialReleaseHold@
Hold[Compile][

Unevaluated[{{t, _Real}}]
,
Unevaluated @@
(
Hold[
IdentityMatrix[2] - f[t - 0.5]
] /. DownValues[f]
)
]


And we have


CompilePrint@cfu == CompilePrint@cfu2


True



ReleaseHold would actually do the same thing as specialReleaseHold here (in fact, everywhere in this answer), but in similar cases using ReleaseHold could be bad.


Another alternative (focussed on large matrices), using ConstantArray


cfu3 =
specialReleaseHold@

Hold[Compile][
Unevaluated[{{t, _Real}}],

Unevaluated @@
Hold[
Block[
{res, i},
res =
ConstantArray[
-f[t - 0.5], {2, 2}

];
For[i = 1, i <= 2, i++,
res[[i, i]] += 1

];
res
]
] /. DownValues[f]
]


Small matrices


For small matrices we want to avoid MainEvaluate altogether. We could do


cfu4 =
specialReleaseHold@
Hold[Compile][
Unevaluated[{{t, _Real}}],
Unevaluated @@
(
Hold[
Block[{res, term},

term = -f[t - 0.5];
res = {{1., 0.}, {0., 1.}};
res + term
]
] /. DownValues[f]
)

];

Which somehow turns out to be faster than



cfu5 =
specialReleaseHold@
Hold[Compile][
Unevaluated[{{t, _Real}}],
Unevaluated @@
(
Hold[
Block[{term},
term = -f[t - 0.5];
{{1. + term, 0. + term}, {0. + term, 1. + term}}

]
] /. DownValues[f]
)
];

As we will see further below, neither of these functions use MainEvaluate.


Uncompiled (small matrices)


We will see indeed that compiling pays off. For reference, we define the following function


fu =
ReleaseHold@

Hold[Function][
Hold[t],
Hold[
Block[{term},
term = -f[t - 0.5];
{{1. + term, 0. + term}, {0. + term, 1. + term}}
]
] /. DownValues[f]
];


Comparison


cfu[2.] == cfu2[2.] == cfu3[2.] == cfu4[2.] == cfu5[2.] == fu[2.]==
IdentityMatrix[2]


True



cfu[0.1] == cfu2[0.1] == cfu3[0.1] == cfu4[0.1] == cfu5[0.1] == fu[0.1]



True



StringFreeQ[CompilePrint@#, "MainEvaluate"] & /@
{cfu, cfu2, cfu3,
cfu4, cfu5}

{False, False, False, True, True}


Function[Do[#[0.1], {10000}] // Timing // First] /@ {cfu, cfu2, cfu3, 
cfu4, cfu5, fu}



{0.023010, 0.020006, 0.024175, 0.009206, 0.011438, 0.077503}



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[ ...

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...

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?