Macaulay2 » Documentation
Packages » ForeignFunctions » just-in-time compilation example
next | previous | forward | backward | up | index | toc

just-in-time compilation example -- using foreign function calls for computing Fibonacci numbers using JIT

Macaulay2's language is interpreted, which means that in general programs will run more slowly than programs written in a compiled language like C. If the speed of a compiled language is desirable, then one option is just-in-time compilation (JIT), where we compile some code at runtime.

As an example, let's suppose that we would like to compute Fibonacci numbers using the recursive definition, i.e., for all nonnegative $n\in\ZZ$, $F_n = n$ if $n < 2$ and $F_n = F_{n-2} + F_{n-1}$ otherwise. As an algorithm, this is horribly inefficient and has exponential running time, but it will be useful to illustrate our point.

We will write two functions. One will be a straight Macaulay2 implementation:

i2 : fibonacci1 = n -> if n < 2 then n else fibonacci1(n - 1) + fibonacci1(n - 2);

The next will be a function that creates a C source file, compiles it into a shared library using GCC, opens that library using openSharedLibrary, calls foreignFunction to create a foreign function, calls that foreign function, and then converts the output back into a Macaulay2 integer using value(ForeignObject).

i3 : fibonacci2 = n -> (
         dir := temporaryFileName();
         makeDirectory dir;
         dir | "/libfib.c" << ///int fibonacci2(int n)
     {
         if (n < 2)
             return n;
         else
             return fibonacci2(n - 1) + fibonacci2(n - 2);
     }
     /// << close;
         run("gcc -c -fPIC " | dir | "/libfib.c -o " | dir | "/libfib.o");
         run("gcc -shared " | dir | "/libfib.o -o " | dir | "/libfib.so");
         lib := openSharedLibrary("libfib", FileName => dir | "/libfib.so");
         f = foreignFunction(lib, "fibonacci2", int, int);
         value f n);

Now let's run both functions to compare the results.

i4 : peek elapsedTiming fibonacci1 35

o4 = Time{10.0202, 9227465}
i5 : peek elapsedTiming fibonacci2 35

o5 = Time{.0958821, 9227465}

As we can see, despite the additional overhead of creating a shared library and making a foreign function call, the function that used JIT was significantly faster.


The source of this document is in /build/reproducible-path/macaulay2-1.25.05+ds/M2/Macaulay2/packages/ForeignFunctions.m2:2378:0.