【问题标题】:1 million remote function calls per second每秒 100 万次远程函数调用
【发布时间】:2013-10-15 07:53:12
【问题描述】:

我想实现每秒 0.5-1 百万次远程函数调用。假设我们有一台 Central 计算机开始计算,一台 Worker 计算机进行计算。实际配置中会有很多 Worker 计算机。

假设我们的任务是计算sum of [(random int from 0 to MAX_VAL)*2], PROBLEM_SIZE times 非常幼稚的原型是

Worker:

//The real function takes 0.070ms to compute.
int compute(int input) {
    return input * 2;
}

void go() {
    try {
        ServerSocket ss = new ServerSocket(socketNum);

        Socket s = ss.accept();
        System.out.println("Listening for " + socketNum);

        DataInput di = new DataInputStream(s.getInputStream());
        OutputStream os = s.getOutputStream();
        byte[] arr = new byte[4];
        ByteBuffer wrap = ByteBuffer.wrap(arr);

        for (; ; ) {
            wrap.clear();

            di.readFully(arr);
            int value = wrap.getInt();
            int output = compute(value);

            wrap.clear();
            byte[] bytes = wrap.putInt(output).array();
            os.write(bytes);
        }

    } catch (IOException e) {
        System.err.println("Exception at " + socketNum);
        e.printStackTrace();
    }
}

Central:

void go(){    
    try {

        Socket s = new Socket(ip, socketNum);
        s.setSoTimeout(2000);
        OutputStream os = s.getOutputStream();
        DataInput di = new DataInputStream(s.getInputStream());

        System.out.println("Central socket starting for " + socketNum);

        Random r = new Random();

        byte[] buf = new byte[4];
        ByteBuffer wrap = ByteBuffer.wrap(buf);

        long start = System.currentTimeMillis();
        long sum = 0;

        for(int i = 0; i < n; i++) {
            wrap.clear();
            int value = r.nextInt(10000);

            os.write(wrap.putInt(value).array());

            di.readFully(buf);
            wrap.clear();

            int answer = wrap.getInt();
            sum += answer;
        }

        System.out.println(n + " calls in " + (System.currentTimeMillis() - start) + " ms");
    } catch(SocketTimeoutException ste) {
        System.err.println("Socket timeout at " + socketNum);
    }

    catch (Exception e) {
        e.printStackTrace();
    }

如果 ping 为 0.150 毫秒,并且我们运行 1 线程 Worker 和 1 线程 Central,则每次迭代将花费约 0.150 毫秒。为了提高性能,我在 Worker 和 Central 上都运行 N 线程,n-th 线程监听端口 2000+n。每个线程停止后,我们对结果进行总结。

基准测试

首先,我在我同事的学校网络中运行了上面的程序。其次,我在两个 Amazon EC2 集群实例上运行它。结果差距很大。

CHUNK_SIZE = 100_000 在所有运行中。

朋友的网络:

我认为 3 年前它是可用的顶级配置(至强 E5645)。我相信它针对并行计算进行了高度优化,并且具有简单的 LAN 拓扑,因为它只有 20 台机器。

操作系统:Ubuntu

平均 ping:~0.165 毫秒

N=1 total time=6 seconds 
N=10 total time=9 seconds
N=20 total time=11 seconds
N=32 total time=14 seconds
N=100 total time=21 seconds
N=500 total time=54 seconds

亚马逊网络:

我在同一个 Placement Group 中启动的两个 Cluster Compute Extra Large Instance (cc2.8xlarge) 上运行该程序。

操作系统是一些亚马逊的 linux

平均 ping:~0.170 毫秒。

结果有点令人失望:

N=1 total time=16 seconds 
N=10 total time=36 seconds
N=20 total time=55 seconds
N=32 total time=82 seconds
N=100 total time=250 seconds
N=500 total time=1200 seconds

我对每个配置运行了 2-4 次,结果相似,大部分都是 +-5%

Amazon N=1 结果是有意义的,因为每个函数调用 0.170 毫秒 = 每秒 6000 次调用 = 每 16 秒 100_000 次调用。 Fellow 的人脉 6 秒,其实很惊喜。

我认为现代网络每秒最大的 TCP 数据包约为每秒 40-70k。 它对应于 N=100, time=250 秒:N*CHUNK_SIZE / time = 100 * 100_000packets / 250sec = 10_000_000packets / 250sec = 40_000packets/second。

问题是,我的研究员的网络/计算机配置如何做到如此出色,尤其是在 N 值较高的情况下?

我的猜测:将每个 4 字节的请求和 4 字节的响应放入单个数据包是很浪费的,因为有大约 40 字节的开销。明智的做法是将所有这些微小的请求集中起来,例如 0.010 毫秒,然后将它们放入一个大数据包中,然后将请求重新分配到相应的套接字。 可以在应用程序级别实现池化,但似乎 Fellow 的网络/操作系统已配置为执行此操作。

更新:我玩过 java.net.Socket.setTcpNoDelay(),它没有改变任何东西。

最终目标: 我使用非常大的树来近似具有数百万个变量的方程。目前,具有 200_000 个节点的树适合 RAM。但是,我对需要具有数百万个节点的树的近似方程很感兴趣。这将需要几 TB 的 RAM。算法的基本思想是从节点到叶子的随机路径,并沿着它改进值。当前程序是 32 线程的,每个线程每秒执行 15000 次迭代。我想将它移动到具有相同每秒迭代次数的集群。

【问题讨论】:

  • 这不是编程题吧?
  • 也许是这样(那么请给我一个提示,我应该在哪里搜索有关操作系统网络配置的答案),或者当前的方法有缺陷,或者我应该自己实现池化,或者有现成的解决方案/framework 用于此类高频远程函数调用
  • 其他需要考虑的事情包括确保线程数大致等于处理器数,并确保没有锁争用。

标签: java multithreading networking amazon-ec2


【解决方案1】:

您可能希望启用 Nagle 算法:wikipedia entry

这是一个关于禁用它的链接,可能会有所帮助:Disabling Nagle's Algorithm in linux

【讨论】:

  • 谢谢,乍一看似乎是我需要的选项。
  • 我尝试使用 java.net.Socket.setTcpNoDelay(true) 来破坏 Fellow 的网络性能,但总时间没有改变。
  • 亚马逊计算机上的 .getTcpNoDelay() 返回错误。 Nagle 的算法可以在 Java 和 OS 级别上实现吗?或者也许我只是在寻找错误的方向。
  • 我相信 Nagle 是在套接字库中实现的(例如 Java 的 NIO,Windows 上的 Winsock),而不是操作系统,而不是硬件。但我可能弄错了。
  • 我想知道您是否看错了方向。是否有直接证据表明网络是瓶颈?你运行过 dstat 吗?也许是CPU?或者,Fellow 有更多的 RAM,而 Amazon 正在分页到磁盘?您能否在排除网络的两种环境中从您的工作人员运行中捕获高精度计时数据以将其排除在外吗?
猜你喜欢
  • 1970-01-01
  • 2011-11-06
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
  • 2018-05-17
  • 2018-02-16
相关资源
最近更新 更多