Does anyone have experience using C extensions to Mathematica (LibraryLink or MathLink -- currently I'm using LibraryLink) with remote parallel kernels?
In short: How can I transparently use a LibraryLink-defined function (see CreateLibrary and LibraryFunctionLoad) in both parallel and non-parallel evaluations when the subkernels run on a remote machine?
I am looking for some setup steps that will allow me to have a libraryFun
function (written in C) that can be called either normally as libraryFun[args]
, or in parallel (both of Parallelize@Table[libraryFun[arg], {arg, 0, 100}]
and the same with ParallelTable[]
) when the subkernels run on a remote machine.
Running the main kernel remotely too might be better if I weren't having trouble with that as well.
Update
In the meantime I made some progress. I'll describe it here.
First, ParallelEvaluate
will evaluate an expression in all parallel kernels. If the source files for the C extension are copied to the remote machine, we can compile them there like this:
ParallelNeeds["CCompilerDriver`"]
k1 = First@Kernels[]
ParallelEvaluate[SetDirectory["/path/to/source/files"]]
ParallelEvaluate[CreateLibrary["sourefile", "myLibrary"]]
This needs to be done one time only. I assume that the library has been already compiled on the main kernel machine.
After this, in all subsequent sessions we can use FindLibrary
on both the main and the remote machines to load the library.
LibraryFunctionLoad[myFun = FindLibrary["myLibrary"], "fun", ...]
ParallelEvaluate[myFun = LibraryFunctionLoad[FindLibrary["myLibrary"], "fun", ...]]
And here comes the trouble. Because of different paths, myFun
will have different values in the main and in the parallel kernels.
So the question is: How can ensure that the value of myFun
will not accidentally get synchronized between the main and the parallel kernels?
I'll show in an isolated examples how this might accidentally happen:
In[1]:= LaunchKernels[2]
Out[1]= {KernelObject[1, "local"], KernelObject[2, "local"]}
Set value of x
in main kernel:
In[2]:= x = 1
Out[2]= 1
Note that it gets the same value in remote kernels too:
In[3]:= ParallelEvaluate[x]
Out[3]= {1, 1}
Set a different value for x
in the parallel kernels and verify that they keep it:
In[4]:= ParallelEvaluate[x = 2]
Out[4]= {2, 2}
In[5]:= {x, ParallelEvaluate[x]}
Out[5]= {1, {2, 2}}
Now "innocently" use Parallelize
on something containing x
:
In[6]:= Parallelize[Table[x, {10}]]
Out[6]= {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
And see how the value of x
got re-synced between the main and subkernels.
In[7]:= {x, ParallelEvaluate[x]}
Out[7]= {1, {1, 1}}
The new question is: How can I prevent a certain symbol from ever auto-syncing between the main and the subkernels?