查看原文
其他

底层I/O性能大PK:Python/Java被碾压,Rust有望取代C++

Eugene Retunsky CSDN 2021-04-25

作者 | Eugene Retunsky

译者 | 弯月

出品 | CSDN(ID:CSDNnews)

以下为译文:

我发现一旦计算机没了互联网,我就无所事事。大多数时候,我们都使用笔记本电脑和智能手机访问其他地方存储或生成的信息。我们很难想象如果没有了网络通信,不面向用户的应用还有什么用处。尽管I/O操作与数据处理的比例可能有所不同,但此类操作可能会引发服务延迟。

实现后端服务的编程语言有许多。因此,人们对于比较这些语言的性能有着一种天然的好奇心,而比较的基准也各种各样。例如,有一个基准可以比较不同语言在解决不同的离线任务时的性能。还有一个基准TechEmpower,可以衡量Web框架的性能。这些度量非常实用,我们可以通过它们大致了解语言的性能。然而,一般这些基准都有特定的使用场合和特定的操作,所以不一定具有代表性。

因此,我对各个平台上基本I/O的不可降低的成本非常好奇。对TCP代理进行基准测试可能是最简单的情况。其中不涉及数据处理,只处理传入/传出连接,并中继原始的字节数据。微服务的速度基本上不可能比TCP代理更快,因为微服务所需做的处理不可能少于TCP代理,它的功能只会更多,比如在此基础上构建的其他功能:解析、验证、遍历、打包、计算等等。

在本文中,我们将比较以下解决方案:

  • HAProxy:TCP代理模式。这是一个使用C语言编写的成熟解决方案。http://www.haproxy.org/

  • draft-http-tunnel:一个使用C++的解决方案(以TCP模式运行),只有最基本的功能(trantor):https://github.com/cmello/draft-http-tunnel/(

  • http-tunnel:使用Rust(tokio)编写的简单的HTTP隧道 / TCP代理(在TCP模式下运行):https://github.com/xnuter/http-tunnel/

  • tcp-proxy:Golang解决方案:https://github.com/jpillora/go-tcp-proxy

  • NetCrusher:Java解决方案(Java NIO)。使用G1在JDK 11上运行基准测试:https://github.com/NetCrusherOrg/NetCrusher-java/

  • pproxy:基于asyncio的Python解决方案(以TCP代理模式运行):https://pypi.org/project/pproxy/

以上所有的解决方案均使用非阻塞I / O。

注意:我尽力挑选了Golang、Java和Python的最佳解决方案,如果你知道更好的解决方案,请在下方留言。

实际的后端是Nginx,具体配置为:以HTTP模式发送10kb的数据。

基准结果分为两组:

  • 基准、C、C++、Rust:高性能语言。

  • Rust、Golang、Java、Python:内存安全的语言。

没错,两组中都包含Rust。


方法的简要说明


  • 为TCP代理分配了两个核心(使用cpuset)。

  • 为后端分配了两个核心(Nginx)。

  • 请求速率从10k开始,最高至每秒25k请求。

  • 每50个请求共用一个连接(每个请求10kb)。

  • 测试在同一台虚拟机上运行,为的是避免网络噪音。

  • VM选择了针对计算优化的实例类型(独占所有分配到的CPU),为的是避免出现“嘈杂的邻居”问题。

  • 延迟测量分辨率为微秒(µs)。

我们比较的统计数据包括:

  • 百分位数(从p50到p99):关键统计数据。

  • 离群值(p99.9和p99.99):对于大型分布式系统的组件非常关键。

  • 最大延迟:最坏的情况永远不容忽视。

  • 修整后的均值tm99.9:去除0.1%的最佳/最差值后得到的均值,为的是捕捉中心趋势(不考虑离群值)。

  • 标准偏差:评估延迟的稳定性。

为了收集数据,我使用了perf-gauge。

下面,我们来看看结果!


高性能语言:C、C++、Rust


我经常听人说,Rust的性能可与C / C ++媲美。下面我们就来看一看在处理网络I/O时,Rust是否能够表现出同等的水平。 

以下四个图分别是:基准、C、C++、Rust:

图:绿色:p50;黄色:p90;蓝色:p99(以µs为单位)(左图);橙色-速率(右图)

将每个统计信息的后端也加上去,所得到的微秒数如下所示。以下数字是在最大请求速率下间隔的平均值(不包括加速):

图:开销(微秒)

相对结果(开销占基准的百分比):

图:基准统计的开销(%)

有趣的是,尽管在p99.9级别上,用C++编写的代理比HAProxy和Rust都快一点,但在p99.99和max上的表现却最糟。但这可能是由于实现导致的,在这里是非常基本的(而且通过回调实现,没有通过处理future实现)。

此外,我测量了CPU和内存消耗,详情请看这里(https://github.com/xnuter/perf-gauge/wiki/Moderate-request-rate#cpu-and-memory-consumption)。

总的来说,使用C、C++和Rust编写的三个TCP代理表现出的性能不相上下:精简且稳定


比较内存安全语言:Rust、Golang、Java和Python


下面,我们来比较一下内存安全语言。不幸的是,Java和Python的解决方案无法仅通过两个核心处理25,000 rps,因此Java的基准测试为15,000 rps,而Python的基准测试为10,000 rps。

下列四个图分别是:Rust、Golang、Java和Python。

图:绿色:p50;黄色:p90;蓝色:p99(以µs为单位)(左图);橙色-速率(右图)

我们看到了巨大的差异。对于Rust,上一张图中的“噪声”似乎很多,但在这张图中非常稳定。另外,请注意Java的冷启动高峰。以下数字是在最大请求速率下间隔的平均值(不包括加速):

如你所见,Golang在p50 / p90级别上也具备一定的竞争力。但是,在更高的百分位上,差异急剧增加,标准偏差充分反映了这一点。但是,请再看一下Java的数字!

离群值(p99.9和p99.99)百分位非常耐人寻味。与Rust的差异肉眼可见:

图:绿色:p99.9;蓝色:p99.99(µs)(左图);橙色-速率(右图)

相对结果(开销占基准的百分比):

图:基准统计的开销(%)

总的来看,Rust的延迟差异远低于Golang、Python,尤其是Java。在p50 / p90的延迟级别上, Golang可与Rust并肩


最大吞吐量


还有一个问题:每个代理可以处理的最大请求速率是多少?

Nginx能够处理的请求量超过60,000 rps。如果我们在客户端和后端之间建立一个代理,就会降低吞吐量。如下所示,C、C ++、Rust和Golang服务的请求量可以达到Nginx的70%~80%,而Java和Python的表现则比较差:

图:左轴:延迟开销;右轴:吞吐量

  • 蓝线是tail延迟(Y轴在左侧):越低越好。

  • 灰色代表吞吐量(Y轴在右侧):越高越好。


总结


这些基准测试并不全面,我的目标是比较各个语言基本的I / O。

根据以上测试结果,再结合Benchmarks Game和TechEmpower的结果表明,如果性能对你的服务来说非常重要,那么与Golang、Java或Python相比,Rust可能是更好的替代方案。另外,在使用C或C++编写新服务之前,可以考虑一下Rust。因为Rust不仅具有可与C / C++同等出色的性能,而且还具备以下优势:

  • 内存安全。

  • 无数据竞争。

  • 易于编写方便理解的代码。

  • 与Python一样易于访问和灵活。

  • 它会迫使工程师在投入生产之前,提前设计和理解数据的所有权。

  • 它有一个了不起的社区和一个庞大的组件库。

  • 所有试过的人都说好!

原文链接:https://medium.com/star-gazers/benchmarking-low-level-i-o-c-c-rust-golang-java-python-9a0d505f85f7

声明:本文由CSDN翻译,转载请说明来源。

一条推文卖出 290 万美元天价?网友:我竟是隐藏的富翁

经纬张颖「炮轰」扫码点餐;淘宝特价版给拼多多送芒果;Firefox 87.0 发布|极客头条

谷歌低调了 5 年的 Fuchsia OS,终于有望面世了!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存