我想每秒实现0.5到100万个远程函数调用。假设我们有一台Central
计算机开始计算,一台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.150ms并且我们运行单线程Worker和1线程Central,则每次迭代将需要~0.150ms。为了提高性能,我在Worker和Central上运行N
个线程,n
- 线程侦听端口2000+n
。每个线程停止后,我们总结结果。
基准
首先,我在我校的学校网络上运行上面的程序。其次,我在两个Amazon EC2 Cluster实例上运行它。结果差距非常大。
所有运行中都 CHUNK_SIZE = 100_000
。
研究员的网络:
我认为3年前它是顶级配置(Xeon E5645)。我相信它针对并行计算进行了大量优化,并且具有简单的LAN拓扑,因为它只有20台机器。
操作系统:Ubuntu
平均ping:~0.165ms
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 Eight Extra Large Instance(cc2.8xlarge)的程序。
OS是一些亚马逊的linux
平均ping:~0.170ms。
结果有点令人失望:
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.170ms =每秒6000个调用=每16秒100_000个调用。 Fellow网络的6秒实际上令人惊讶。
我认为现代网络每秒最大TCP数据包大约为每秒40-70k。 它对应N = 100,时间= 250秒:N * CHUNK_SIZE /时间= 100 * 100_000包/ 250秒= 10_000_000包/ 250秒= 40_000包/秒。
问题是,我的Fellow的网络/计算机配置如何做得很好,尤其是高N值?
我的猜测:将每个4byte请求和4byte响应放入单个数据包是浪费的,因为有大约40字节的开销。将所有这些微小的请求汇集到0.010ms并将它们放在一个大数据包中,然后将请求重新分配到相应的套接字是明智的。 可以在应用程序级别实现池化,但似乎Fellow的网络/操作系统已配置为执行此操作。
更新:我玩过java.net.Socket.setTcpNoDelay(),它没有改变任何东西。
最终目标: 我使用非常大的树来估计数百万变量的方程。目前,具有200_000个节点的树适合RAM。然而,我有兴趣近似方程式,这需要具有数百万个节点的树。它需要几TB的RAM。算法的基本思想是从节点到叶子的随机路径,并沿着它改变值。目前程序是32线程的,每个线程每秒执行15000次迭代。我想将它移动到每秒具有相同迭代次数的集群。
答案 0 :(得分:1)
您可能希望启用Nagle'算法:wikipedia entry。
以下是关于禁用它的链接:Disabling Nagle's Algorithm in linux