Getting Started
The Wolfram Compiler is a compiler for the Wolfram Language. It converts Wolfram Language programs into low-level machine hardware instructions. It makes use of the latest advances in compiler knowledge and technology.
Some of the advantages of the compiler include generating fast code, detecting programming errors, selecting advantageous code paths and algorithms at compilation time, allowing for novel optimizations and transformations of code and accessing low-level computer functionality.
A key and unique feature of the Wolfram Compiler is that it is tightly integrated with the Wolfram Language. It applies the highly productive workflow of the Wolfram Language to language compilation. The many powerful features of the Wolfram Language, such as visualization and graph analysis, are immediately available for developing compiler-based solutions. Code generated by the compiler embeds particularly naturally into the Wolfram System, allowing for many interesting solutions.
This documentation is designed to help people start to learn and use the Wolfram Compiler.
First Steps
The compiler is ready to be used immediately with no special setup steps.
A simple way to start is with a Wolfram Language function with type annotations:
This function can be used in the regular way with the Wolfram Language interpreter. The type annotations are ignored by the interpreter:
A compiled version of the code can be generated with FunctionCompile:
The compiled code executes when given arguments. It generates the same result but in a different way:
The speed advantage of the compiled code can be seen with larger input:
The compiled code runs significantly faster.
Compiler Input
The Wolfram Compiler is very focused on working with functions. Its tools such as FunctionCompile work neatly with Wolfram Language Function objects. However, it also works well with code written in other ways.
Here is a definition of a function:
It works in the normal fashion:
A call to this definition can be compiled using DownValuesFunction:
The compiled version works in exactly the same way:
A larger dataset can be used for a timing comparison:
The compiled code runs very significantly faster:
A declaration for the function can be made as in the following:
The declaration makes it easier to reuse the function in several places:
This returns True because everything is greater than zero:
This returns False because everything is not greater than zero:
Changing the test means that the elements in the argument vector are negated and now they are all greater than zero and the result is True:
Many Wolfram Language functions are supported in the Wolfram Compiler. However, not all functions are supported in all possible ways. One useful way to learn about support is with CompilerInformation. For example, the following returns information known to the compiler about Nest:
There is a single version of Nest that takes a function, an initial value and a counter.
This can be invoked as follows:
CompilerInformation also gives useful information on types:
The Wolfram Language notebook interface gives useful help, such as command completion for types in certain cases. For example, when Typed is entered, you may see suggestions such as the following:
Compilation Errors
If the compiler encounters a function that is not supported (or is not supported for the types it is being given) it will be unable to proceed, and there will be a failure. Frequently, errors are found on compilation rather than when the program is being executed. This can seem to be irksome, but is often useful, since finding and resolving errors early is often easier.
When an error is found, there is a message and usually an error popup:
The "Source" button of the message can be used to open a popup window that identifies the location of the error:
This error location is quite useful and works even if you have a chain of functions calling each other. More information is found in a more detailed section on compilation errors.
Running the Compiler
The first time the compiler is used, it has to be loaded and initialized. This can take some time.
When individual compilations are run, they may put up a progress monitor. This gives an indication how the compilation is proceeding:
Compiler Examples
Here are some examples of programming with the Wolfram Compiler. In addition, there are many examples throughout the documentation included detailed examples in a later section.
Mandelbrot Example
The following function computes points in a Mandelbrot set:
A compiled version can be generated:
The function can be invoked as follows:
The following generates a set of points and plots them:
What is interesting is that the uncompiled function also works. It is just slower:
It is quite useful to develop uncompiled functions and then to compile them to get extra speed.
Compile a Recursive Function
The following demonstrates how compiled functions can be made from nested inner functions. Note how the type inferencer only requires a type annotation on the input argument:
Use Compiled Functions in Compiled Functions
The following demonstrates how compiled functions can accept compiled functions as input. The following implements a simple root bisection computation:
Create a function whose root will be found:
Use Plotting Functions in Compiled Functions
As noted, the compiler does not compile every Wolfram Language function. However, it can make use of functions that live outside of compiled code in the Wolfram evaluator. This is done with KernelEvaluate. It is demonstrated in the following, which runs a bubble sort and plots the updates to show progress of the sorting. Of course, a bubble sort is a very inefficient algorithm; it is shown here because it is simple to code:
Run the sorting algorithm; a sequence of plots is generated:
Generate a Library
The compiler is currently quite slow to run, but its output can be saved from one session to another by creating a dynamic library. The following creates a dynamic library:
This library can be loaded in any further sessions without having to recompile:
This calls the code loaded from the library:
Generate Assembler
The compiler can generate output in a variety of native machine code formats. For example, it can generate different kinds of assembly code. A standalone file can be created using FunctionCompileExport, or the string representation can be created using FunctionCompileExportString.
The following is a trivial function that adds one to an integer:
Obtain the assembler code for the current platform; on this machine, "MacOSX-ARM4":
Generate LLVM Code
The compiler is built on top of a set of tools for compilers called LLVM. It can generate the internal code for LLVM.
The following is a trivial function to add one to an integer:
Obtain the LLVM code for the current platform; on this machine, "MacOSX-ARM64":