Подтвердить что ты не робот

Java: эффективность метода readLine BufferedReader и возможных альтернатив

Мы работаем над уменьшением задержки и повышением производительности процесса, написанного на Java, который потребляет данные (строки xml) из сокета с помощью метода readLine() BufferedReader. Данные разделяются концом разделителя строк (\n), и каждая строка может иметь переменную длину (6KBits - 32KBits). Наш код выглядит так:

Socket sock = connection;
InputStream in = sock.getInputStream();
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
...
do 
{
   String input = inputReader.readLine();
   // Executor call to parse the input thread in a seperate thread
}while(true)

Итак, у меня есть пара вопросов:

  • Будет ли метод inputReader.readLine() возвращаться сразу после обращения к символу \n или он будет ждать, пока буфер не будет заполнен?
  • Есть ли более быстрый сбор данных из гнезда, чем с помощью BufferedReader?
  • Что происходит, когда размер входной строки меньше размера буфера приема Socket?
  • Что происходит, когда размер строка ввода больше размера буфера приема Socket?

Я собираюсь (медленно) с Java IO-библиотеками, поэтому любые указатели очень ценятся.

Спасибо!

4b9b3361

Ответ 1

Будет ли метод inputReader.readLine() возвращаться сразу после обращения к символу \n или он будет ждать, пока буфер не будет заполнен?

  • Он вернется, как только он получит новую строку.

Есть ли более быстрый сбор данных из сокета, чем с использованием BufferedReader?

  • BufferedReader влечет за собой некоторое копирование данных. Вы можете попробовать api NIO, который может избежать копирования, но вы можете захотеть профилировать, прежде чем тратить время на это, чтобы убедиться, что это действительно то, что является узким местом ввода-вывода. Более простое исправление заключается в том, чтобы добавить BufferedInputStream вокруг сокета, чтобы каждое чтение не попадало в сокет (он не очищается, если InputStreamReader сам выполняет буферизацию). Например,

    new BufferedReader (новый InputStreamReader (новый BufferedInputStream (in)))

Что происходит, когда размер входной строки меньше размера буфера приема Socket?

  • BufferedReader будет извлекать все данные. Затем он будет проверять эти данные для поиска новой строки. В результате последующие чтения могут уже иметь данные в BufferedReader.

Что происходит, когда размер входной строки больше размера буфера приема Socket?

  • BufferedReader будет считывать то, что находится в буфере приема, и поскольку не существует новой строки или конец потока достигнут, он будет продолжать считывать данные из сокета, пока не найдет EOF или новую строку. Последующие чтения могут блокироваться до тех пор, пока не будет доступно больше данных.

Подводя итог, BufferedReader блокирует только тогда, когда это абсолютно необходимо.

Ответ 2

Одним из преимуществ BufferedReader является то, что он обеспечивает уровень разделения (буфера) между методами ввода (read, readLine и т.д.), которые вы используете, и фактический сокет читает, поэтому вам не нужно беспокоиться обо всех случаях, таких как "большая часть строки находится в буфере, но вам нужно прочитать другой буфер, чтобы получить \n" и т.д.

Выполнено ли измерение производительности, которое указывает, что использование BufferedReader является проблемой производительности для вашего приложения? Если нет, я бы предложил начать с выбора метода ввода, который обеспечивает требуемую функциональность (линейный вход завершается \n, из его звука), и беспокоиться о том, есть ли "быстрый" способ сделать это только если вы обнаружите, что метод ввода является узким местом.

Если вход на основе строки действительно то, что вам нужно, вы собираетесь использовать какой-то буфер, например BufferedReader, поэтому зачем изобретать это колесо?

Ответ 3

Ответ на ваш первый вопрос: да и нет. Если буфер уже содержит терминатор линии, он немедленно вернется, но если он не содержит терминатор, он попытается заполнить буфер, но не обязательно полностью. Он будет считываться только до тех пор, пока не появятся новые данные (по крайней мере один char) или EOF.

Одна из приятных вещей в java заключается в том, что библиотеки имеют открытый исходный код, поэтому, если у вас есть полная копия JDK, вы можете сами найти источник, чтобы ответить на эти вопросы. Я использую eclipse как IDE, и по умолчанию, если вы поместите курсор над именем класса и нажмите F3, он доставит вас к источнику (так я получил ответ выше). Предостережение со стандартным распределением, источник для некоторых внутренних классов/собственный код недоступен.

Для вашего второго вопроса, я бы сказал, вообще нет, поскольку логика, используемая BufferedReader, в целом одинакова, любой код должен будет воссоздать для достижения той же задачи. Единственное, что может замедлить работу BufferedReader, - это внутреннее использование StringBuffer, который синхронизируется вместо несинхронизированного StringBuilder.

Ответ 4

Если вам известно кодирование символов входящих данных, вы можете написать свой собственный класс, который выполняет чтение двоичных данных, и ищет ваш конечный терминатор конца строки. Это может удалить много ненужного кодирования/декодирования и копирования. Убедитесь, что вы реализуете что-то с повторно используемыми буферами (например, классы NIO CharBuffer или ByteBuffer приходят на ум или правильно инициализируются StringBuilder, если вам нужны экземпляры String). Удостоверьтесь, что у вас достаточно свободного места в буфере, 32Ki до 64Ki ничего для текущих компьютеров.

После того, как вы получили данные в пригодном для использования контейнере, вы можете использовать любой трюк в книге (несколько потоков, исполнителей и т.д.) для эффективного управления данными. Помните, что единственный способ замедлить текущий процессор - это использовать кэш-промахи - большие/динамические наборы данных, ложное копирование - или ветки - ненужные циклы, if - операторы и что еще и, конечно, вызовы ядра и ввод-вывод.