Эффективный strtod в Java? - PullRequest
       9

Эффективный strtod в Java?

5 голосов
/ 07 сентября 2011

Итак, у меня есть эта Java-программа, которую я использую для обработки нескольких терабайт данных.Производительность - это проблема.

Я профилировал приложение, и большая часть всех выделений памяти, а также большая часть времени ЦП приходятся на выполнение одной простой операции:

У меня естьмассив символов ASCII.Я знаю, что символы от смещения i до смещения j представляют число с плавающей запятой.Мне нужно извлечь это число с плавающей точкой в ​​double.

Наивный Double.parseDouble(new String(buf, i, j - i)) делает свою работу.Однако именно здесь тратится много времени и выделяется много памяти, вероятно потому, что:

  • new String() создает новый объект, создает внутренний массив char[] и копируетсимволов в массив;
  • Double.parseDouble() создает объект FloatingDecimal и также создает массив char[], также копируя в него символы.

Все эти выделения и все это копирование не являются действительно необходимыми.Могу ли я избежать их?

Что мне действительно нужно, так это strtod -подобная функция, которая будет принимать char[] (или byte[]), а также запуск /завершить смещения и вернуть double.

Есть предложения?Должен ли я выкатить свой собственный?Должен ли я написать оболочку JNI около strtod?Должен ли я использовать библиотеку Java, которая уже существует?

Ответы [ 4 ]

5 голосов
/ 07 сентября 2011

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

Код ядра выглядит следующим образом. Он не обрабатывает показатели, но вы можете добавить это.

@Override
public double read() throws BufferUnderflowException {
  long value = 0;
  int exp = 0;
  boolean negative = false;
  int decimalPlaces = Integer.MIN_VALUE;
  while (true) {
    byte ch = buffer.get();
    if (ch >= '0' && ch <= '9') {
      while (value >= MAX_VALUE_DIVIDE_10) {
        value >>>= 1;
        exp++;
      }
      value = value * 10 + (ch - '0');
      decimalPlaces++;
    } else if (ch == '-') {
      negative = true;
    } else if (ch == '.') {
      decimalPlaces = 0;
    } else {
      break;
    }
  }

  return asDouble(value, exp, negative, decimalPlaces);
}

Полный код

Останавливается, как только получает байт, которого не ожидает, например. , или \n

5 голосов
/ 07 сентября 2011

Я бы посмотрел на источник для java.lang.Double, скопировал код, который делает parseDouble, в свой собственный вспомогательный класс и изменил его так, чтобы он работал на char[] с offset и length напрямую. *

2 голосов
/ 07 сентября 2011

Из любопытства я скопировал функцию strtod в Java и получил ~ 10-кратное ускорение по сравнению с методом Double.parseDouble (String) (даже без создания новых строк в цикле). Но, возможно, этого недостаточно для вашей реализации.

Микро бенчмаркинг дает:

Double.parseDouble (): 1.6M преобразований в секунду
Метод Java strtod (): 10,5 млн. Преобразований в секунду

1 голос
/ 07 сентября 2011

Если вам известна эффективная реализация на C, вы можете написать для нее оболочку с помощью JNI.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...