【问题标题】:Is representing a large matrix as a 1D array more efficient than a 2D array?将大矩阵表示为一维数组是否比二维数组更有效?
【发布时间】:2016-07-25 12:21:18
【问题描述】:

上下文

我正在实现seam carving 算法。

我将图片中的像素表示为一维数组

private int[] picture;

每个int代表像素的RGB。

要访问像素,我使用辅助方法,例如:

private int pixelToIndex(int x, int y) {return (y * width()) + x;}

另一种方法是存储在二维数组中:

private int[][] picture;

接缝雕刻算法有两个部分。

首先,它会进行一些图像处理,找到能量最低的水平或垂直连接接缝。在这里,像素访问会跨行跳跃一点。

其次,它会移除这个相连的接缝。

对于垂直接缝,我用-1 标记要删除的像素并创建一个新的图片数组,像这样跳过删除的像素:

int i = 0, j = 0;
while (i < temp.length) {
    if (picture[j] != -1) {
        temp[i++] = picture[j];
    }
    j++;
}
picture = temp;

对于水平接缝,给定一个特定的列,我将该列的已删除像素之后的所有像素向上移动一行,如下所示:

for (int i = 0; i < temp.length; i++) {
    int row = indexToY(i);
    int col = indexToX(i);
    int deletedCell = seam[col];

    if (row >= deletedCell) temp[i] = picture[i + width()];
    else temp[i] = picture[i];
}
picture = temp;

问题

显然,由于每个子数组的开销,一维数组使用较少的物理内存,但考虑到我迭代矩阵的方式,二维数组是否会更有效地被 CPU 缓存,从而更高效?

这些数组在加载到 CPU 缓存和 RAM 中的方式有​​何不同?一维数组的一部分会进入一级缓存吗?一维和二维数组如何加载到内存中?它会取决于数组的大小吗?

【问题讨论】:

  • 为什么不隔离createMatrix()set(m, x, y)get(m, x, y) 后面的所有更改——并对这两个选项(2D 与 1D)进行计时,看看哪个更快?在几分钟内删除此代码以获得最高效的定时替代方案将是微不足道的,并且您可以确定您的选择,因为您可以引用适用于您的用例的实际数据。
  • 你应该考虑一下tucuxi所说的:将它隐藏在某个界面后面。除此之外,还有一个重要提示:布局(1D 与 2D)可能会影响性能,但与 迭代顺序 所暗示的差异相比,这可能可以忽略不计。到目前为止,比“1D vs 2D”更重要的是确保嵌套循环,如for(y) for(x) { { pixelToIndex(x,y); }}(而不是for(x) for(y) ...,参考你建议的pixelToIndex实现!)。
  • 另一个提示,关于一维数组的优势:它们可以很容易地复制到 GPU。 Seam Carving 似乎是可以在 GPU 上很好地实现的任务,如果你想要这个,一维数组更容易处理。 (可能有点牵强,但想提一下)
  • 对每个像素查找使用函数调用 pixelToIndex 的开销将大大超过选择 1D 而不是 2D 可能获得的任何好处。
  • 你确定@bhspencer 吗?

标签: java arrays memory heap-memory seam-carving


【解决方案1】:

一个整数数组就是这样表示的:一个整数值数组。数组数组...增加了一定的开销。所以,简短的回答:在处理真正 大量 的数据时;普通的一维数组是你的朋友。

另一方面:只有在了解瓶颈后才开始优化。您知道,优化内存中的数据结构并没有多大帮助……例如,当您的应用程序花费大部分时间等待 IO 时。如果您编写“高性能”代码的尝试产生了“复杂、难以阅读、因此难以维护”的代码……您可能专注于错误的领域。

此外:具体的性能数字受许多不同变量的影响。所以你想先做分析;看看不同硬件、不同数据集等会发生什么。

还有一个旁注:有时,对于实数运算;在 C++ 中实现某些东西也可以是一个可行的选择,可以通过 JNI 进行调用。这实际上取决于您的问题的性质;使用的频率是多少;用户期望的响应时间;等等。

【讨论】:

  • 这根本不能真正回答问题。当在更通用的级别上迭代多维数组等时,我一直在寻找引擎盖下发生的事情的深入细分。我可以为我的案例做更多的分析,但我正在寻找一些理论。
  • 问题是:使用 java,您没有太多控制权。大多数“相关”方面发生在运行时,并由 JIT 实现为您所做的定义。
【解决方案2】:

Java 具有用于多维数组的数组数组。在您的情况下,int[][]int[] 的数组(当然int[]int 的数组)。因此,矩阵表示为一组行和每行的指针。在这种情况下,这意味着 NxM 矩阵正在为数据和指针数组占用 NxM。

由于您可以将任何矩阵表示为数组,因此以这种方式存储它会减少内存消耗。

另一方面,将二维矩阵表示为数组的情况下的地址操作并不那么复杂。

如果我们假设您有一个 NxM 访问的矩阵和一个大小为 NxM 的数组表示该矩阵,则您可以将 Matrix[x,y] 的元素作为Array[x*n+y] 访问。

Array[i] 很紧凑,它很有可能在 L1 缓存中,甚至在寄存器缓存中。

Matrix[x,y] 需要一次内存读取和添加 Array[x*n+y] 需要一乘一加。

所以,我会把我的两分钱放在Array,但无论如何它必须是tested(不要忘记等待 JIT 编译器的预热时间)

【讨论】:

  • 谢谢 我对上述内容有所了解^^,但总结得很好。但是,我更感兴趣的是迭代顺序如何与两种数据结构交互。不过我同意,这两个选项必须在一天结束时进行分析。
猜你喜欢
  • 2020-03-19
  • 2019-09-14
  • 1970-01-01
  • 1970-01-01
  • 2017-10-11
  • 1970-01-01
  • 2010-11-29
  • 2011-02-28
  • 1970-01-01
相关资源
最近更新 更多