Java: эффективность метода readLine в BufferedReader и возможные альтернативы - PullRequest
12 голосов
/ 07 мая 2010

Мы работаем над уменьшением задержки и повышением производительности процесса, написанного на 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?
  • Что происходит, когда размер входная строка больше размера приемного буфера сокета?

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

Спасибо!

Ответы [ 4 ]

15 голосов
/ 07 мая 2010

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

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

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

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

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

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

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

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

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

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

3 голосов
/ 07 мая 2010

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

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

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

2 голосов
/ 07 мая 2010

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

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

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

0 голосов
/ 09 мая 2013

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

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

...