连接不同的编程语言



这更像是一个设计问题

我计划写一些实现CPU密集型算法的web服务。我试图解决的问题是-更高级的语言,如python, perl或java使编写web服务变得容易。而较低级别的语言,如C、c++,则可以微调代码的性能。

所以我在考虑我能做些什么来连接两种语言。以下是我想到的选项:

特定于语言的绑定

使用perl-xs或python的ctypes/loadlibrary或java的JNI。这样做的好处是,我可以编写可以在同一进程中执行的扩展。在本地语言类型和C语言类型之间进行转换的开销很小。

实现一个单独的守护进程

使用像thrift/avro这样的东西,并有一个单独的守护进程来运行C/c++代码。好处是,它与高级语言是松散耦合的。我可以快速替换高级语言。缺点是序列化和本地unix域套接字的开销可能比在相同的地址空间(由前一个选项提供)中执行代码要高。

你们觉得怎么样?

如果您的C/c++代码已经存在,您最好的选择是将其作为服务发布,并使用与您已经拥有的功能匹配的API。然后你可以用你选择的语言编写新的服务,匹配你需要的API,它们可以调用C/c++服务。

如果你的C/c++代码还不存在,并且你打算用一种高级语言(如Java或c#)创建大部分代码,那么考虑最初也用这种语言实现性能关键部分。只有在分析显示了特定的性能问题,并且在您用尽了语言中最基本的优化技术(例如避免在最热循环中分配)之后,您才应该考虑使用glue(如JNI)将已被证明消耗最多周期的位重写到另一种语言中。

换句话说,在手头有数据之前不要进行优化。也没有根本的理由说明为什么你不能从Java中挤出(几乎)与c++相同的性能水平,只要你做足够的尝试。您很有可能最终得到一个比预期更简单的体系结构。

根据您自己的推理,用更高级的语言实现守护进程不是更好吗?这让我们回到了第一步:如何提供从高级语言访问C代码的权限。

如果你决定直接用C语言写一个守护进程,那么最大的缺点就是你将不得不维护所有与你所提供的核心功能无关的服务器代码。这是另一件需要调试的事情。因为它是一种服务,所以它也需要保持安全,没有安全漏洞。

但是正如你提到的,用C为一种特定的语言写一个扩展意味着你基本上被这种语言困住了。对吧?

不一定。请允许我向您介绍SWIG: http://www.swig.org/

使用SWIG,编写C代码和目标语言之间的接口桥的工作大大简化了。事实上,在我工作的地方,我们甚至用它来单独与Java交互,永远不会使用它的多语言支持。它是一个基于您提供的接口文件自动生成样板代码的工具。

是的,接口文件是另一个需要学习的语法。是的,它确实有其局限性。但是效果非常好。

使用SWIG的一个很大的优点是你的库只是一个普通的C/c++库。这有助于简化和澄清代码。这也意味着如果你愿意,你也可以在C/c++项目中直接使用这个库。

看看Mongrel,用c++编写web服务和高性能代码。

"序列化和本地UNIX域套接字的开销"不是问题。假设守护进程和web服务代码之间交换的数据大小与通过网络从web服务发送到客户机的数据大小相同(或更小),那么网络很可能最终成为您的瓶颈,并且开销几乎无法测量。

下面是一个测试通过UNIX套接字传输1GB的脚本(需要netcat-openbsd包和具有大量支持的bash,计时代码借用自定义格式的时间命令):

#!/bin/bash
rm -f hello.sock
nc -l -U hello.sock | wc -c &
sleep 1
T="$(date +%s%N)"
dd if=/dev/zero bs=1048576 count=1024 | nc -U hello.sock
T="$(($(date +%s%N)-T))"
echo "$T nanoseconds / GB"
rm -f hello.sock
在我的英特尔酷睿i7系统上,我得到1664042362纳秒/GB。转换为gbit/s:
(1 GB / 1664042362 ns) * (1000000000 ns / s) * (8 gbit / 1 GB) ~ 4.8 gbit / s

在我的系统上,这个基准测试充分利用了2个内核(通过修改程序以输出几个GB并查看vmstat来确定)。因此,如果您的互联网连接以gbit/s为单位是K,并且您有N个内核,那么当网络满载时,IPC使用的CPU百分比将为:

(K / 4.8) * (2 / N)

如果你使用至少四个核心,你到web服务客户端的带宽是100mbps或更慢,并且通过IPC传输的数据大小与从web服务传输到客户端的数据大小相同或更小,这计算出1%或更少的CPU被IPC开销所消耗。

最新更新