远程并行内核和 LibraryLink——如何让它们一起工作?

发布于 2024-12-24 19:21:35 字数 2558 浏览 1 评论 0原文

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

简而言之:如何透明地使用 LibraryLink 定义的函数(请参阅 CreateLibrary 和 LibraryFunctionLoad)?

我正在寻找一些设置步骤,使我能够拥有一个 libraryFun 函数(用 C 语言编写),该函数可以通常作为 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}}

现在“innocently”在包含 x 的内容上使用 Parallelize

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}}

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

很酷又爱笑 2024-12-31 19:21:35

我希望这能回答您的问题:

目前,我假设主内核和并行内核位于同一体系结构上,对我来说就是 Windows 7。首先编译一个函数;你可以在外面做这个
使用 C 编译器的 Mathematica ,或直接在 Mathematica 中:

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

您可以通过查看生成的 dll 所在的 fInputForm 进行检查:

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"]
核心)。

I hope this answers your question:

For the moment, I will assume the main kernel and the parallel kernels are on the same architecture, which for me is Windows 7. First you compile a function; you can do this outside
of Mathematica using a C compiler, or directly in Mathematica:

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

You can examine by looking at the InputForm of f where the generated dll is located:

f // InputForm

Gives something like:

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]]

You can copy this file to a location where the parallel (possibly remote) kernel can find it, for example:

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

Then you can load the library in all the parallel kernels like so:

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

And check if this works:

ParallelEvaluate[ff[3.4]]

Which returns {11.56,11.56} for me.

If the parallel kernel is on a different architecture, you will need to compile the C code
for that architecture (or evaluate the Compile[..., CompilationTarget->"C"] on the parallel
kernel).

太阳公公是暖光 2024-12-31 19:21:35

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

解决方案是将我们不想同步的符号放入单独的上下文中。在我的示例中使用 c`x 代替 x 可以防止在 Parallelize 内部使用 x 的值时同步该值>。然后我们可以将此上下文添加到 $ContextPath 以使符号易于访问。

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


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

更新已确认这里

I seem to have found a solution to my question in Update above. It seems to work but I cannot confirm yet that it is not fragile.

The solution is to put symbols that we don't want synchronized into a separate context. Using c`x in my example in place of x prevents synchronizing the value of x when it is used inside Parallelize. Then we can add this context to the $ContextPath to make the symbol easily accessible.

The most convenient way to do this is probably putting all definitions in a package that loads the library functions using LibraryFunctionLoad[FindLibrary[...], ...]. For this to work, the library must have been compiled manually first on both the local and remote machine, however, the package code can be the exactly same both for the main and the subkernels.


I am still interested if someone can confirm that variables not in $Context are guaranteed not to be auto-synchronized.

Update It has been confirmed here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文