Java: сокращение потребления памяти двумерными массивами с плавающей точкой (float [][])
У меня есть Java-приложение, которое интенсивно работает с 2D-массивами с плавающей точкой (float[][]), которые на самом деле держат изображения на черном фоне. Оба измерения равны (квадрат) и имеют степень 2 (в основном это 256, 512, 1024), поэтому области, близкие к границам, в большинстве случаев имеют нули.
Имея размеры, равные степени 2, сделано для увеличения производительности (есть некоторое БПФ) и снижения сложности операций над этими массивами, такими как вращение и т. Д. Недавно я столкнулся с нехваткой кучи для этого приложения на моей машине с 6Gb. По моим подсчетам - потребление памяти для этого приложения должно быть примерно до 2-3 Гб, а до 4-5 Гб (смотрите в диспетчере задач Windows). Я использовал профилировщик "YourKit", и он показывает, что эти массивы с плавающей точкой действительно занимают большую часть памяти, однако общий приблизительный размер этих массивов с плавающей запятой должен быть равен 1,3 ГБ (ну, я знаю, что JVM должна решить, как хранить данные, но Я не ожидал 2-3-кратной разницы в потреблении памяти).
Я пытался сжать / распаковать данные с помощью компрессора Snappy на лету (и потребление памяти падает до 3,5 Гб), но производительность падает в несколько раз, что не очень приемлемо. Кроме того, я тестировал производительность при замене этих float [] [] на BufferedImage, но производительность была очень низкой.
Итак, осталось 2 способа, которые помогут мне уменьшить потребление памяти: 1) написать обертки для массива float [] [], чтобы сэкономить на элементах "ноль" (есть много "пустых" строк и столбцов) 2) уйти от "силы 2"
Оба способа требуют довольно много кодирования / рефакторинга, поэтому, пока я думаю "быть или не быть" - может быть, вы лучше разбираетесь в этом вопросе, ребята?
Спасибо!
2 ответа
БПФ требует сложного массива, который в два раза больше реального массива данных, даже если вы преобразуете из реального массива на входе и обратно в массив величин в конце. Это может составлять в 2 раза больше, чем ожидалось, использования памяти.
Разреженный массив не будет работать для БПФ, поскольку промежуточные шаги в БПФ почти всегда будут заполнять весь сложный массив.
Многие современные высокопроизводительные библиотеки FFT, такие как библиотеки, основанные на FFTW, могут очень эффективно работать с длинами FFT, отличными от просто степеней 2 (любая длина, которая является продуктом только простых простых чисел, может быть FFT довольно эффективно). Это может сэкономить много 2D отступов для многих размеров.
После более подробного расследования - оказалось, что JVM запускается с флагами "UnlockEperimentalFeatures" и "use GC1". В результате - было довольно много НЕ собранных мусоров "недоступных" растров BufferedImage (которые содержат байтовые [] массивы). При вызове GC из приофилера "YourKit" эти объекты удаляются из кучи (это, конечно, для меня неприемлемо, так как я ожидал, что JVM сама будет управлять кучей).
Я хочу сказать спасибо всем, кто уделил мне время. Особая благодарность Джиму Гаррисону (похоже, я просто отложил спрос на память на какое-то время, убрав упомянутые выше флаги, но когда будет задействовано больше массивов - покупка большего количества памяти будет самым простым способом избежать снижения производительности.