Обработка больших наборов данных в Java/Clojure: данные littleBig

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

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


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

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

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

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

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

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

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

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

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


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

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

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


РЕДАКТИРОВАТЬ Даунсэмплинга возможна только при графике, а не всегда тогда, в зависимости от гравюры частей. Мне нужен доступ ко всем данным для проведения анализа. (Просто очистив это!) Хотя я должен определенно смотреть на понижающую дискретизацию, я не думаю, что это решит проблемы с моей памятью, поскольку все, что я делаю для графика, опирается на BufferedImage.

Ответ 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Update:

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

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

Ответ 2

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

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

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

Ответ 3

Несколько мыслей:

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

  • У меня возникнет соблазн снизить размер, но при этом нужно поддерживать ленивый доступ к деталям "по запросу", если вам нужно, например, в случае взаимодействия с пользователем. Подобно тому, как карты Google позволяют видеть весь мир и загружают только детали при увеличении....

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