Compile procedure - Maple Help

Home : Support : Online Help : Programming : Compiling : Compile procedure

Compile a numeric Maple procedure to native code

 Calling Sequence Compiler:-Compile( p, opts )

Parameters

 p - {procedure,list(procedure)}; a Maple procedure or list of procedures opts - options; one of trace=true/false, optimize=true/false, inmem=true/false and warnings=true/false

Description

 • The Compiler:-Compile command compiles a limited subset of Maple procedures (described in more detail below) to native code and returns the compiled procedure. Note that the module Compiler is not a package.
 • Typically, the Compiler can provide improved performance for procedures that run long enough to justify the overhead of compilation, or which are called so frequently as to amortize this overhead.
 • Translation is effected by the use of a special purpose CodeGeneration backend. Maple procedures are translated to C language code, which is subsequently compiled by an external C compiler. The compiled code is then dynamically linked into Maple, using the external calling facility, to produce a Maple-callable procedure that, when called with appropriate inputs, will invoke the compiled code. Because the overhead of interpretation has been largely removed from the compiled code, in many cases, a compiled procedure will run faster even than it does when run under evalhf.
 • The set of procedures that can be successfully translated by the numeric code compiler is very limited. In general, procedures are limited to those that manipulate data types that have fairly direct representations as hardware integers, floats, and arrays containing these types of values. In part, procedures are limited to those that can be successfully translated to C by the CodeGeneration package. (See CodeGeneration/TranslationDetails for detailed information.)
 • There are additional restrictions imposed by the numeric code compiler on top of those imposed by the CodeGeneration package.
 No nested procedures can be translated.
 No modules can be translated.
 Exception handling (other than merely raising an exception) cannot be translated.
 Procedures that return arrays or allocate new arrays cannot be translated.
 Procedures that call other procedures about which insufficient type information can be obtained at compile time cannot be translated.
 Integer data are limited to word-sized integers on the current hardware. Computations that produce larger integers will raise an overflow exception.
 Floating-point computations, including complex floating-point operations, are carried out entirely in hardware precision (like evalhf).
 Since all supported hardware uses radix 2 floating-point arithmetic, while Maple's software floating-point system is a radix 10 system, computations that depend crucially on the radix will produce different results.
 • A single procedure or a list of procedures may be passed as the first argument to the Compiler:-Compile command. If the first argument is a list, then the Compiler:-Compile command automatically maps over this list, returning a list of the compiled procedures.
 • Since correct compilation is heavily dependent upon the Compiler determining the types of parameters and variables, it is advisable to annotate procedures submitted to the compiler with type information.  While this is often not necessary, as the Compiler will usually deduce the correct types for variables, it may be needed in certain cases where the Compiler deduces the incorrect type.

The External C Compiler

 • A crucial component of the compilation tool chain is the external C compiler, which is used to compile the generated C code.
 • On all platforms, the default compiler is LLVM which is distributed and linked into Maple.  LLVM will be used to compile code as long as the inmem=false option is not passed to the Compiler:-Compile command.
 • On Windows platforms, the Microsoft compiler, if available, can be used to compile the emitted C code if inmem=false is passed in as an option.
 • On all UNIX and Mac OS X systems, the GNU C compiler can be used to compile the emitted C code. The GNU C compiler is not distributed with Maple, and must be installed separately, if it is not already present on the system. The numeric code compilation facility relies on the "gcc" command being present in the program search path (the value of the "PATH" environment variable). To access "gcc" the inmem=false option must be passed to the Compiler:-Compile command.
 • You can set infolevel[Compiler] to a positive value (1 or 3) to obtain some information about what the compiler is doing.

Options

 • The Compiler:-Compile command accepts several options that affect how code is generated.
 • Use the trace option to enable tracing of compiled procedure execution. This is similar to the trace option for ordinary Maple procedures. You can specify the trace option by writing trace = true (or just trace) or trace = false as an argument to the Compiler:-Compile command. By default, tracing is not enabled.
 • Enable extra warnings during translation by specifying warnings = true (or just warnings) as an option to the Compiler:-Compile command. To disable extra warnings (the default), specify warnings = false.
 • The inmem option can be used to select which external C compiler is used.  On all platforms LLVM will be used unless the option inmem=false is passed.  If inmem=false is passed in, the GNU C or Microsoft Visual Studio compilers will be used on the UNIX/OS X and Windows platforms respectively.
 • Request that Maple attempt to optimize the input procedure by specifying the optimize option, in the form optimize = true (or just optimize) or optimize = false. At present, this causes the code to be optimized prior to translation by passing it through codegen[optimize]. Optimization is not performed by default.

Temporary File Storage

 • The compiler generates a number of temporary files during execution. The shared library or DLL into which the code is compiled is stored in a temporary directory unique to each user and process. These files are meant to persist for the duration of the Maple session in which they were created. You can control the directory into which these temporary directories are placed by means of the system environment variable MAPLE_TEMPDIR, which must be set to a directory (or folder) to which you have write permissions.
 • If the system environment variable MAPLE_TEMPDIR is not defined, or if it cannot be used (for example, if it does not exist or is not writable), then a platform-dependent algorithm is used to try to find a suitable directory in which to store files. If no other directory can be found, then the current working directory is used. Note that, on Windows, the temporary directory used may not have spaces in its name, unless it is the current directory, in which case relative path names are passed to the OpenWatcom compiler.

Run-time Support

 • Compiled code is able to access a run-time library of support functions. The run-time library contains a number of utilities that aid in the execution of compiled code, as well as a library of mathematical functions that is shared with the evalhf command. In addition, some hardware-integer versions of several Maple commands that operate on integers are provided, as are limited versions of print and type.
 • The built-in mathematical functions available in the run-time library are as follows.

 • In addition, limited versions of the following Maple procedures are provided in the run-time library.

 • The following types are recognized by the type run-time function.

 • Modular arithmetic is supported: addition, multiplication, division (by a unit), powers, and negation will not overflow. The mod environment variable is implemented in the runtime.
 • The run-time library contains only limited versions of the listed builtin procedures. These procedures are capable only of handling hardware datatypes (integer, floating-point or complex floating-point). Integer-valued run-time procedures can neither return nor accept infinity or -infinity. Integer computations that overflow the size of the machine word cause an exception indicating the overflow to be raised. This applies also to integer arithmetic. The run-time type procedure recognizes no types not listed above, and neither structured types nor user-defined types are supported.
 • When a compiled procedure returns a complex result equal to $0.+0.I$, the result is converted to $0.$ in all cases. However, an imaginary part equal to $-0.$ is preserved.
 • In the Compiler run-time environment, non-default numeric event handling settings are not guaranteed to be respected.

 • As of Maple 18, the run-time library for compiled procedures is thread-safe.  This means that a thread-safe Maple procedure will result in one that remains thread-safe once compiled.
 • In order to take advantage of this in multithreaded code, you must include option threadsafe on procedures to be compiled.  This will ensure that the kernel recognizes the compiled procedure as one that is thread-safe and allow it to be called from multiple threads.
 • Note, however, that the compiler itself is not thread-safe. This means that procedures with option autocompile cannot be used in multithreaded code, until after they have been compiled (by calling them at least once).

Managing Large Procedures

 • Compilation of procedures involving very large expressions may require significant overhead, both to generate the C code and to run the C compiler on the generated code. Consider using the optimize option if you encounter any of the following problems.
 The Compiler is taking a very long time to generate C code.
 The external C compiler takes a very long time to compile the generated C code.
 The external C compiler runs out of memory when compiling the generated C code.

Array Support

 • Arrays can only appear as parameters of a procedure that is to be compiled. Although there is some support for global and lexically scoped variables, these are limited to scalar-valued quantities, and sufficient type information must be present to determine their effective type during the execution of the compiled procedure.
 • Multidimensional Arrays (such as Matrices) are supported. Because it is normally inconvenient to provide dimensional information for Arrays in a parameter type description, the CodeGeneration package will determine the number of dimensions of an Array parameter from the way in which it is accessed in the body of the procedure being compiled. If an Array parameter is never accessed in the body of a procedure being compiled, and no dimensional information is provided in a parameter type description, then the Array parameter will be assumed to be one dimensional.

Support for Recursion

 • Recursive procedures are supported by the Compiler. For the compiled code to operate recursively, recursive calls must be made via the procname implicit parameter rather than the name the procedure is assigned to. (Strictly speaking, a recursive procedure in Maple must be coded this way to be guaranteed of operating recursively whether or not it is to be compiled.)
 • During compilation, tail-recursive calls are detected and transformed into iteration. This means that tail-recursive procedures compile to very fast code.

Operation in Secure Mode

 • Because the Compiler creates temporary files and executes external processes, it is not available when Maple is started in secure mode, using the -z command line option, or when enhanced security has been enabled via the Options menu in the GUI.

The autocompile Procedure Option

 • A Maple procedure may be given the option autocompile. This causes the kernel to attempt to compile the procedure to native code on first execution. If the compilation succeeds, the name by which the procedure was invoked is re-assigned the value of the compiled procedure. In effect, the original procedure is replaced with the procedure that runs the compiled code.

Examples

 > p := proc( x :: float ) :: float; 2.3 * x end proc:
 > $\mathrm{cp}≔\mathrm{Compiler}:-\mathrm{Compile}\left(p\right):$
 > $\mathrm{cp}\left(1.1\right)$
 ${2.52999999999999980}$ (1)
 > p := proc( n :: posint, x :: float ) :: float;   local    i :: integer, s :: float;   s := 0.0;   for i from 1 to n do     s := s + x / i   end do;   s end proc:
 > $\mathrm{cp}≔\mathrm{Compiler}:-\mathrm{Compile}\left(p\right)$
 ${\mathrm{cp}}{≔}{\mathbf{proc}}\left({}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{option}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{call_external}}{,}{\mathrm{define_external}}{}\left({\mathrm{_m0ac6a896a5a9f41c9517543d1c6e9edb}}{,}{\mathrm{MAPLE}}{,}{\mathrm{IN_MEM}}{=}{139671497539712}\right){;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{call_external}}{}\left({0}{,}{139671497539712}{,}{\mathrm{true}}{,}{\mathrm{false}}{,}{\mathrm{args}}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end proc}}$ (2)
 > $\mathrm{time}\left(p\left(100000,2.3\right)\right)$
 ${0.243}$ (3)
 > $\mathrm{time}\left(\mathrm{evalhf}\left(p\left(100000,2.3\right)\right)\right)$
 ${0.009}$ (4)
 > $\mathrm{time}\left(\mathrm{cp}\left(100000,2.3\right)\right)$
 ${0.}$ (5)
 > p := proc( n :: posint, a :: Array( datatype = float[ 8 ] ) )   local    i::integer, s::float;   s := 0.0;   for i from 1 to n do     s := s + a[ i ]   end do;   s end proc:
 > $\mathrm{cp}≔\mathrm{Compiler}:-\mathrm{Compile}\left(p\right):$
 > $a≔\mathrm{Array}\left(1..5,u↦\mathrm{evalf}\left(\frac{1}{u}\right),\mathrm{datatype}=\mathrm{float}\left[8\right]\right)$
 ${a}{≔}\left[\begin{array}{ccccc}{1.}& {0.500000000000000}& {0.333333333300000}& {0.250000000000000}& {0.200000000000000}\end{array}\right]$ (6)
 > $p\left(5,a\right)$
 ${2.28333333330000}$ (7)
 > $\mathrm{cp}\left(5,a\right)$
 ${2.28333333329999988}$ (8)

Compiled procedures retain array bounds checking:

 > $\mathrm{cp}\left(7,a\right)$
 > $N≔100000:$$a≔\mathrm{Array}\left(1..N,u↦\mathrm{evalf}\left(\frac{1}{u}\right),\mathrm{datatype}=\mathrm{float}\left[8\right]\right):$
 > $\mathrm{time}\left(p\left(N,a\right)\right);$$\mathrm{time}\left(\mathrm{evalhf}\left(p\left(N,a\right)\right)\right);$$\mathrm{time}\left(\mathrm{cp}\left(N,a\right)\right)$
 ${0.067}$
 ${0.008}$
 ${0.}$ (9)

Recursive procedures can be handled, as in this implementation of the Towers of Hanoi problem.

 > HanoiMove := proc( a :: Array( 1 .. 4, datatype = integer[ 4 ] ),     numdisks :: posint,     frompeg :: posint, topeg :: posint )   local    tmp;   if numdisks = 1 then      a[ frompeg ], a[ topeg ] := a[ frompeg ] - 1, 1 + a[ topeg ]   else     tmp := 6 - ( frompeg + topeg );     procname( a, numdisks - 1, frompeg, tmp );     procname( a, 1, frompeg, topeg );     procname( a, numdisks - 1, tmp, topeg )   end if end proc:
 > $\mathrm{cHanoiMove}≔\mathrm{Compiler}:-\mathrm{Compile}\left(\mathrm{HanoiMove}\right):$
 > $\mathrm{st}≔\mathrm{time}\left(\right):$$\mathrm{HanoiMove}\left(\mathrm{Array}\left(1..4,'\mathrm{datatype}'='\mathrm{integer}'\left[4\right]\right),17,1,3\right):$$\mathrm{time}\left(\right)-\mathrm{st}$
 ${10.498}$ (10)
 > $\mathrm{st}≔\mathrm{time}\left(\right):$$\mathrm{cHanoiMove}\left(\mathrm{Array}\left(1..4,'\mathrm{datatype}'='\mathrm{integer}'\left[4\right]\right),17,1,3\right):$$\mathrm{time}\left(\right)-\mathrm{st}$
 ${0.009}$ (11)

Complex floating-point data are supported.

 > p := proc( a :: Array( datatype = complex[8] ), n :: posint ) :: complex(extended_numeric);     local    s, i;     s := 0.0 + 0.0*I;     for i from 1 to n do         s := s + arcsin( a[ i ] )     end do;     s end proc:
 > $\mathrm{cp}≔\mathrm{Compiler}:-\mathrm{Compile}\left(p\right):$
 > $a≔\mathrm{Array}\left(\left[I,-I,1,-1\right],':-\mathrm{datatype}'=':-\mathrm{complex}'\left[8\right]\right):$
 > $\left[p,\mathrm{cp}\right]\left(a,4\right)$
 $\left[{0.}{+}{0.}{}{I}{,}{0.}{+}{0.}{}{I}\right]$ (12)

Use the trace option to effect tracing of compiled procedures.

 > p := proc( n :: integer ) :: integer;   if n < 1 then     0   else     1 + procname( n - 1 )   end if end proc:
 > $\mathrm{cp}≔\mathrm{Compiler}:-\mathrm{Compile}\left(p,'\mathrm{trace}'\right):$
 > $\mathrm{cp}\left(5\right)$
 {--> enter p, args = 5
 ${5}$ (13)

The autocompile option can be used to cause procedures to attempt compilation on their first invocation.

 > Sort := proc( a :: Array( datatype = integer[ 4 ] ), n :: posint )   local    i, j, m, tmp;   option autocompile;   for i from 1 to n do     m := i;     for j from 1 + i to n do       if a[ j ] < a[ m ] then         m := j       end if     end do;     a[ m ], a[ i ] := a[ i ], a[ m ]   end do end proc:
 > $a≔\mathrm{Array}\left(1..10,i↦\mathrm{mods}\left(\mathrm{RandomTools}:-\mathrm{MersenneTwister}:-\mathrm{GenerateInteger32}\left(\right),2147483647\right),\mathrm{datatype}=\mathrm{integer}\left[4\right]\right)$
 ${a}{≔}\left[\begin{array}{cccccccccc}{-795755683}& {581869301}& {-404620561}& {-708632710}& {545404203}& {-133711904}& {-372047866}& {949333984}& {568478650}& {-823916245}\end{array}\right]$ (14)
 > $\mathrm{Sort}\left(a,10\right)$
 ${949333984}$ (15)
 > $a$
 $\left[\begin{array}{cccccccccc}{-823916245}& {-795755683}& {-708632710}& {-404620561}& {-372047866}& {-133711904}& {545404203}& {568478650}& {581869301}& {949333984}\end{array}\right]$ (16)

The following example demonstrates support for modular arithmetic and lexically scoped parameters.

 > p := proc( m :: posint )   proc( a :: integer, b :: integer ) ( a + b ) mod m end end proc:
 > $\mathrm{add111}≔\mathrm{Compiler}:-\mathrm{Compile}\left(p\left(111\right)\right):$
 > $\mathrm{add111}\left(234,456\right)$
 ${24}$ (17)
 > $234+456\phantom{\rule[-0.0ex]{0.3em}{0.0ex}}\mathbf{mod}\phantom{\rule[-0.0ex]{0.3em}{0.0ex}}111$
 ${24}$ (18)
 > $\mathrm{add111}\left(199,14\right)$
 ${102}$ (19)
 > $\mathrm{mod}≔\mathrm{mods}:$
 > $\mathrm{add111}\left(199,14\right)$
 ${-9}$ (20)
 > $\mathrm{mods}\left(199+14,111\right)$
 ${-9}$ (21)

Overloaded procedures can be used to implement algorithms that run compiled code for suitable inputs, but fall back to interpreted Maple code for other inputs.

 > F := overload([   proc( s :: float, n :: integer[ 4 ] ) :: float;     local  i;     option  overload, autocompile;     userinfo( 1, "F", "using native code" );     sin( s / n ) # for small n, use native code   end proc,   proc( s :: float, n :: integer ) :: float;     local  i;     option overload;     userinfo( 1, F, "using Maple software code" );     sin( s / n ) # for large n use Maple arithmetic   end proc,   proc( s :: algebraic, n :: algebraic )     'F( args )'  # return a symbolic result   end proc, NULL]):
 > $\mathrm{infolevel}\left[F\right]≔2:$
 > $F\left(\mathrm{evalf}\left(\mathrm{\pi }\right),100\right)$
 F:   "using native code"
 ${0.0314107590822283386}$ (22)
 > $F\left(\mathrm{evalf}\left(\mathrm{\pi }\right),{2}^{31}\right)$
 F:   "using native code"
 ${1.46291807945817713}{×}{{10}}^{{-9}}$ (23)
 > $F\left(\mathrm{\pi },100\right)$
 ${F}{}\left({\mathrm{\pi }}{,}{100}\right)$ (24)

Another example.

 > $\mathrm{mod}≔\mathrm{modp}:$
 > F := overload([   proc( a :: Array( datatype = integer[ 4 ] ), n :: posint, m :: integer[ 4 ] ) :: integer[ 4 ];       option  overload, autocompile;       local  i, s;       userinfo( 1, F, "using small modulus implementation" );       s := 0;       for i from 1 to n do         s := ( s + a[ i ] ) mod m       end do;       s   end proc,   proc( a :: Array, n :: posint, m :: integer ) :: integer;       option  overload;       local  i, s;       userinfo( 1, F, "using large modulus implementation" );       s := 0;       for i from 1 to n do         s := ( s + a[ i ] ) mod m       end do;       s   end proc, NULL]):
 > $\mathrm{infolevel}\left[F\right]≔2:$
 > $a≔\mathrm{Array}\left(\left[\mathrm{seq}\right]\left({2}^{i}\phantom{\rule[-0.0ex]{0.3em}{0.0ex}}\mathbf{mod}\phantom{\rule[-0.0ex]{0.3em}{0.0ex}}101,i=0..50\right),\mathrm{datatype}=\mathrm{integer}\left[4\right]\right):$
 > $F\left(a,51,101\right)$
 F:   "using small modulus implementation"
 ${98}$ (25)
 > $a≔\mathrm{Array}\left(\left[\mathrm{seq}\right]\left(\left({2}^{i}\phantom{\rule[-0.0ex]{0.3em}{0.0ex}}\mathbf{mod}\phantom{\rule[-0.0ex]{0.3em}{0.0ex}}{2}^{40}\right)+1,i=0..50\right)\right):$
 > $F\left(a,51,{2}^{40}+1\right)$
 F:   "using large modulus implementation"
 ${49}$ (26)

If Maple is started with the -z' option, the Compiler is not available for use, and an exception like the following is raised.  (The exception will not be raised if you execute this example in a session not started with the -z' option.)

 > $\mathrm{Compiler}:-\mathrm{Compile}\left(p\left(111\right)\right)$