【问题标题】:Java: multidimensional arrays - which dimension goes first?Java:多维数组 - 哪个维度在前?
【发布时间】:2011-11-06 18:29:09
【问题描述】:

这两个数组在java中是否存在性能差异(可能与J2ME开发有关...):

String[][][] a = //for getting an entry by a[group][person][field]
    {
        { // group A:
            {"John", "Doe", "teacher", "New York"},
            {"Donald", "Duck", "jinx", "Duckburg"},
            // 10 or more further entries
        },
        { // group B:
            {"Barack", "Obama", "president", "Washington"},
            // ...
        }
    };
String[][][] b = //for getting an entry by b[field][group][person]
    {
        { // prenames:
            {"John", "Donald", ...},
            {"Barack", ...}
        },
        { // surnames:
            {"Doe", "Duck", ...},
            {"Obama", ...}
        },
        { // job:
            {"teacher", "jinx", ...},
            {"president", ...}
        },
        { // city:
            {"New York", "Duckburg", ...},
            {"Washington", ...}
        }
    };

我猜第二个数组的性能更高,因为它包含的嵌套数组总数较少,而第一个数组每个人都有一个数组!将其转移到更大的阵列上...

感谢您的回答!

更新:

一个更好(现实)的例子是一个数组,比方说,1000 x/y 坐标:

int[][] coordsA =
    {
        {0, 0},
        {2, 7},
        {8, 2},
        {4, 2},
        {-3, 15},
        {1, 32},
        // ...
    };
int[][] coordsB =
    {
        {0, 2, 8, 4, -3, 1, ...}, // x values
        {0, 7, 2, 2, 15, 32, ...} // y values
    }

【问题讨论】:

  • 数组少了怎么办?您的变量声明类型定义了有多少。无论如何,您有什么理由不为此使用课程?这就是他们的目的。
  • 多维数组并不意味着它的元素是嵌套的,你总是可以直接访问它的属性,例如a[1][2][3]。因此,在这种情况下,性能是您的访问模式的函数...
  • 我会说更少的数组可能会为您节省一些length 字段和指针占用的内存,但我不确定它是否会产生很大的不同。不过,由于缓存,我同意@home 关于访问模式的看法。你可以看看this,虽然我不完全确定它如何应用于移动设备上的Java。
  • @flo:如果您真的想将数组用于您需要做的事情并希望在 J2ME 上获得最佳性能(正如您在问题中所写),那么 the 技巧J2ME 曾经包括展平数组(当然这意味着可能有空单元格)并自己执行“数组索引算术”,节省数组绑定检查(在您的情况下,只进行一项这样的检查而不是三项) .如今,这些技巧不太可能提供可衡量的性能增益,但它曾经在这些旧的笨重 J2ME 手机上很重要:)

标签: java arrays performance multidimensional-array


【解决方案1】:

几年前,当我使用 Fortran 时,我们被告知要在多维数组上安排循环,以便第一个维是迭代最快的那个,因为内存的排列方式,它会导致更少页面错误。

然而,从那以后,我了解到在 Java 中(几乎所有东西),如果您遇到性能问题,您应该衡量所有建议的解决方案,直到找到不再存在性能问题的解决方案。过早的优化是万恶之源。

如果您认为数组的数量会成为问题,您是否测量过每个选项占用的内存量?您是否测量过特定操作所花费的时间。

如果您没有性能问题,请使用最符合您要求的表示。

【讨论】:

  • 你被告知以这种方式排列循环的原因是因为 fortran 使用 column-major ordering 而不是今天更常见的类 c 语言的行主要排序,如果你这样做相反你得到可怕的缓存位置。但是由于java没有多维数组,所以在这里没有太大区别。
  • @Voo 我知道。但关键是当你在做表现时,衡量它。
  • 是的,我们都牢记这句话(尽管我总是觉得它来自认为 C 效率太低的人很有趣),但仍然没有理由选择性能较差的解决方案,如果这是完全相同的努力和复杂性。访问 arr[i][j] 而不是 arr[j][i] 几乎没有问题,并且会在正确的语言中带来巨大的改进。
【解决方案2】:

<insert usual knuth quote, etc. here - but I assume anyone here knows that already anyhow>

Java 的问题在于,每个数组级别都被视为一个对象本身,即我们没有为我们的数组获得连续的内存区域(好吧,GC 可能会有所帮助)

这有几个效果:

  • 更糟糕的缓存位置
  • 每个数组级别的附加边界检查
  • 访问一个元素所需的额外内存访问

不是很好(即在最坏的情况下,我们可能会获得 2*index 级别的内存访问,而不是对一个元素进行一次内存访问),因此如果您需要高效的数组,通常的解决方案是只创建一个大数组并自己做索引。这以需要一些简单的辅助方法为代价避免了所有这些问题(这些方法非常简单,尤其是在所有子数组具有相同大小的情况下)。

性能提升也很大程度上取决于您的访问方案。如果您想比较您帖子中给出的两种不同的变体,它可能不会那么重要(即,如果您正确订购阵列,那么您在 C 或 co 中获得的收益远不及相同)。不过,在它们的子数组中按顺序访问它们仍然应该提供更好的缓存局部性。

【讨论】:

    猜你喜欢
    • 2014-11-29
    • 1970-01-01
    • 2011-01-31
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 2021-04-24
    • 1970-01-01
    相关资源
    最近更新 更多