Save a C compiled function without losing the blessing of C compiler



Consider the following example:

generatef[opt_: "MVM"] := 
 Compile[{}, Module[{j = 0}, Do[j++, {i, 10^8}]; j], CompilationTarget -> opt]

f1 = generatef[];
f2 = generatef["C"];

f1[] // AbsoluteTiming
(* {1.262879, 100000000} *)
f2[] // AbsoluteTiming
(* {0.495352, 100000000} *)

DumpSave["", f2];



f2[] // AbsoluteTiming
(* {1.295921, 100000000} *)

Tested in v9.0.1, Win8.1 64bit, TDM-GCC 4.9.2, "SystemCompileOptions"->"-Ofast".

Apparently f2 is no longer compiled in C after the save & load, which is undesired. Any solution?


Posted 2015-05-09T08:28:12.790

Reputation: 44 878

1C compiled is not Mathematica code. There's a function CreateLibrary or something like this to create a dll from mm code. Unfortunately you cannot compile many functions in a single dll. – faysou – 2015-05-09T08:58:46.420

1@faysou happily MMA creates a library for us when compiling which we can reload! – dr.blochwave – 2015-05-09T09:33:35.900

1Yes, CreateLibrary is practical to know where to export the library. – faysou – 2015-05-09T10:02:46.860



Yes, there is! Mathematica creates a LibraryFunction when compiling to C, but puts it in a temporary directory. If you can recover the library, you can load it as often as you like!

First let's define the function as in the question:

generatef[opt_] := 
 Compile[{}, Module[{j = 0}, Do[j++, {i, 10^8}]; j], CompilationTarget -> opt];
f2 = generatef["C"];

Now take the expression f2, and replace the heads of CompiledFunction and LibraryFunction with List. The last bit tells us all the information we need to load function again with LibraryFunctionLoad:

f2List = (f2 /. {CompiledFunction -> List, LibraryFunction -> List})

(* {{10, 10.1, 5468}, {}, {{2, 0, 2}}, {{0, {2, 0, 0}}, 
    {100000000, {2, 0, 1}}, {1, {2, 0, 4}}}, {0, 8, 0, 0, 0}, 
    {{6, 0, 2}, {6, 1, 3}, {6, 0, 5}, {3, 4}, {6, 2, 6}, 
    {12, 6, 4, 7}, {6, 7, 2}, {4, 5, 3, -3}, {1}}, 
    Function[{}, Module[{j = 0}, Do[j++, {i, 100000000}]; j]], Evaluate, 
     "compiledFunction0", {}, Integer}} *)

So now all we need to do is load up the library and run it, with next-to-no-difference in the timing.

f2[] // AbsoluteTiming
(* {0.589635 seconds, 100000000} *)

    mynewfunction = LibraryFunctionLoad @@ Last@f2List;
(* {0.584785 seconds, 100000000} *)

Then you need to copy the file out of the temporary directory to a directory of your choice in order to preserve the DLL. Here I've chosen the notebook directory, but you could also put it in one of the directories in $LibraryPath instead and then only have to specify the filename rather than a path.

CopyFile[f2List[[-1, 1]], ToString@NotebookDirectory[] <> "compiledFunction0.dll"]
myf2 = LibraryFunctionLoad[
  ToString@NotebookDirectory[] <> "compiledFunction0.dll", 
      "compiledFunction0", {}, {Integer}];

In this example, the {}, {Integer} refers to the input and output variables as per the documentation, which will obviously be different for different functions.

The final point to note is that trying to overwrite/delete the copied DLL can throw an error if you haven't unloaded the library function via LibraryFunctionUnload first.

I've not tested it on OSX or Linux, but apart from the file extensions (.dll, .dylib, .so) I presume it should all be the same.


Posted 2015-05-09T08:28:12.790

Reputation: 8 483

Exemplary answer! +1 – Sjoerd C. de Vries – 2015-05-09T09:35:35.360

1+1. Perhaps also note that there is a directory that LibraryFunctionLoad automatically searches, so that one can specify a short name of a library, rather than a full path. – Jacob Akkerboom – 2015-05-09T10:04:53.573

@JacobAkkerboom yes good point - one could copy the file to that instead. – dr.blochwave – 2015-05-09T10:06:18.290

+1. BTW it's a little surprising to me that the default Directory[] isn't included in $LibraryPath. – xzczd – 2015-05-09T10:32:44.327


Actually there's no need to use LibraryFunctionLoad, the only necessary part is to save the DLL to a non-temporary directory. Then one can save & load f2 and recover the C compiled function with something like f2new = ReplacePart[f2, {-1,1} -> (* The file path of the DLL *)]. In this way the RuntimeAttributes of the compiled function will also be recovered so your answer will be fully apply to this question :D

– xzczd – 2015-05-09T10:59:33.157

@xzczd it is actually necessary to use LibraryFunctionLoad; the LibraryFunction in the CompiledFunction will not work unless you do. There is no error but the library is not initialized and will not be used otherwise (only the bytecode), because LibraryFunction objects are stateful (which is not surprising when one thinks about it). I wrote about it on MathGroup back in 2011.

– Oleksandr R. – 2015-05-20T07:32:48.513

@OleksandrR. Er…are you sure? I just tested the code in this answer, and funcNew apparently makes use of the DLL even if I restart Mathematica:

– xzczd – 2015-05-20T09:19:20.633

2@xzczd not in version 8, which was the current version when I wrote to the MathGroup. I suppose someone might have seen my message or noticed the behavior themselves, because it seems to have been fixed in version 9. – Oleksandr R. – 2015-05-20T19:09:48.477