Обработка больших наборов данных в Java / Clojure: данные littleBig - PullRequest
10 голосов
/ 04 августа 2010

Я работал над приложением для построения графиков / обработки данных ( вы можете увидеть скриншот здесь ), используя Clojure (хотя часто мне кажется, что я использую больше Java, чем Clojure), и начал тестировать мое приложение с большими наборами данных. У меня нет проблем с примерно 100 тысячами очков, но когда я начинаю подниматься выше этого уровня, я сталкиваюсь с проблемами кучи.

Теперь, теоретически, около половины ГБ должно быть достаточно для хранения около 70 миллионов дублей. Конечно, я делаю много вещей, которые требуют некоторых накладных расходов, и на самом деле я могу одновременно хранить 2-3 копии данных в памяти, но я еще не оптимизировал много, и 500k или около того все еще * На 1005 * порядков меньше, чем я должен быть в состоянии загрузить.


Я понимаю, что у Java есть искусственные ограничения (которые могут быть изменены) на размер кучи, и я понимаю, что они могут быть частично изменены с помощью параметров, которые вы можете указать при запуске JVM. Это приводит меня к моим первым вопросам :

  • Могу ли я изменить максимально допустимое пространство кучи, если я использую Swank-Clojure (через Leiningen), который JVM имеет при запуске?

  • Если я упакую это приложение (как я планирую) как Uberjar, смогу ли я убедиться, что у моей JVM есть какое-то минимальное пространство кучи?

Но я не довольствуюсь тем, что полагаюсь на кучу JVM для поддержки моего приложения. Я не знаю размера данных, с которыми я мог бы в конечном итоге работать, но он мог достигнуть миллионов пунктов, и, возможно, куча не могла вместить это. Поэтому мне интересно найти альтернативы тому, чтобы просто накапливать данные. Вот несколько идей, которые у меня были, и вопросы о них:

  • Можно ли было бы читать только части большого (текстового) файла за раз, чтобы я мог импортировать и обрабатывать данные в виде "кусков", например, n строк за раз? Если так, то как?

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

  • Могу ли я "пробовать" из файла; например читать только каждые z строк, эффективно уменьшая мои данные?

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

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


Больше всего хотя есть ли лучший способ ? Обратите внимание, что я не могу уменьшить выборку данных основного окна, так как мне нужно иметь возможность обрабатывать их и позволить пользователю взаимодействовать с ними (например, щелкните точку или рядом с ней, чтобы добавить «маркер» к этой точке: этот маркер отображается как вертикальное правило над этой точкой).

Буду признателен за любые идеи, ответы, предложения или исправления! Я также готов разъяснить на мой вопрос любым способом, который вы хотели бы.

Мы надеемся, что это будет, по крайней мере, частично, с открытым исходным кодом; Мне нужен простой в использовании, но быстрый способ создания xy-графиков с большим количеством данных в мире Clojure.


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

Ответы [ 3 ]

7 голосов
/ 04 августа 2010

Могу ли я изменить максимально допустимую кучу пробел, если я использую Swank-Clojure (через Leiningen) JVM имеет при запуске?

Вы можете изменить размер кучи Java, указав при запуске опции -Xms (min heap) и -Xmx (max heap), см. docs .

Таким образом, что-то вроде java -Xms256m -Xmx1024m ... даст начальную кучу 256 МБ с возможностью увеличения до 1 ГБ.

Я не использую Leiningen / Swank, но я ожидаю, что это можно изменить. Если ничего другого, то должен быть скрипт запуска для Java где-нибудь, где вы можете изменить аргументы.

Если я упакую это приложение (как я планирую) как Uberjar, буду ли я в состоянии обеспечить мой JVM какой-то минимальное пространство кучи?

Память контролируется не из файла jar, а из сценария запуска, обычно это файл .sh или .bat, который вызывает java и предоставляет аргументы.

Можно ли "сэмплировать" из файла; например читать только каждые z строк?

java.io.RandomAccessFile предоставляет произвольный доступ к файлу по байтовому индексу, который можно использовать для выборки содержимого.

Можно ли читать только части большого (текстового) файла в время, чтобы я мог импортировать и обрабатывать данные в виде «кусков», например, n строк в время? Если да, то как?

line-seq возвращает ленивую последовательность каждой строки в файле, поэтому вы можете обрабатывать столько раз, сколько пожелаете.

Либо используйте механизмы Java в java.io - BufferedReader.readLine() или FileInputStream.read(byte[] buffer)

Есть ли какой-нибудь более быстрый способ доступа файл, из которого я буду читать (потенциально быстро, в зависимости от реализация), кроме просто читать из него поочередно?

В Java / Clojure есть BufferedReader, или вы можете поддерживать свой собственный байтовый буфер и одновременно читать большие куски.

Чтобы максимально использовать имеющуюся у вас память, сохраняйте данные как можно более простыми.

Для некоторых реальных чисел предположим, что вы хотите отобразить содержимое музыкального CD:

  • CD имеет два канала, каждый с 44 100 сэмплами в секунду.
    • 60 мин. музыки тогда ~ 300 миллионов точек данных
  • Представлено как 16 битов (2 байта, короткий) для каждой точки данных: 600 МБ
  • Представлено в виде примитивного массива int (4 байта на точку данных): 1,2 ГБ
  • Представлено в виде целочисленного массива (32 байта на точку данных): 10 ГБ

Использование чисел из этого блога для размера объекта (16 байтов на объект, 4 байта для примитива int, объекты, выровненные по 8-байтовым границам, 8-байтовые указатели в массиве = 32 байта на Целочисленное назначение данных).

Даже 600 МБ данных - это большой объем памяти, который можно сохранить сразу на «нормальном» компьютере, поскольку вы, вероятно, будете использовать много памяти и в других местах. Но переключение с примитивных чисел в штучной упаковке само по себе сократит количество точек данных, которые вы можете хранить в памяти, на порядок.

Если бы вы отображали данные с 60-минутного компакт-диска на временной шкале общего обзора 1900 пикселей, у вас был бы один пиксель для отображения двух секунд музыки (~ 180 000 точек данных). Это явно слишком мало для того, чтобы показывать какой-либо уровень детализации, вам может потребоваться некоторая форма подвыборки или сводных данных.

Таким образом, решение, которое вы описываете - обрабатывать полный набор данных по одному куску за раз для сводного отображения на временной шкале «обзора» и сохранять в памяти только небольшое подмножество для основного «подробного» окна - звучит совершенно разумно.

Обновление:

При быстром чтении файла: Эта статья * в 1081 * раз увеличивает скорость чтения файла для 13 различных способов чтения 100 МБ файла в Java - результаты варьируются от 0,5 секунды до 10 минут (! ). Как правило, чтение выполняется быстро с приличным размером буфера (от 4 до 8 Кбайт) и (очень) медленно при чтении по одному байту за раз.

Статья также имеет сравнение с C на случай, если кому-то будет интересно. (Спойлер: самые быстрые чтения Java находятся в пределах 2-х коэффициентов от файла с отображением в памяти в C.)

2 голосов
/ 04 августа 2010

Пара мыслей:

  • Лучший способ обработки больших наборов данных в памяти в Java / Clojure - это использование больших примитивных массивов. Если вы делаете это, вы в основном используете только немного больше памяти, чем размер базовых данных. Вы отлично справляетесь с этими массивами в Clojure с помощью функции aget / aset

  • Я бы соблазнился уменьшить выборку, но оставил бы способ ленивого доступа к подробным пунктам «по требованию», если вам нужно, например, в случае взаимодействия с пользователем. Это похоже на то, как карты Google позволяют вам видеть весь мир и загружать детали только при увеличении масштаба ...

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

2 голосов
/ 04 августа 2010

Выбрасывая пару идей из левого поля ...

Вы можете найти что-то полезное в библиотеке Colt ... http://acs.lbl.gov/software/colt/

Или, возможно, ввод-вывод с отображением в памяти.

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