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

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...