При сравнении производительности сокета Java TCP между RH Linux и Solaris, один из моих тестов был выполнен с использованием Java-клиента, отправляющего строки и чтения ответов с эхо-сервера Java. Я измеряю время, затраченное на отправку и получение данных (т. Е. Обратный цикл и обратно).
Тест выполняется 100 000 раз (большее количество повторений дает аналогичные результаты). Судя по моим тестам, Solaris в среднем на 25/30% быстрее, чем RH Linux, на том же компьютере с настройками системы и сети по умолчанию, такими же аргументами 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(); }
}
}
}
Я использую JRE 1.6.0_18 для обеих ОС на одном двухъядерном Nehalem с тактовой частотой 2,3 ГГц. 2 ОС - это Solaris 10 и RH Linux 5.4 с ядром RT 2.6.24.7.
Большое спасибо.
Solaris 10 имеет довольно впечатляющий сетевой стек. Но я думаю, что вы испытываете оптимизацию обратной связи TCP.
Обычно при выполнении определенных условий обработка протокола отключается для петлевых соединений.
Вы можете прочитать об этом в этом впечатляющем сообщении в блоге: Сеть Solaris 10 - раскрытие магии
В Solaris это называется «TCP Fusion», что означает, что две локальные конечные точки TCP будут «объединены». Таким образом, они полностью обходят путь к данным TCP.
Попробуйте отключить его и снова запустить тест:
# echo do_tcp_fusion/W 0 | mdb -kw do_tcp_fusion: 0x1 = 0x0
Вероятно, вам следует попытаться создать тестовую среду, максимально имитирующую вашу продукцию. Вероятно, это означает настройку реальной сети с сетевыми адаптерами.
Если вы хотите поиграть с дросселированием соединения или другими сложными сетевыми ситуациями, я предлагаю вам разместить блок FreeBSD между двумя конечными точками и поиграть с ipfw / dummynet или pf / altq.