Java 中 RH Linux 和 Solaris 之间的 TCP 性能差异?

Java 中 RH Linux 和 Solaris 之间的 TCP 性能差异?

在比较 RH Linux 和 Solaris 之间的 Java TCP 套接字性能时,我的一项测试是使用 Java 客户端发送字符串并读取 Java 回显服务器的回复来完成的。我测量了发送和接收数据所花费的时间(即回环往返)。

测试运行了 100,000 次(更多次测试给出的结果相似)。根据我的测试,Solaris 平均比 RH Linux 快 25/30%,在同一台计算机上使用默认系统和网络设置、相同的 JVM 参数(如果有)等。

我不明白如此大的差异,是不是我遗漏了一些系统/网络参数?

如果有人有兴趣运行它,所使用的代码(客户端和服务器)显示如下(必须在命令行中给出出现次数):

import java.io.*;
import java.net.*;
import java.text.*;

public class SocketTest {

public final static String   EOF_STR = "EOF";
public final static String[] st      = {"toto"
    ,"1234567890"
    ,"12345678901234567890"
    ,"123456789012345678901234567890"
    ,"1234567890123456789012345678901234567890"
    ,"12345678901234567890123456789012345678901234567890"
    ,"123456789012345678901234567890123456789012345678901234567890"
};

public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
    double mean = 0.0;
    int port = 30000;
    int times = Integer.parseInt(args[0]); 
    String resultFileName = "res.dat"; 
    new EchoServerSimple(port);        // instanciate and run
    Socket s = new Socket("127.0.0.1", port);        
    s.setTcpNoDelay(true);        
    PrintWriter pwOut = new PrintWriter(s.getOutputStream(), true);
    BufferedReader brIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
    long[] res = new long[times];

    int j = 0;
    for(int i = 0; i < times; i++) {
        if(j >= st.length) j = 0;            
        long t0 = System.nanoTime();
        pwOut.println(st[j++]);
        brIn.readLine();
        res[i] = System.nanoTime() - t0;
        mean += ((double)res[i]) / times;
    }
    pwOut.println(EOF_STR);
    s.close();
    print(res, resultFileName);
    System.out.println("Mean = "+new DecimalFormat("#,##0.00").format(mean));
}

public static void print(long[] res, String output) {
    try {        
        PrintWriter pw;
        pw = new PrintWriter(new File(output));            
        for (long l : res) {
            pw.println(l);
        }
        pw.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

static class EchoServerSimple implements Runnable {

    private ServerSocket _serverSocket;

    public EchoServerSimple(int port) {
        try { _serverSocket = new ServerSocket(port); } 
        catch (IOException e) { e.printStackTrace(); }
        new Thread(this).start();
    }

    public void run() {
        try {
            Socket clientSocket = _serverSocket.accept();
            clientSocket.setTcpNoDelay(true);    
            PrintWriter pwOut = new PrintWriter(clientSocket.getOutputStream(), true);
            BufferedReader brIn = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));                
            try {
                while(true) {
                    String s = brIn.readLine();
                    pwOut.println(s);
                    if(s.equals(EOF_STR)) { clientSocket.close(); break;    }
                }
            } catch (Exception e) {
                e.printStackTrace(); 
                try { clientSocket.close(); } catch (IOException e1) { e1.printStackTrace(); }
            }
        } catch (IOException e) {e.printStackTrace(); }
    }
  }
}

我在一台 2.3GHz 双核 Nehalem 上为两个操作系统使用了 JRE 1.6.0_18。这两个操作系统分别是 Solaris 10 和 RH Linux 5.4,带有 RT 内核 2.6.24.7。

多谢。

答案1

在 Solaris 中,这称为“TCP 融合”,这意味着两个本地 TCP 端点将被“融合”。因此,它们将完全绕过 TCP 数据路径。

尝试禁用它并再次运行测试:

# 回显 do_tcp_fusion/W 0 | mdb -kw
do_tcp_fusion:0x1 = 0x0

您可能应该尝试创建一个尽可能接近您产品的测试环境。这可能意味着设置一个带有网络适配器的真实网络。

如果您想尝试连接限制或其他复杂的网络情况,我建议您在两个端点之间放置一个 FreeBSD 盒,并使用 pf/altq 的 ipfw/dummynet 进行尝试。

答案2

Solaris 10 拥有非常令人印象深刻的网络堆栈。但我认为您遇到的是 TCP 环回优化。

基本上,当满足某些条件时,环回连接的协议处理将被禁用。

您可以在这篇令人印象深刻的博客文章中阅读相关内容:Solaris 10 网络 – 揭秘魔法

相关内容