我发现 Elixir 程序可以通过NIF(本机实现的函数)或OS-level ports运行 C 代码。阅读了这些和类似的链接后,我不是百分百清楚何时使用一种或另一种方法(或完全是其他方法?),并且觉得为我自己和其他新手提供直接比较会很好. 谁能提供?
1 回答
什么是端口?
端口基本上是独立于 Erlang VM 运行的独立程序。Erlang VM 通过标准输入/输出与正在运行的端口进行通信,生成的端口位于拥有它的 Erlang 进程之后,可以促进端口与 Erlang 或 Elixir 应用程序的其余部分之间的通信。端口是“安全的”,因为如果端口崩溃,它不会导致整个 Erlang VM 瘫痪。
PorcelainPort
可能是对模块 中已经提供的内容的可能改进和扩展。System.cmd/3
还在其底层实现中使用端口。
什么是 NIF?
本机内联函数或“NIF”是在本质上是由 Erlang VM 加载的共享库/DLL 中定义的函数,并使用某种公开了 C 兼容 ABI 的语言编写。NIF 比端口更有效(因为它们不必通过STDIN
/进行通信STDOUT
)并且在许多方面更简单(因为您不必处理 Elixir 和非 Elixir 代码库之间的编码和解码数据),但是它们'也不太安全; NIF可能会使 Erlang VM 崩溃,而长时间运行的 NIF 可能会锁定 Erlang VM(因为调度程序无法推理本机代码)。
什么是端口驱动程序?
端口驱动程序是一种将外部代码与 Erlang 或 Elixir 代码库集成的中间方法。与 NIF 一样,它们被加载到 Erlang VM 中,因此端口驱动程序可能会导致整个 VM 崩溃或挂起。像端口一样,它们的行为类似于 Erlang 进程。
我什么时候应该使用端口?
- 您希望您的外部代码表现得像一个普通的 Erlang 进程(至少足以让这样的进程包装它并代表您的外部代码发送/接收消息)
- 您希望 Erlang VM 能够在外部代码崩溃后幸存下来
- 你想在你的外部代码中实现一个长时间运行的任务
- 你想用不支持 C 兼容 FFI 的语言编写你的外部代码(或者不想处理你的语言的 FFI 工具)
什么时候应该使用 NIF?
- 你希望你的外部代码表现得像普通 Erlang 函数的集合(特别是如果你想定义一个 Erlang/Elixir 模块来导出在本机编译代码中实现的函数)
- 您希望避免通过标准输入/输出进行通信的任何潜在性能损失/开销和/或您希望避免在 Erlang 术语和外部代码理解的内容之间进行转换
- 你有理由相信你的外部代码正在做的事情既不会长时间运行也不会崩溃(包括,在后一种情况下,如果你用 Rust 之类的东西编写你的 NIF;另见:Rustler),或者.. .
- 您有理由相信崩溃或挂起 Erlang VM 对于您的用例是可以接受的(例如,您的代码是分布式的并且能够在突然丢失 Erlang 节点时幸存下来,或者您正在编写桌面应用程序和应用程序范围的崩溃除了给用户带来不便之外没什么大不了的)
什么时候应该使用端口驱动程序?
- 你希望你的外部代码表现得像一个 Erlang 进程
- 您希望避免通过标准输入/输出进行通信的开销和/或复杂性
- 您有理由相信您的端口驱动程序不会崩溃或挂起 Erlang VM,或者...
- 您有理由相信 Erlang VM 的崩溃或挂起不是关键问题
你有什么建议吗?
这里有两个方面需要权衡:
- 类流程与类模块
- 安全与高效
如果您希望在类似流程的界面后面获得最大的安全性,请使用端口。
如果您希望在类似模块的接口之后获得最大的安全性,请使用具有包装System.cmd/3
或直接使用端口与外部代码通信的功能的模块
如果您希望在类似进程的界面后面获得更高的效率,请使用端口驱动程序。
如果您希望在类似模块的界面后面获得更高的效率,请使用 NIF。