运行 Elixir/Erlang 中的 C 代码:端口还是 NIF?

Running C code in Elixir/Erlang: Ports or NIFs?

我发现 Elixir 程序可以通过 NIFs (native implemented functions) or via OS-level ports 运行 C 代码。阅读了这些链接和类似链接后,我不是百分百清楚什么时候使用一种或另一种方法(或者完全是其他方法?),并且觉得为我自己和其他新手提供直接比较会很好.谁能提供一下?

什么是端口?

端口基本上是独立于 Erlang VM 运行 的独立程序。 Erlang VM 通过标准 input/output 与 运行ning 端口通信,生成的端口位于拥有它的 Erlang 进程后面,可以促进端口与 Erlang 或 Elixir 应用程序的其余部分之间的通信。端口是 "safe",如果端口崩溃,它不会关闭整个 Erlang VM。

Porcelain 可能会对 Port 模块中已经提供的内容进行改进和扩展。 System.cmd/3 还在其底层实现中使用端口。

什么是 NIF?

本机内联函数或 "NIFs" 是在本质上由 Erlang VM 加载的共享库/DLL 中定义的函数,并使用公开 C-compatible ABI 的某种语言编写。 NIF 比端口更高效(因为它们不必通过 STDIN/STDOUT 进行通信)并且在许多方面更简单(因为您不必处理编码和解码数据之间的数据) Elixir 和 non-Elixir 代码库),但它们也不太安全; NIF 可以 使 Erlang VM 崩溃,而 long-running NIF 可能会锁定 Erlang VM(因为调度程序无法推理本机代码)。

什么是端口驱动程序?

端口驱动程序是一种 in-between 将外部代码与 Erlang 或 Elixir 代码库集成的方法。与 NIF 一样,它们被加载到 Erlang VM 中,因此端口驱动程序可能会崩溃或挂起整个 VM。与端口一样,它们的行为类似于 Erlang 进程。

什么时候应该使用端口?

  • 您希望您的外部代码表现得像一个普通的 Erlang 进程(至少足以让这样的进程代表您的外部代码包装它和 send/receive 消息)
  • 您希望 Erlang VM 能够在您的外部代码崩溃后继续存在
  • 您想在外部代码中实现 long-running 任务
  • 您想使用不支持 C-compatible FFI 的语言编写外部代码(或者不想处理您语言的 FFI 设施)

我什么时候应该使用 NIF?

  • 您希望您的外部代码表现得像普通 Erlang 函数的集合(特别是如果您想要定义一个 Erlang/Elixir 模块来导出在 native-compiled 代码中实现的函数)
  • 您想避免通过标准通信造成任何潜在的性能影响/开销 input/output and/or 您希望避免在 Erlang 术语和您的外部代码理解的东西之间进行转换
  • 您有理由相信您的外部代码正在做的事情既不会 long-running 也不太可能崩溃(包括,在后一种情况下,如果您使用 Rust 之类的语言编写 NIF;另请参阅: Rustler), 或者...
  • 您有理由相信崩溃或挂起 Erlang VM 对于您的用例来说是可以接受的(例如,您的代码是分布式的并且能够在 Erlang 节点突然丢失的情况下幸存下来,或者您正在编写桌面应用程序并且application-wide 崩溃除了给用户带来不便之外没什么大不了的)

什么时候应该使用端口驱动程序?

  • 您希望您的外部代码表现得像 Erlang 进程
  • 您想避免通过标准进行通信的开销 and/or 复杂性 input/output
  • 您有理由相信您的端口驱动程序不会崩溃或挂起 Erlang VM,或者...
  • 您有理由相信 Erlang VM 的崩溃或挂起不是严重问题

你推荐什么?

这里有两个方面需要权衡:

  • Process-like诉module-like
  • 安全与高效

如果您想在 process-like 接口后获得最大的安全性,请使用端口。

如果您想在 module-like 接口后获得最大的安全性,请使用具有包装 System.cmd/3 或直接使用端口与外部代码通信的功能的模块

如果你想在 process-like 接口后获得更高的效率,请使用端口驱动程序。

如果你想在 module-like 接口后获得更高的效率,请使用 NIF。