4

有没有人有使用远程并行内核对 Mathematica 的 C 扩展(LibraryLink 或 MathLink——目前我正在使用 LibraryLink)的经验?

简而言之:当子内核在远程机器上运行时,如何在并行和非并行评估中透明地使用 LibraryLink 定义的函数(请参阅CreateLibraryLibraryFunctionLoad )?

我正在寻找一些设置步骤,这些步骤将允许我拥有一个函数(用 C 编写),当子内核在远程计算机上运行时,该libraryFun函数可以正常调用,也可以libraryFun[args]并行调用(两者都相同Parallelize@Table[libraryFun[arg], {arg, 0, 100}]) 。ParallelTable[]

如果我也没有遇到问题,那么远程运行主内核可能会更好。


更新

与此同时,我取得了一些进展。我会在这里描述它。

首先,ParallelEvaluate将评估所有并行内核中的表达式。如果 C 扩展的源文件被复制到远程机器,我们可以像这样在那里编译它们:

ParallelNeeds["CCompilerDriver`"]
k1 = First@Kernels[]
ParallelEvaluate[SetDirectory["/path/to/source/files"]]
ParallelEvaluate[CreateLibrary["sourefile", "myLibrary"]]

这只需要执行一次。我假设该库已经在主内核机器上编译。

在此之后,在所有后续会话中,我们可以FindLibrary在主计算机和远程计算机上使用来加载库。

LibraryFunctionLoad[myFun = FindLibrary["myLibrary"], "fun", ...]
ParallelEvaluate[myFun = LibraryFunctionLoad[FindLibrary["myLibrary"], "fun", ...]]

麻烦来了。 由于路径myFun不同,在主内核和并行内核中会有不同的值。

那么问题来了:如何保证的值myFun不会在主内核和并行内核之间意外同步?

我将在一个孤立的例子中展示这可能是如何意外发生的:

In[1]:= LaunchKernels[2]
Out[1]= {KernelObject[1, "local"], KernelObject[2, "local"]}

x在主内核中设置值:

In[2]:= x = 1
Out[2]= 1

请注意,它在远程内核中也获得相同的值:

In[3]:= ParallelEvaluate[x]
Out[3]= {1, 1}

在并行内核中设置不同的值x并验证它们是否保留它:

In[4]:= ParallelEvaluate[x = 2]
Out[4]= {2, 2}

In[5]:= {x, ParallelEvaluate[x]}
Out[5]= {1, {2, 2}}

现在“无辜地”用于Parallelize包含以下内容的内容x

In[6]:= Parallelize[Table[x, {10}]]
Out[6]= {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}

并查看 的值如何x在主内核和子内核之间重新同步。

In[7]:= {x, ParallelEvaluate[x]}
Out[7]= {1, {1, 1}}

新问题是:如何防止某个符号在主内核和子内核之间自动同步?

4

2 回答 2

5

我希望这回答了你的问题:

目前,我假设主内核和并行内核在同一个架构上,对我来说是 Windows 7。首先你编译一个函数;您可以使用 C 编译器在 Mathematica 之外执行此操作,或直接在 Mathematica执行此操作:

f = Compile[{x}, x^2, CompilationTarget -> "C"]

您可以通过查看生成InputFormfdll 所在的位置来检查:

f // InputForm

给出类似的东西:

CompiledFunction[{8, 8., 5468}, {_Real}, {{3, 0, 0}, {3, 0, 1}}, {}, {0, 0, 2, 0, 0}, {{40, 
56, 3, 0, 0, 3, 0, 1}, {1}}, Function[{x}, x^2], Evaluate,
LibraryFunction["C:\\Users\\arnoudb\\AppData\\Roaming\\Mathematica\\ApplicationData\\CCompilerDriver\\BuildFolder\\arnoudb2win-5184\\compiledFunction0.dll", 
"compiledFunction0", {{Real, 0, "Constant"}}, Real]]

您可以将此文件复制到并行(可能是远程)内核可以找到它的位置,例如:

CopyFile["C:\\Users\\arnoudb\\AppData\\Roaming\\Mathematica\\ApplicationData\\CCompilerDriver\\BuildFolder\\arnoudb2win-3316\\compiledFunction1.dll",
"c:\\users\\arnoudb\\compiledFunction1.dll"]

然后你可以像这样在所有并行内核中加载库:

ParallelEvaluate[
 ff = LibraryFunctionLoad["C:\\users\\arnoudb\\compiledFunction1.dll",
 "compiledFunction1", {Real}, Real]
]

并检查这是否有效:

ParallelEvaluate[ff[3.4]]

这对我来说是回报{11.56,11.56}

如果并行内核在不同的架构上,您将需要为该架构编译 C 代码(或Compile[..., CompilationTarget->"C"]在并行内核上评估)。

于 2012-01-05T23:18:54.163 回答
1

我似乎在上面的更新中找到了我的问题的解决方案。它似乎有效,但我还不能确认它并不脆弱。

解决方案是将我们不希望同步的符号放入单独的上下文中。在我的示例中使用c`x代替x可防止同步xwhen it is used inside的值Parallelize。然后我们可以将此上下文添加到$ContextPath以使符号易于访问。

最方便的方法可能是将所有定义放在一个包中,该包使用LibraryFunctionLoad[FindLibrary[...], ...]. 为此,必须首先在本地和远程计算机上手动编译库,但是,主内核和子内核的包代码可以完全相同。


$Context如果有人可以确认不在其中的变量保证不会自动同步,我仍然很感兴趣。

更新已在此处确认。

于 2012-01-06T14:45:35.407 回答