繁体   English   中英

检查数组中是否包含值的更有效方法?

[英]More efficient way to check if a value is included in an array?

编写一个进行大量比较的游戏,它是滞后的。 我正在尝试加快速度,对于不同的 xy 和 z 值,这段特定的代码块每帧运行数千次。 有没有更好的方法来检查 xy 和 z 的值在数组中是否有效? (爪哇)

if (x >= 0 && x < blocksArr.length && y >= 0 && y < blocksArr[x].length && z >= 0 && z < blocksArr[x][y].length && blocksArr[x][y][z])

我试过检查 blocksArr[x][y][z].= null 和 blocksArr[x][y][z] != undefined 但都没有给出结果。

我不知道 java,但总的来说,我希望以下内容至少一样快,可能更快。 你的编译器可能会做这样的事情,所以它可能不会更快。 您可以拆分“If”语句,这样它会进行更少的查找。 抱歉,我必须将它写成 C 代码。 你可以得到这个想法并翻译成java。

if (x >= 0 && y >= 0 && z >= 0 && x < blocksArr.length) {
  if (y < blocksArr[x].length && z < blocksArr[x][y].length && blocksArr[x][y][z]) {
    .....
  }
}

假设 user16320675 的评估顺序是正确的,您可以使用一个“if”语句。 我不知道java编译器是如何工作的。

if (x >= 0 && y >= 0 && z >= 0 && x < blocksArr.length && y < blocksArr[x].length && z < blocksArr[x][y].length && blocksArr[x][y][z]) .....

您正在做的是边界检查,以确保索引xyzblocksArr[][][]数组中的有效索引。 在 C 中,这是必要的,因为 C 允许您到数组之外。

然而,Java 会在您每次访问数组时进行边界检查。 所以你正在做一个手动边界检查,然后是另一个检查,再加上 Java 在二维上进行检查,最后 Java 在三个维度上进行另一个检查。

您真正需要做的就是访问数组,让 Java 进行边界检查,并在任何索引超出范围时捕获异常。

    boolean value;

    try
    {
        value = blocksArr[x][y][z];
    }
    catch (ArrayIndexOutOfBoundsException e )
    {
        value = false;
    }

设置 try/catch 块有一些开销,但总体来说应该更快。

通常,在已知u为正的情况下执行t >= 0 && t < u的最有效方法是使用Integer.compareUnsigned(t, u) < 0比较tu的无符号值。 因此,您的if可以更有效地表示为

if (Integer.compareUnsigned(x, blocksArr.length) < 0 &&
    Integer.compareUnsigned(y, blocksArr[x].length) < 0 &&
    Integer.compareUnsigned(z, blocksArr[x][y].length) < 0 &&
    blocksArr[x][y][z])

但是,我认为您将blocksArr表示为 3 维数组确实效率低下并且会导致很多间接性,这极大地阻碍了潜在的性能。 一种更合乎逻辑的方法是将其表示为单个数组,并分别存储长度、宽度和高度。 这将导致您的代码看起来像这样:

if (Integer.compareUnsigned(x, length) < 0 &&
    Integer.compareUnsigned(y, width) < 0 &&
    Integer.compareUnsigned(z, height) < 0 &&
    blocksArr[x * (width * height) + y * height + z])

然而,这将您的块限制为大约 20 亿个元素,要克服此限制,您需要求助于目前处于预览状态的内存访问 API。 它有一个重要的优点,它允许内存块的分配和释放是确定性的,这对于太大的内存块来说更为可取。 使用内存段来表示blocksArr ,您的代码将变为:

if (Long.compareUnsigned(x, length) &&
    Long.compareUnsigned(y, width) &&
    Long.compareUnsigned(z, height) &&
    blocksArr.get(ValueLayout.JAVA_BOOLEAN, x * (width * height) + y * height + z))

而且,由于blocksArr是一个boolean值块,将它们打包,每个元素只占1位,会大大改善内存消耗和缓存压力。 现在的支票可以表示为:

long index = x * (width * height) + y * height + z;
long byteIndex = index >>> 3;
int shift = (int)(index & 7);
if (Long.compareUnsigned(x, length) &&
    Long.compareUnsigned(y, width) &&
    Long.compareUnsigned(z, height) &&
    blocksArr.get(ValueLayout.JAVA_BYTE, byteIndex) & (1 << shift) != 0)

您有一个由三个维度xyz确定的框。

如果这些索引中的任何一个在框的边界之外,则整个if()语句为 false。 所以不需要遍历像blocksArr[x].length这样的东西

另外,想想幕后到底发生了什么。 变量x实际上是指向 memory 中保存值的位置的指针。 要处理x >= 0之类的语句,计算机必须找到x的 memory 位置,获取该值并将其放入其内部寄存器之一,比如寄存器 A。然后将值 0 放入寄存器 B。它比较寄存器 A 和寄存器 B。如果 A > B,则继续下一个操作,否则它再次比较 A 和 B。 如果比较结果为零,则进行下一个操作,否则将操作指针移到if()语句之后。

为了比较x < blocksArr.length ,它再次获取x ,然后获取blocksArr计算length值的偏移量,然后将其获取到寄存器中,在那里它进行比较。

您可以使用一些 static 常量来缩短所有这些。

考虑x >= 0可以重写为x > -1 这可能会将 2 次比较减少为 1 次,因此节省了几个 CPU 周期。

数组构造是不可变的 object,即它在创建后无法更改。 所以数组的长度永远不会不同。 因此,如果您创建了一个大小为 5 的数组,blocksArr.length 将始终返回 5。在比较x < blocksArr.length中,您可以替换为x < 5 ,这意味着 CPU 不需要获取 blocksArr.length,从而节省了 CPU 周期.

因此,您可以将数组创建和 if 语句重写为:

public static final int MAX_X = 10;
public static final int MAX_Y = 15;
public static final int MAX_Z = 20;

public boolean blocksArr[][][] = new boolean[MAX_X][MAX_Y][MAX_Z];
  
.
.
.

  if ( ( x > -1 && x < MAX_X ) && // validate for X dimension
       ( y > -1 && y < MAX_Y ) && // validate for Y dimension
       ( z > -1 && z < MAX_Z ) && // validate for Z dimension
       blocksArr[x][y][z] )       // check value 

我认为这是执行此操作所需 CPU 占用最少的方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM