【问题标题】:Thrift is too slow compared to direct calling function与直接调用函数相比,Thrift 太慢了
【发布时间】:2014-05-15 04:16:15
【问题描述】:

我尝试“http://thrift-tutorial.readthedocs.org/en/latest/usage-example.html”中的示例。这个例子只是计算两个数字的乘积。服务器:Java,客户端:Python。

如果我尝试通过 thrift 获得产品 3000 次,则经过的时间约为 4.8 秒。 如果我在 python 中创建一个简单的函数(乘法)并直接调用它 3000 次,则经过的时间约为 0.007 秒(快 686 倍)。

那么我该如何提高性能呢?我想构建一个应用程序并将其分成一些子应用程序。它们可以用多种语言实现,并且它们将通过 thrift 相互通信,但是由于性能如此糟糕,我应该考虑将它们组合到单独的应用程序中吗?

App-A (Java)                   App-B (Python)
     |                                 |
     |------------ App-C (C++) --------|

App-A+C (Java)                   App-B+C (Python)
(implement C in Java)            (implement C in Python)

【问题讨论】:

    标签: performance architecture thrift thrift-protocol


    【解决方案1】:

    您的比较基于不正确的假设。假设是,跨进程调用(至少)与进程内调用一样快,这根本不是真的。

    这是著名的8 network fallacies originated by Peter Deutsch之一,后来的extended by others不仅适用于网络,也适用于单机IPC:与你的想法相反,transport cost is NOT zero .

    根据您有限的信息,据我所知,每次 IPC 往返 1.5 毫秒对我来说听起来还不错。

    【讨论】:

    • 我正遭受这个问题的困扰,在过去的 2 天里试图找出我失败的地方。至少现在我知道尝试使其成为快速的进程内调用是一场失败的战斗。需要68秒!完成一个随机样本...
    • @TonyTannous:如果是 68 秒,那么它很可能不是传输层,除非您试图疯狂地移动大量数据。如果不知道更多,我建议开始检查服务器端。
    • 实际上我正在发送大量数据。 10MB 从客户端到服务器。将 10MB 移动到较小的部分会更好吗?我将 1GB 的数据分成 10MB 的块并发送...
    • 哇!通过将1GB 文件切片为110KB 的块而不是10MB,它在12 分钟而不是22 分钟内完成!惊人的。谢谢 :) 已经 +1。
    • 太棒了!如果您尚未使用 TFramedTransportTBufferedTransport,请考虑使用它。它可能有助于减少内存分配量,从而提高性能。
    【解决方案2】:

    您可以将两个关键优化设置为目标:

    • 在等待之前发送您已有的所有数据。
    • 如果唯一要做的事情是直接返回,则不要通过通道发送计算结果。

    您在问题中描述的是“聊天协议”的极端情况。网络有延迟(delay)。如果在开始下一次计算之前等待每个结果,则大部分时间都花在等待网络传输上,而不是等待实际计算。通过在收到第一个结果之前发送另一个计算,您可以显着提高吞吐量。

    所以最简单的方法是允许重叠请求。第二对值的乘积不依赖于第一个结果,所以不要等待第一个结果到达。

    当您处理本地 IPC 时,这并没有太大帮助。通信的代价不是延迟,而是消息处理和线程同步,取决于请求的数量,而不是顺序。

    更大回报的更大变化是让每个请求都代表一个复杂的算法。例如,不是远程调用两个数字的乘法,而是尝试远程调用整个过滤操作,其中参数是整个数据向量或矩阵,服务器将执行 FFT、倍数、逆 FFT、缩放、然后将结果传回。这同时满足了最初的目标:所有可用数据一起发送,而不是单独发送,从而减少等待时间。由于不必交换中间结果,因此减少了总网络流量。


    最后一种选择是将所有三种语言的代码链接到一个进程中,以便直接访问数据和函数调用。许多语言允许构建导出纯“C”函数和数据的对象。

    此外,.NET 等虚拟机运行可以通过编译不同源语言生成的中间语言。使用 .NET,您有 C#(类似 Java)、C++/CLI(支持完整的 C++,以及用于处理 .NET 数据的扩展)和 IronPython,它们涵盖了您的问题图。加上 F#、JavaScript、Ruby 变体等等。 Java 虚拟机应该是特定于语言的,但人们已经编写了 Clojure 和其他编译为字节码的语言。

    虚拟机技术的优势在于它可以实现一些跨语言优化(.NET JIT 进行跨模块内联)。缺点是您的性能取决于 JIT 优化,这通常是最低的公分母。 C++/CLI 实际上非常适合弥合这一差距,因为它支持完全优化的本机代码(包括 SIMD)、.NET 中间语言 (MSIL),以及用于在它们之间进行通信的最低开销层(C++ “It Just Works”互操作) )。

    但您可以在 Java VM 上完成大致相同的事情,方法是使用 JNI 连接完全优化的 C++ 代码,以使用 SIMD 进行密集的数字运算。

    【讨论】:

    • 我的例子只是假的。好的,假设我有一个服务器并且成千上万的客户端同时发送请求,我的服务器通过与通过 Thrift 进行“实际工作”的“某物”通信来服务每个客户端的每个请求。问题是我的服务器调用函数直接比 Thrift 快约 700 倍,无论“实际工作”多么复杂。我曾认为 Thrift 可以帮助一个应用程序直接(而不是通过网络)从另一个应用程序调用函数,因为 Python 调用 C++ 扩展。
    • @William:您不能跨进程边界直接调用。您所拥有的可以更多地与计算机内部的本地网络进行比较。尽量减少消息数量和复制量仍然很重要。本地 IPC 可以使用共享内存等技巧来避免复制,但是告诉另一个线程检查共享数据结构仍然会有一些开销。
    • @William:我不同意你的“无论实际工作多么复杂”。如果你把所有的工作都放在几条消息中,那么即使函数调用慢了 700 倍,它也会从你的程序运行时间的 0.001% 变为你程序运行时间的 0.701%。换句话说,太小了,不用担心。
    • 我以为 Thrift 可以帮助一个应用直接(而不是通过网络)调用另一个应用的功能” - 我有没有提到运输成本不为零?而且这个也适用于IPC(没有网络)?确实,我想我是这么说的。
    • @William:好问题。这取决于。如果 C 仅充当继电器,则不会有太大的改善。但是,如果 C 做了某种处理导致效果,那么 AC 和 BC 之间传递的数据量加起来会明显小于 C 和您的 DB 之间的流量,并且如果 C 检索到的数据可以满足多个来自 A 和/或 B 的请求,这确实可以提高整体性能。
    猜你喜欢
    • 2020-11-19
    • 2013-08-18
    • 1970-01-01
    • 2018-10-05
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 2016-11-05
    • 2022-01-11
    相关资源
    最近更新 更多