Проблема производительности при использовании потоков объектов Javas с сокетами - PullRequest
10 голосов
/ 12 февраля 2010

Я пытаюсь выполнить локальный IPC с использованием сокетов и потоков объектов в Java, однако вижу низкую производительность.

Я проверяю время пинга при отправке объекта через ObjectOutputStream до получения ответа через ObjectInputStream через сокет.

Вот запросчик:

public SocketTest(){

    int iterations = 100;
    try {
        Socket socket = new Socket("localhost", 1212);

        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); 
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); 

        double start = System.currentTimeMillis();
        for (int i = 0; i < iterations; ++i) {

            Request request = new Request();
            objectOutputStream.writeObject(request);

            Response response = (Response)objectInputStream.readObject();
        }
        double finish = System.currentTimeMillis();
        System.out.println("Per ping: " + (finish - start) / iterations );

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

Вот ответчик:

public ServerSocketTest(){

    try {

        ServerSocket serverSocket = new ServerSocket(1212);
        Socket socket = serverSocket.accept();

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());

        Request request = (Request)objectInputStream.readObject();
        while (request != null) {

            Response response = new Response();
            objectOutputStream.writeObject(response);
            request = (Request)objectInputStream.readObject();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Результат, который я получаю:

За пинг: 80,35

80 мсек далеко для медленного местного трафика.

Классы запросов и ответов очень малы, и их сериализация выполняется быстро.

Я попытался наивно добавить:

socket.setKeepAlive(true);  
socket.setTcpNoDelay(true);

с небольшим эффектом.

выполнение ping localhost:

64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=64 time=0.035 ms  
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.037 ms  
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=2 ttl=64 time=0.049 ms  
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=3 ttl=64 time=0.039 ms  
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=4 ttl=64 time=0.056 ms  

тоже быстро.

Java версия 1.6.0_05l Работает на RedHat 2.4

Ответы [ 5 ]

5 голосов
/ 12 февраля 2010

Вы пробовали встраивать как запросы, так и ответы в BufferedInputStream / BufferedOutputStream ? Следует широко улучшить показатели.

1 голос
/ 25 марта 2010

Итак, создайте BufferedOutputStream и очистите его перед созданием BufferedInputStream. Чтобы не висеть.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4788782

сказано в документации

Если вы измените тестовый пример таким образом, чтобы как AServer, так и AClient ObjectOutputStream перед ObjectInputStream, тест не блок. Это ожидаемый поведение с учетом следующего документация:

Конструктор ObjectOutputStream:
Создает ObjectOutputStream, который пишет в указанный
OutputStream. Этот конструктор пишет заголовок потока сериализации в основной поток; абоненты могут хотите промыть поток немедленно убедиться, что конструкторы для получения
ObjectInputStreams не будет блокироваться, когда читая заголовок

Конструктор ObjectInputStream:
Создает ObjectInputStream, который читает из указанного
InputStream. Поток сериализации заголовок читается из потока и проверено. Этот конструктор заблокирует до соответствующего
ObjectOutputStream написал и промыл заголовок.

0 голосов
/ 03 октября 2010

Я бы еще подумал, что это будет быстрее, чем это. Что еще я могу сделать, чтобы улучшить это?

Это похоже на микропроцессор. Их всегда трудно понять правильно, но я думаю, что если вы начнете с отправки, скажем, 2000 сообщений, прежде чем начинать измерения задержки.

Кроме того, см. этот подробный ответ о том, как правильно выполнять микро-тесты.

0 голосов
/ 15 февраля 2010

Помимо использования буферизованных потоков и вызова flush () перед каждым чтением, вы также должны сначала создать ObjectOutputStream с обоих концов. Кроме того, тестирование функции readObject (), возвращающей значение null, бессмысленно, если только вы не планируете вызывать writeObject (null). Тест для EOS с readObject () - это catch (EOFException exc).

0 голосов
/ 12 февраля 2010

Я ожидаю, что вам придется звонить objectOutputStream.flush() с обеих сторон, чтобы обеспечить немедленную отправку данных в сеть. В противном случае стек TCP может подождать, пока больше данных заполнит частичный IP-пакет.

...