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 - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....