3
$\begingroup$

sorry for posting multiple questions about compile, but this is giving me some headache lately. This time my question is about using globally defined variables in a compile statement.

The problem is that when I define a compiled function that uses a global variable like this:

a = 1; (*global variable*)
GlobalVariableCompile = Compile[{{n, _Integer}}, For[i = 1, i < n, i++, a + Cos[i*Pi] + Sin[i*Pi]]];

The function is way slower then the noncompiled version:

GlobalVariable[n_] := For[i = 1, i < n, i++, a + Cos[i*Pi] + Sin[i*Pi]];

which can be easily verified by calling for example:

AbsoluteTiming[GlobalVariableCompile[100000];]
AbsoluteTiming[GlobalVariable[100000];]

What is the reason for this and how can I fix it?

My current solution: My solution right now is to define an additional parameter of the compiled function and pass the global variable a, then everything is very fast as expected (way faster than the noncompiled version):

GlobalVariableCompile = Compile[{{n, _Integer},{a, _Real}}, For[i = 1, i < n, i++, a + Cos[i*Pi] + Sin[i*Pi]]];
AbsoluteTiming[GlobalVariableCompile[100000,a];]
$\endgroup$
7
  • 1
    $\begingroup$ This sounds like a duplicate. Did you search first? Edit: I was probably thinking of (31171) which is not the same. Still looking for other relatives. $\endgroup$
    – Mr.Wizard
    Commented Sep 4, 2013 at 9:43
  • $\begingroup$ Could you describe how you intend to use your function? For example will it be applied many times with the same a value? If that is the case you could rebuild the compiled function when it is called. $\endgroup$
    – Mr.Wizard
    Commented Sep 4, 2013 at 9:54
  • $\begingroup$ @Mr.Wizard I don't recall a question on localization of variables in Compile, except perhaps the one by you, which is not exactly the same. It may be good to have a separate question discussing this, and what happens when the variables are not localized, and why. $\endgroup$ Commented Sep 4, 2013 at 9:57
  • $\begingroup$ @Leonid Yes, I believe I was mistaken about the duplicate unless it's on Stack Overflow. I was hoping you'd show up to answer this as you're far better with Compile than I am. $\endgroup$
    – Mr.Wizard
    Commented Sep 4, 2013 at 10:00
  • 1
    $\begingroup$ @Mr.Wizard I'd like to, but I've been quite busy recently, and also now. Basically, upon a superficial look at the code, it is a matter of variable localization (using Module or Block inside Compile). If this is not done, those variables are considered global by Mathematica. They are then not compiled, and every time they are accessed, there is a call to the main evaluator, which is the reason for the slowness. $\endgroup$ Commented Sep 4, 2013 at 10:06

2 Answers 2

4
$\begingroup$

Create a function with argument a that returns a CompiledFunction:

mkCF[a_] := 
  Compile[{{n, _Integer}}, 
   For[i = 1, i < n, i++, a + Cos[i*Pi] + Sin[i*Pi]]];
cf = mkCF[1];
AbsoluteTiming[cf[100000];]

Inspecting the CompilePrint reveals that there are calls to the evaluator:

Needs["CompiledFunctionTools`"]
CompilePrint[cf]

which should be removed if you need performance. For example:

mkCF2[a_] := 
  Compile[{{n, _Integer}}, Do[a + Cos[i*Pi] + Sin[i*Pi], {i, n}]];
cf2 = mkCF2[1];
AbsoluteTiming[cf2[100000];]

There are no calls to the evaluator left:

CompilePrint[cf2]

As for performance:

AbsoluteTiming[r1 = GlobalVariable[100000];]
AbsoluteTiming[r2 = cf2[100000];]
(*
{1.009310, Null}
{0.054743, Null}
*)

and

r1 == r2
(*True*)
$\endgroup$
1
  • $\begingroup$ Thank you very much, that was enlightening. $\endgroup$
    – Wizard
    Commented Sep 4, 2013 at 11:34
6
$\begingroup$

Based on your comment:

I intend to set the global variable a once and not change it over the whole course of the calculation. So basically a can be treated as a global constant.

I believe you merely need to inject the value of a and properly localize i. These actions can be done with With and Module respectively:

a = 1;

With[{a = a},
 GlobalVariableCompile =
   Compile[{{n, _Integer}},
     Module[{i},
       For[i = 1, i < n, i++, a + Cos[i*Pi] + Sin[i*Pi]]
     ]
   ]
]

With[{a = a}, body] directly substitutes the global value of a for any explicit appearance of the Symbol a in body. More significant to the speed of your code Module[{i}, body] defines i as a variable local to Compile.

ruebenko's use of Do fixes the second problem because Do uses Block-like scoping of its iterators, so they are already localized.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.