## Exporting a compiled function for later use

9

6

I wish to export compiled functions for later use. This means that I do not want the compiled function to be recompiled every time I reload it. It also means that if my compiled function was for example Listable, then the function should still be Listable when I reload it.

Below I will present some attempts to save and reload the following function.

function =
Compile[{{x, _Real}}, Sum[Sin[Cos[-x^2*i]], {i, 1, 10^6}],
CompilationTarget -> "C", RuntimeAttributes -> {Listable},
Parallelization -> True];


## Attempt using .wdx file

SetDirectory[NotebookDirectory[]]
Export["compiled.wdx", function];
function[Range[10]] // AbsoluteTiming//First
(* 0.191019 seconds *)


Now after restarting the kernel, we get

SetDirectory[NotebookDirectory[]];
function2 = Import["compiled.wdx"];
function2[Range[10]] // AbsoluteTiming//First
(* 0.310031 seconds *)


Of course this difference in timing is unacceptable - it seems the function is now recompiled every time it is called.

## Attempt using LibraryGenerate

Our example function function accepts lists, because we used RuntimeAttributes -> {Listable}

function[{1, 0}]
(* {-0.2982909888387981, 841470.9848238959} *)


We now try to save this function as follows:

Needs["CCodeGenerator"]
LibraryGenerate[function, "Myfun"]


And load it again:

function3 = LibraryFunctionLoad["Myfun", "Myfun", {Real}, Real]


There are two problems when using this method of exporting the function.

1. It seems that even if you do not compile to C code when you export as mentioned above it compiles to C code because if the function is not compiled to C code after loading it, it gets as fast as a function compiled to C code.

2. The more important issue is that now the function won't accept a list as its argument:

function3[{1, 0}]


LibraryFunction::cfsa: Argument {1,1} at position 1 should be a machine-size real number. >>

At a guess, it's because you're exporting the Mathematica code to compile the function, not the compiled function itself. So the extra time comes from recompilation. – dr.blochwave – 2014-08-25T14:45:28.327

The C code generated will be in a directory as .dll file, you need to find that, and perhaps you can load it with LibraryLink. Or you can put the compilation command as an initialization cell to run when you open the notebook. – dr.blochwave – 2014-08-25T14:50:11.770

By directory, it'll be a temp/working directory. – dr.blochwave – 2014-08-25T19:38:44.283

Di you manage you export the compiled function? I read the documentation but I didn't succeed. – MOON – 2014-08-26T16:22:20.070

Instead of using the Listable attribute, try {x, _Real, 1} and submit your variable as a tensor. – dr.blochwave – 2014-08-26T17:32:45.283

Currently you're passing a tensor to C code that expects a single value. – dr.blochwave – 2014-08-26T17:33:36.753

As for Point 1, yes, LibraryGenerate creates C code, so it will be compiled... – dr.blochwave – 2014-08-26T17:34:50.053

1@blochwave You do not like the blockquote environment (yellow background thingy) for output I see. I suppose the question is closer to the original after your edit. – Jacob Akkerboom – 2015-05-12T16:16:31.000

@JacobAkkerboom sorry, just personal preference regarding the quotes! Thanks for tidying up the question for reopening though - good job! – dr.blochwave – 2015-05-12T16:35:57.647

11

This is inspired by my recent answer to Save a C compiled function without losing the blessing of C compiler.

The key point here is to retain the {Listable} attribute of the compiled function. This is difficult as this attribute is stored in the Mathematica definition of the function, rather than the compiled C code, so neither Export/DumpSave or LibraryGenerate can store both pieces of information.

Instead we have to save the Mathematica definition of the function, and also locate the compiled library (e.g. *.dll) so that we can reload that rather than recompiling.

First, let's define an example listable function.

func = Compile[{{x, _Integer}},
Module[{j = 0},
Do[j++, {i, 10^8}];
x*j],
CompilationTarget -> "C",
RuntimeAttributes -> {Listable}]

func[{1, 2, 3, 4, 5}] // AbsoluteTiming
(* {1.44 seconds, {1*10^8, 2*10^8, 3*10^8, 4*10^8, 5*10^8}} *)


The next step is to extract the location of the library created by this compilation (which is what I did in the linked question) and copy it to a new location, such as the current notebook directory.

funcLoc = func[[-1, 1]]
newfuncLoc = ToString@NotebookDirectory[] <> FileNameTake[funcLoc, -1]
CopyFile[funcLoc, newfuncLoc, OverwriteTarget -> True]


Now let's save the definition of func and the location of the library, and reload it.

DumpSave["compile.mx", {func, newfuncLoc}];
Clear[func, newfuncLoc]
<< compile.mx

func[{1, 2, 3, 4, 5}] // AbsoluteTiming
(* 2.59 seconds, {1*10^8, 2*10^8, 3*10^8, 4*10^8, 5*10^8} *)


Herein lies the problem, as this is significantly slower. But remember, we also saved the location of the C library, so let's link that file back into a new function:

funcNew = ReplacePart[func, {-1, 1} -> newfuncLoc]
funcNew[{1, 2, 3, 4, 5}] // AbsoluteTiming
(* 1.40 seconds, {1*10^8, 2*10^8, 3*10^8, 4*10^8, 5*10^8} *)


There we are - the benefit of compilation to C is restored, and the {Listable}` attribute remains.