5
$\begingroup$

I am trying to compile a little summation function. As far as I understand, because the built-in Sum function can also return NaN objects I have to make sure only numbers get out before returning.

X = Compile[{}, (
   result = Sum[i, {i, 10}];
   If[NumberQ[result], Return[result], Abort[]];
   )];

The problem is that I keep getting the following two warnings...

CompiledFunction::cfse: Compiled expression True should be a machine-size real number. CompiledFunction::cfex: Could not complete external evaluation at instruction 2; proceeding with uncompiled evaluation.

I guess the glitch is in the NumberQ function and its return type.

Any ideas on how to handle this issue correctly and effectively?

EDIT:

When I omit the check for numeric values while inserting a Return[] statement Mathematica falls back to the uncompiled version (warning: Compile::cret).

$\endgroup$
0

4 Answers 4

6
$\begingroup$

For the error handling use the compilers error handling mechanism:

cffail = Compile[{{x, _Real, 1}}, Exp[x], 
   "RuntimeOptions" -> {"RuntimeErrorHandler" -> Function[$Failed]}];
cffail[{1000.}]

The Function can be anything (Throw[..],...).

For the summation you could use Total in stead:

Compile[{{x, _Real, 1}}, Total[x]]
$\endgroup$
8
  • $\begingroup$ Even though there doesn't seem to be any call to MainEvaluate, I didn't notice any difference in execution speed on my system between: tot = Compile[{{x, _Real, 1}}, Total[x]] ; Timing[tot[RandomInteger[4, 100000000]]] and Timing[Total[RandomReal[4, 100000000]]]. I'm not sure why, but Total applied to integers seemed to be about 15% quicker than applied to reals. $\endgroup$ Apr 16, 2012 at 9:47
  • $\begingroup$ @image_doctor, I am not sure what you are trying to say here. I wanted to point out that Total will be faster than Sum. Also, as a side note, AbsoluteTiming is better suited for than Timing. $\endgroup$
    – user21
    Apr 16, 2012 at 10:09
  • $\begingroup$ Thanks for the tip about AbsoluteTiming. I'm not always sure about what I'm saying either :) Perhaps it was that in this context, with Total and other 'simple' functions there is little if any advantage to using a compiled function, it may be that Total is already quite well optimised. I can see there is a difference between using Sum and Total. Total sums an existing list of numbers whereas Sum needs to construct an iteration across a range of not yet existing values, perhaps this means that they can't be compared directly. $\endgroup$ Apr 16, 2012 at 13:55
  • $\begingroup$ @image_doctor Compiled Total is just a direct call to the kernel function TotalAll, which is probably the same one used to implement the ordinary Total for machine number inputs. In regard to why Total of integers is faster than reals: memory bandwidth. Reals are twice the size of integers. $\endgroup$ Apr 16, 2012 at 22:05
  • $\begingroup$ @OleksandrR Interesting, I was assuming that RandomInteger, which I used to generate the list to be summed, returned Mathematica arbitrary length exact integers as this is what is described in the "Types of Numbers" tutorial. So I thought they would consume more space than a machine sized reals. So does this mean exact integers are dynamic in size, or that RandomInteger and presumably other functions, return a machine sized integer type? $\endgroup$ Apr 17, 2012 at 7:26
2
$\begingroup$

I'm having to read between the lines because you did not fully specify your problem, however I suspect that you need to use the third argument of Compile.

func =
  Compile[{{length}},
   (result = Sum[i^2, {i, length}]; If[NumberQ[result], result, Abort[]]),
   {{NumberQ[_], True | False}}
  ]

func[5]
55.
$\endgroup$
1
  • $\begingroup$ Although if you look at the result of CompilePrint, you will see that this is not very efficient, since sum is not compiled, but just called using MainEvaluate. $\endgroup$
    – Ajasja
    Apr 16, 2012 at 8:16
2
$\begingroup$

Are you definitely sure you need the error checking for NaN inside Compile? The error checking seems to generate very inefficient compiled code. It basically only calls MainEvaluate, so you gain nothing by compiling.

data = Range[1000];
func /@ data; // AbsoluteTiming
func1 /@ data; // AbsoluteTiming

(* ==> {0.1280073, Null} *)

(* ==> {0.0140008, Null} *)

func = 
  Compile[{{length}}, (result = Sum[i^2, {i, length}]; 
    If[NumberQ[result], result, Abort[]]), {{NumberQ[_], 
     True | False}}];
func1 = Compile[{{length, _Integer}}, Sum[i^2, {i, length}]];

data = Range[1000];
d1 = func /@ data; // AbsoluteTiming
d2 = func1 /@ data; // AbsoluteTiming
d1 == d2

(* ==> {0.1160067, Null} *)

(* ==> {0.0140008, Null} *)

(* ==> True *)

Needs["CompiledFunctionTools`"]
CompilePrint[func]
CompilePrint[func1]

(*
==> "
        1 argument
        1 Boolean register
        4 Integer registers
        7 Real registers
        Underflow checking off
        Overflow checking off
        Integer overflow checking on
        RuntimeAttributes -> {}

        R0 = A1
        I1 = 0
        R3 = 0.
        Result = R2

1   V17 = MainEvaluate[                                  2
Function[{length}, result = Sum[i , {i, length}]][ R0]]
2   B0 = MainEvaluate[ Function[{length}, NumberQ[result]][ R0]]
3   if[ !B0] goto 7
4   R5 = MainEvaluate[ Function[{length}, result][ R0]]
5   R2 = R5
6   goto 9
7   R1 = MainEvaluate[ Function[{length}, Abort[]][ R0]]
8   R2 = R1
9   Return
"
*)

(*
==> "
        1 argument
        9 Integer registers
        Underflow checking off
        Overflow checking off
        Integer overflow checking on
        RuntimeAttributes -> {}

        I0 = A1
        I3 = 0
        Result = I4

1   I4 = I3
2   I5 = I0
3   I7 = I3
4   goto 8
5   I6 = Square[ I7]
6   I8 = I4 + I6
7   I4 = I8
8   if[ ++ I7 < I5] goto 5
9   Return
"
*)

So my recommendation is to skip the check (what could generate the NaN in such a simple sum anyway?) or check for NaN values outside the compiled function.

$\endgroup$
2
  • $\begingroup$ But when I run your "func1" the result I get is Null. At the moment I use the Return[] statement it falls back to the uncompiled version because "the return types in the Sum statement are different (Compile::cret)"... $\endgroup$
    – Petr
    Apr 16, 2012 at 9:39
  • $\begingroup$ Works fine for me. func1[55] returns 56980. Which mma version are you using? $\endgroup$
    – Ajasja
    Apr 16, 2012 at 10:48
1
$\begingroup$

I’m afraid your base assumption here is false, and the sum compiles much better without this call to NumberQ. See:

<< CompiledFunctionTools`;
func = Compile[{{length, _Integer}}, (
    Sum[i^2, {i, 1, length}]
    )];
CompiledFunctionTools`CompilePrint[func]

The output of CompilePrint shows that the sum is performed without any call to MainEvaluate, which you absolutely want to avoid if you to compile efficiently.

$\endgroup$
1
  • $\begingroup$ As a side note, this uses the compiled version up to parameters values of around 1870 when, I guess, numerical overflow causes it to switch back to the uncompiled version. $\endgroup$ Apr 16, 2012 at 12:56

Your Answer

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

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