我有两个代码库:一个用C ++编写,另一个用Common Lisp编写。在Lisp代码库中实现了一个特殊的功能,我想从我的C ++代码中访问它。我搜索了外部函数接口来从C ++调用Lisp函数,但似乎找不到任何东西(我发现其他方向的FFI主要是)。所以我决定实现符合我要求的某种形式的RPC,它们是:
这两个代码都将在同一台机器上运行,因此远程机器调用的可扩展性并不重要。
来自C ++的输入将是一个Lisp风格的列表,这是Lisp代码中的函数将作为输入。
每次执行代码时,此调用将进行1000次,因此每次远程调用的性能至关重要。
到目前为止,我从网上的各种资源中了解到可能的解决方案:
套接字 - 设置一个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的大小通常不大:
L1是一个通常包含100个元素的列表,每个元素都是最多3-4个原子的列表。
L2也是一个包含< 10个元素,每个元素是最多3-4个原子的列表。
因此,每个RPC的总数据量可能是100s / 1000s字节的字符串。这个调用是在我的C ++代码中的每个while循环开始时进行的,因此很难给出每秒调用次数的具体数字。但是根据我的实验,我可以说它通常每秒完成10s-100s。 f 不是数值计算:它的象征性。如果您熟悉AI,它基本上在一阶逻辑中进行符号统一。所以没有副作用。
答案 0 :(得分:5)
如果你看一些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方面使用。
答案 1 :(得分:2)
我最近开始在一个需要类似功能的项目上工作。以下是我迄今为止研究过的一些评论:
cl-mpi
原则上允许(尽管是非常低级别的)直接进程间通信,但编码数据是一场噩梦!你在C / C ++方面有非常不舒服的设计(只是非常非常有限+无法发送可变长度数组)。另一方面,Lisp库已经过时了,似乎还处于开发的早期阶段。
Apache Trift,它更像是一种语言,然后是一种程序。慢慢的记忆力。 Protobuf,BSON是一样的。 Protobuf可能是这一组中最有效的,但你需要推出自己的通信解决方案,它只是编码/解码协议。
XML,JSON,S表达式。 S表达式在这个类别中获胜,因为它们更具表现力,并且一方已经拥有一个非常有效的解析器。唉,就速度/记忆而言,这比Trift / Protobuf更糟糕。
CFFI。叹息......管理双方的指针将是一场噩梦。这在理论上是可能的,但在实践中必须非常困难。这也将不可避免地对Lisp垃圾收集器的性能造成负担,因为你必须阻止它。
最后,我切换到ECL。到现在为止还挺好。我正在研究mmap
ed文件作为共享数据的方法。我到目前为止做的结论,这将是要走的路。至少我现在想不出更好的东西。
答案 2 :(得分:1)
还有很多其他方法可以让两个进程进行通信。你可以阅读inter-process communication wikipage。
其中一个参数是异步或同步字符。您的远程处理是remote procedure call(来自客户端的每个请求只有一个来自服务器的响应),还是异步message passing(双方都在发送消息,但没有请求和响应的概念;每个将传入的消息作为事件处理。)
另一个参数是延迟和带宽,即交换的数据量(每条消息,例如每秒)。
即使在同一台机器上,带宽也很重要。当然,管道或Unix套接字会为您提供非常大的带宽,例如100兆字节/秒。但是有些情况可能还不够。在该管道情况下,数据通常从内存复制(通常两次)到内存(例如从一个进程地址空间复制到另一个进程地址空间)。
但你可以考虑例如CORBA(请参阅lisp方面的CLORB和this tutorial on OmniORB),或RPC / XDR或XML-RPC(在lisp方面使用S-XML-RPC),或{ {3}}等...
如果您没有大量数据和大量带宽(或每秒许多请求或消息),我建议使用文本协议(可能JSON-RPC使用JSON或YAML或XML)因为它比二进制协议(serializing,BSON等更容易......)
套接字层(可以使用protobuf AF_UNIX
套接字,简单匿名或命名为unix(7) - s,或pipe(7)即TCP / IP,具有以下优点:让你能够在两台通过网络通信的机器上分配计算)可能是最简单的,只要你在(C ++和Lisp)两侧有一个多路复用系统调用,如tcp(7)。你需要缓冲双方的消息。
我们无法帮助你,除非你在细节中解释得非常好,从C ++到Lisp共享的“功能”是什么,它是做什么的,每秒有多少个远程调用,什么卷和数据类型,计算时间等等....)。是远程函数调用CL-MPI还是idempotent,是否有nullipotent?是side-effects ......
远程过程调用中涉及的实际数据类型非常重要:使用共享节点序列化复杂的[数学]循环图比使用简单的人类可读字符串要昂贵得多......
鉴于你的最新细节,我建议使用JSON ......这非常适合传输stateless protocol之类的数据。或者,只发送s表达式(你可能会留下C ++中的小问题来解析它们,一旦你指定并记录了你的约定,这很容易;如果你的叶子或符号名称有任意字符,你只需要定义一个对它们进行编码的惯例。)。