The first release (version 0.1.0) of the package is available now. You can access the releases here, and the guide here.
MathCompile is a package I am working on as a different approach to code compilation than Compile
and FunctionCompile
. MathCompile is focused on improving the availability of functional programming and covering the functionalities provided by the built-in compiler. It also brings performance improvements due to its architecture.
MathCompile is still at its early stage where a lot of features are to be added and bugs are expected. Any feedback and comments are welcome.
The package can be accessed here https://github.com/njpipeorgan/MathCompile with Documentations.
Installation
To install the package:
- clone the repository to local machine;
- open Mathematica and and choose File | Install...;
- Select Application for the type of item to install, and select From Directory... for the source, then open the directory you cloned named MathCompile.
A C++ compiler supporting C++17 standard is required; see this wiki page for more information. If you are working on a recent Linux or macOS platform, a compatible version of GCC or Clang may have been installed. On Windows platforms, you may follow §Windows on the page to install and configure the compiler (Mingw-w64 is recommeded).
In a nutshell
After installing the package, load it by executing
<<MathCompile`
Here is a Wolfram Language function that calculates the product of the elements in a list of integers:
f = Function[{Typed[x, {Integer, 1}]}, Times @@ x];
Compile this function using CompileToBinary
.
cf = CompileToBinary[f];
The compiled function can be called just like a normal function.
cf[{1,2,3,4}] (* gives 24 *)
How is its performace
Functions compiled by MathCompile are intended to perform as if they were written in native C++. They typically have equal or higher performance than those by the built-in compiler. Here I show the example of accelerating Mandelbrot set computation from version 12 code compilation introduction page.
The implementation of the function is the same.
f0=Function[{Typed[p0, "ComplexReal64"]},
Module[{iters = 1, max = 1000, p = p0},
While[iters < max && Abs[p] < 2, p = p^2 + p0; iters++]; iters
]
]
It is compiled by both the built-in compiler and MathCompile.
f1 = FunctionCompile[f0];
f2 = CompileToBinary[f0];
Measure how long it takes to apply each of these functions to a grid.
AbsoluteTiming[Table[f0[x + I y], {y, -1.5, 1.5, .025}, {x, -2., 1., .025}];]
AbsoluteTiming[Table[f1[x + I y], {y, -1.5, 1.5, .025}, {x, -2., 1., .025}];]
AbsoluteTiming[Table[f2[x + I y], {y, -1.5, 1.5, .025}, {x, -2., 1., .025}];]
The performance are shown in the table below. Measurements are made on Mathematica 12.0, Ubuntu 18.04, Intel Coffe Lake, GCC 7.4.
Time (sec) Speedup
Uncompiled 2.695 /
Built-in 0.060 45x
MathCompile 0.016 168x
MathCompile achieved a factor of ~170 speedup compared to the uncompiled version and a factor of ~4 speedup compared to the built-in compiler.
Which functions are compilable
Currently, over 180 functions are compilable, see this wiki page Compilable Functions for the full list. MathCompile covers most of the functions compilable by the built-in compiler and add a few more, and many more functions will be supported with the development of the package.
How does it work
MathCompile compiles a Wolfram Language function similar to the native Compile function with CompilationTarget as "C". During the compilation process, the function is first translated into C++ code; then the C++ compiler is called to compile the C++ code into a dynamic library that can be loaded into the kernel as a library function.
MathCompile package comes with a header-only library in C++ that implements all supported functions. Therefore, the generated C++ code can be called by external programs without any dependency on the Wolfram kernel or the runtime library.
You may wonderbut why C++? A short answer is that C++ is expressive enough so that the C++ compiler is able to do many things for developers (me in this case).
Consider the function f = #+2&
, which can be called later by a variety of arguments: we expect f[2]
to give 4
and f[3.14]
to give 5.14
, or f[1, 2, 3]
to give 3
and f[{1, 2, 3}]
to give {3, 4, 5}
. If we want the function to compile in C, we have to trace every call to the function and implement that version of the function ourselves when necessary, like these:
int f_i(int x) { return x + 2; }
double f_d(double x) { return x + 2; }
double f_ddd(double x, double y, double z) { return x + 2; }
......
But in C++, this function is naturally expressed as a generic lambda
auto f = [](auto x, auto...) { return x + 2; };
where C++ compiler will instantiate the function for us automatically whenever it is called.
More information
The Guide on MathCompile wiki introduces how to use the package. It also shows how to generate the intermediate C++ code which can be used with external programs.
Performance Tests page shows the performance of MathCompile on some interesting problems.
Check out What to expect for what is going to be implemented potentially in the near future.