【问题标题】:RPC from C++ code to Common Lisp code从 C++ 代码到 Common Lisp 代码的 RPC
【发布时间】:2013-12-03 13:32:08
【问题描述】:

我有两个代码库:一个用 C++ 编写,另一个用 Common Lisp 编写。 Lisp 代码库中实现了一个特定的功能,我想从我的 C++ 代码中访问它。我搜索了 Foreign Function Interfaces 以从 C++ 调用 Lisp 函数,但似乎找不到任何东西(我发现 FFI 主要用于另一个方向)。所以我决定实现某种形式的 RPC 来满足我的要求,它们是:

  • 两个代码都将在同一台机器上运行,因此远程机器调用的可扩展性并不重要。

  • 来自 C++ 的输入将是一个 Lisp 样式的列表,这是来自 Lisp 代码的函数将作为输入的内容。

  • 每次执行代码都会进行 1000 次调用,因此每次远程调用的性能至关重要。

到目前为止,我从网络上的各种资源中了解到可能的解决方案是:

  • Sockets - 设置一个 Lisp 代码实例,该实例将侦听来自 C++ 代码的函数调用,在给定的输入上运行函数,并将结果返回给 C++ 代码.

  • XML-RPC - 在 Lisp 端设置 XML-RPC 服务器(这很容易,因为我使用 Allegro Common Lisp,它提供了支持 XML-RPC 的 API)然后使用 C++ 的众多 XML-RPC 库之一进行客户端调用。

我认为这些方法的优缺点如下:

  • 套接字是一个低级结构,所以看起来我需要自己完成大部分连接管理、读取和解析套接字上的数据等工作。

  • XML-RPC 似乎更适合我的需求,但我读到它总是使用 HTTP,并且无法使用 UNIX 域套接字。所以,感觉 XML-RPC 对于我的想法来说可能有点过头了。

有没有人在实现一些类似的代码集成方面有任何经验?本地 RPC 的套接字和 XML-RPC 之间的性能是否存在显着差异?任何关于哪种方法可能更好的建议都会非常有帮助。此外,也将不胜感激有关执行此操作的不同技术的建议。

编辑:以下是有关共享功能的更多详细信息。 Lisp 代码中有一个函数 f 可用(它足够复杂,使得在 C++ 中重新实现的成本高得令人望而却步)。它将两个列表 L1 和 L2 作为输入。我对这种情况的设想如下:

  • L1 和 L2 用 C++ 构建并发送到 Lisp 端并等待结果,
  • 在 Lisp 端的输入 L1 和 L2 上调用 f 并将结果返回给 C++ 端,
  • C++ 端接收结果并继续计算。

L1 和 L2 的尺寸通常不大:

  • L1 是一个列表,通常包含 100 个元素,每个元素是一个最多包含 3-4 个原子的列表。

  • L2 也是一个包含

所以每个 RPC 的总数据量可能是一个 100s/1000s 字节的字符串。这个调用是在我的 C++ 代码中的每个 while 循环开始时进行的,因此很难给出每秒调用次数的具体数字。但从我的实验来看,我可以说它通常每秒完成 10 到 100 次。 f 不是数值计算:它的符号。如果您熟悉 AI,它本质上是在一阶逻辑中进行符号统一。所以它没有副作用

【问题讨论】:

  • 你应该多解释一下共享功能......
  • 你可能会发现这个有用:common-lisp.net/projects/cffi/manual/html_node/…
  • 即使进行了编辑,您对共享功能的解释也不够充分。它的真正作用是什么(简而言之);实际的数据类型是什么(远程传递的参数,接收的结果)......你多久调用一次......?它是幂等的吗...?如果类型是列表,它们的元素的类型是什么?
  • 您可以查看cl-cxx

标签: c++ sockets common-lisp rpc xml-rpc


【解决方案1】:

如果您查看一些 Common Lisp 实现,它们的 FFI 允许从 C 端调用 Lisp。那不是远程的,而是本地的。有时直接包含 Lisp 而不是远程调用它是有意义的。

像 LispWorks 或 Allegro CL 这样的商业 Lisps 也可以提供共享库,您可以在应用程序代码中使用这些库。

例如define-foreign-callable 允许调用 LispWorks 函数。

Franz ACL 可以做到:http://www.franz.com/support/documentation/9.0/doc/foreign-functions.htm#lisp-from-c-1

ECL 之类的东西也应该可以从 C 端使用。

【讨论】:

  • 如果 Lisp 和 C++ 代码在同一个进程中,则此方法有效。
  • @Rainer Joswig:ACL 文档说:“C 函数必须已加载到 Lisp 中,并且必须已从 Lisp 中调用。”但这对我来说是个问题,因为我只想将 Lisp 代码用作服务器,并且不想再调用耦合代码库,更不用说从 Lisp 调用 C++ 可执行文件了。我确实研究过 ECL,我认为同样的困难也存在。此外,Lisp 代码使用了几个 ACL 特定的结构,如果我尝试将其移植到 ECL,这些结构可能会中断。
【解决方案2】:

我最近开始从事一个需要类似功能的项目。以下是我迄今为止研究的一些内容以及一些评论:

  • cl-mpi 原则上允许(尽管非常低级)直接的进程间通信,但编码数据是一场噩梦!您在 C/C++ 方面的设计非常不舒服(只是非常非常有限 + 无法发送可变长度数组)。另一方面,Lisp 库既过时又似乎处于其开发的早期阶段。

  • Apache Trift 更像是一种语言,然后是一个程序。慢,记忆力强。 Protobuf、BSON 都是一样的。 Protobuf 可能是这组中效率最高的,但您需要推出自己的通信解决方案,它只是编码/解码协议。

  • XML、JSON、S 表达式。 S 表达式在这一类别中获胜,因为它们更具表现力,并且一方已经拥有非常高效的解析器。唉,这在速度/内存方面比 Trift / Protobuf 还要糟糕。

  • CFFI。叹息......管理双方的指针将是一场噩梦。理论上是可以的,但在实践中肯定很难。这也将不可避免地对 Lisp 垃圾收集器的性能造成负担,因为你必须阻止它。

  • 最后,我切换到 ECL。到目前为止,一切都很好。我正在研究mmaped 文件作为共享数据的手段。到目前为止,我为自己做出的结论,这将是要走的路。至少我现在想不出更好的了。

【讨论】:

    【解决方案3】:

    还有许多其他方法可以使两个进程进行通信。你可以阅读inter-process communication wikipage。

    其中一个参数是异步或同步字符。您的远程处理是remote procedure call(来自客户端的每个请求都只有一个来自服务器的响应)还是异步message passing(双方都在发送消息,但没有请求和响应的概念;每一方都处理传入的消息作为事件)。

    另一个参数是延迟和带宽,即交换的数据量(每条消息,例如每秒)。

    带宽确实很重要,即使在同一台机器上也是如此。当然,管道或 Unix 套接字为您提供了非常大的带宽,例如 100 兆字节/秒。但在某些情况下,这可能还不够。在这种管道情况下,数据通常从内存复制到内存(通常是两次)(例如,从一个进程地址空间到另一个)。

    但你可能会考虑例如CORBA(参见例如 CLORB 在 lisp 端,和 this tutorial on OmniORB),或 RPC/XDR,或 XML-RPC(在 lisp 端使用 S-XML-RPC),或 JSON-RPC 等...

    如果您没有大量数据和大量带宽(或每秒有很多请求或消息),我建议您使用文本协议(也许serializing 带有 JSON 或 YAML 或 XML),因为它比二进制协议更容易(BSONprotobuf 等...)

    套接字层(可以使用unix(7)AF_UNIX套接字,纯匿名或命名pipe(7)-s,或tcp(7)即TCP/IP,它的优点是让您能够分配计算在通过网络通信的两台机器上)可能是最简单的,只要你在(C++ 和 Lisp)两边都有一个多路复用系统调用,比如poll(2)。你需要在两边缓冲消息。

    也许您想要 MPI(在 lisp 端使用 CL-MPI)。

    我们无法为您提供更多帮助,除非您非常详细地解释了从 C++ 到 Lisp 共享的“功能”是什么(它在做什么,每秒多少远程调用,多少容量)和类型的数据,什么计算时间等等......)。远程函数调用是idempotent还是nullipotent,有side-effects吗?是stateless protocol...

    远程过程调用中涉及的实际数据类型很重要:序列化具有共享节点的复杂 [数学] 循环图比普通的人类可读字符串要昂贵得多....

    鉴于您的最新详细信息,我建议使用 JSON... 非常适合传输 abstract syntax tree 之类的数据。或者,只传输 s 表达式(您可能会在 C++ 中遇到一个小问题来解析它们,一旦您指定并记录了您的约定,这真的很容易;如果您的叶子或符号名称具有任意字符,您只需要定义一个对它们进行编码的约定。)。

    【讨论】:

    • 是的,我确实看到了 IPC 页面。我感兴趣的远程处理类型是远程过程调用(来自 C++ 的一些数据被设置到 Lisp 服务器,计算在那里完成,然后发送回 C++ 代码,等待结果)。带宽并不重要,因为所有计算都在同一台机器上。谢谢,我会看看 CORBA 和 JSON/YAML。每次发送的数据不会太大,就像我之前所说的,带宽并不重要,因为两个代码都在同一台机器上运行。
    • 评论cl-mpi - 几天前我真的试过了。不幸的是,该项目似乎被放弃了。它仍然存在错误,并且不能很好地与最新版本的 OpenMPI 配合使用(在项目开始时,这些都是实验性的)。尽管最终我可以让它工作,但我只尝试了非常基本的东西,我不确定在大型复杂项目中使用它是否安全。
    • 是的,套接字层解决方案和 XML-RPC(和 S-XML-RPC)是我目前所考虑的。 MPI 方法听起来也很有希望。我不熟悉用法,所以我需要更详细地阅读它。 @wvxvw:啊,我明白了!感谢您的提醒。
    • @Basile:当你说传输 s 表达式时,你是指任何特定的 RPC 协议还是只是通过普通的 UNIX 套接字?
    • 就在普通的 Unix 套接字(甚至是管道)之上。它非常简单,如果您添加 s 表达式适合以换行符结尾的单个(可能很长)行的约定,它会更简单。 FWIW,MELT 几乎就是这样做的。您可以查看代码(它是免费软件),或在gcc-melt@googlegroups.com 论坛上向我询问更多详细信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多