【问题标题】:Java: reducing memory consumption by 2D float (floats[][]) arraysJava:通过 2D float (floats[][]) 数组减少内存消耗
【发布时间】:2013-11-07 19:58:34
【问题描述】:

我有 Java 应用程序,它集中处理 2D 浮点数组(float[][] 数组),它实际上将图像保存在黑色背景上。两个维度都等于(正方形)并且是 2 的幂(大多数是 256、512、1024),因此在大多数情况下,靠近边界的区域为零。

为了提高性能(存在一些 FFT)和降低对这些数组的操作(如旋转等)的复杂性,将大小设置为 2 的幂。 最近,我在 6Gb 的机器上遇到了这个应用程序的堆不足问题。 根据我的计算 - 此应用程序的内存消耗应该高达 2-3Gb,而它达到 4-5Gb(在 Windows 任务管理器中查看)。 我使用了“YourKit”分析器,它表明这些浮点数组确实占用了大部分内存,但是,这些浮点数组的总粗略大小应该是 1.3Gb(嗯,我知道由 JVM 来决定如何存储数据,但是我没想到内存消耗会相差 2-3 倍)。

我尝试使用 Snappy 压缩器即时压缩/解压缩数据(内存消耗降至 3.5Gb),但性能下降了好几次,这不是很可接受。 另外,我在用 BufferedImage 替换那些 floats[][] 时测试了性能,但性能很差。

所以,我还有 2 种方法可以减少内存消耗: 1) 为 float[][] 数组编写包装器以保存“零”元素(有很多“空”行和列) 2)远离“2的幂”

这两种方式都需要大量的编码/重构,所以当我在思考“成为或不成为”时,你们对这个问题可能有更好的线索吗?

谢谢!

【问题讨论】:

  • floats 代表什么?例如,您为什么不能将它们设为ints?
  • 我会更加注意数组的使用方式,以及是否引入了任何临时数组,这将显着提高内存使用率。您可能还想研究一些用于稀疏数组的 Java 类。
  • @Arsen 你如何将 RGB 图像存储在 float[][] 中,我不禁相信你有颜色通道的第三维。向我们展示这样一个数组的实际声明,并评论像素是如何存储在其中的。
  • @Arsen 那么你的数组 三维的吗?这很容易解释意外的内存消耗(java多维数组被实现为数组数组)。由于每个数组都有一些开销(大约 12 个字节),对于最里面的 float[3] 而言,这相当于存储在其中的浮点数所消耗的内存。无论如何,这就是您的代码示例所暗示的。我特别询问了这种数组的实际声明,因为这会明确地清除您真正使用的内存布局。玩猜谜游戏对任何人都没有帮助。使用所有相关详细信息更新您的问题
  • @Arsen Hotlicks 和我对你的解释也有问题:你展示的代码创建了一个每个像素 3 个浮点数 的数组。您告诉我们数组是由 (x, y) 索引的二维数组。这根本不适合,同意吗?然后你说每个像素一个浮点数,在这种情况下,它更没有意义,因为热点指出了精度问题。然后你说你需要一个用于 FFT 的浮点数,但是将 R、G 和 B 放在一起的浮点数几乎不适合 FFT 处理(除了精度问题),因为你需要拆分颜色通道 无论如何在处理之前。尽量减少混乱/困惑

标签: java arrays out-of-memory fft memory-optimization


【解决方案1】:

FFT 需要一个复数数组,其大小是实际数据数组的两倍,即使您在输入处从实数数组转换并在最后转换回幅度数组也是如此。这可能占内存使用量超出预期的 2 倍。

稀疏数组不适用于 FFT,因为 FFT 中的中间步骤几乎总是会填充整个复数数组。

许多现代高性能 FFT 库,例如基于 FFTW 的库,可以非常有效地处理 FFT 长度,而不仅仅是 2 的幂(任何长度只是小素数的乘积都可以被 FFT有效率的)。这可以为多种尺寸节省大量 2D 填充。

【讨论】:

  • true,在进行 FFT 之前(使用 JTransfrom,也尝试过 Cuda) - 我正在将 float[][] 数组转换为 Re 和 Im 值(因此,总数组长度增加了两倍),但这完成了只有一次,在计算相关性之后,我将绝对值和数组缩小到正常大小。 GC 应该收集那些 Re-Im 数组,我在任何地方都没有链接。关于 FFT 库的好处,谢谢
【解决方案2】:

经过更详细的调查 - 似乎 JVM 启动时带有“UnlockEperimentalFeatures”和“use GC1”标志。结果 - 有很多非垃圾收集的“无法访问”的 BufferedImage 栅格(其中包含 byte[] 数组)。从“YourKit”priofiler 调用 GC 时 - 从堆中删除的那些对象(这对我来说当然是不可接受的方式,因为我期望 JVM 会自己管理堆)。

我要感谢所有花时间帮助我的人。 特别感谢 Jim Garrison(看起来我只是将内存需求推迟了一段时间,移除了上面提到的标志,但是当更多的数组开始发挥作用时——购买更多的内存将是避免性能损失的最简单方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-13
    • 1970-01-01
    • 2023-03-18
    • 2015-05-10
    • 1970-01-01
    • 2016-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多