Morton-order最近邻搜索的好处?

时间:2010-11-23 19:32:13

标签: algorithm nearest-neighbor spatial-index z-order-curve

在研究粒子相互作用的过程中,我偶然发现了Morton-order(Z-order)(Wikipedia link)中的网格索引,这被认为是提供有效的最近邻小区搜索。我读过的主要原因是内存中空间紧密单元的几乎顺序排序。

在第一次实现的过程中,我无法理解如何有效地为最近邻居实现算法,特别是与基本的统一网格相比。

  1. 给定一个单元格(x,y),获得8个相邻单元格索引并计算相应的z-index是微不足道的。虽然这为元素提供了恒定的访问时间,但是要在预定义的表中计算或查找z-index(对于每个轴和OR'ing分开)。这怎么可能更有效率?是否正确,按顺序访问数组A中的元素说A [0] - >一个1 - > A [3] - > A [4] - > ......比订单A [1023]更有效 - > A [12] - > A [456] - > A [56] - > ...?

  2. 我预计存在一种更简单的算法来查找z顺序中的最近邻居。顺便说一句:找到邻居的第一个单元格,迭代。但这不可能是真的,因为这只能在2 ^ 4大小的块内很好地工作。然而,存在两个问题:当小区不在边界上时,可以容易地确定该块的第一个小区并且遍历该块中的小区,但是必须检查该小区是否是最近邻居。当细胞位于边界上时,情况更糟,而不是必须考虑2 ^ 5个细胞。我在这里错过了什么?是否有一个相对简单而有效的算法可以满足我的需求?

  3. 第1点中的问题很容易测试,但我不熟悉所描述的访问模式生成的基本指令,并且非常希望了解幕后发生的事情。

    提前感谢任何帮助,参考等...

    <小时/> 修改
    谢谢你澄清第1点!因此,使用Z排序,相邻单元的平均缓存命中率会增加,这很有趣。有没有办法分析缓存命中/未命中率?

    关于第2点: 我应该补充一点,我理解如何为R ^ d中的点云构建Morton有序数组,其中索引i = f(x1,x2,...,xd)是从逐位隔行扫描等获得的。我尝试理解是否有比下面的天真ansatz更好的方法来获得最近的邻居(这里是d = 2,“伪代码”):

    // Get the z-indices of cells adjacent to the cell containing (x, y) 
    // Accessing the contents of the cells is irrelevant here
    (x, y) \elem R^2    
    point = (x, y)
    zindex = f(x, y)     
    (zx, zy) = f^(-1)(zindex)          // grid coordinates 
    nc = [(zx - 1, zy - 1), (zx - 1, zy), (zx - 1, zy + 1),  // neighbor grid 
          (zx    , zy - 1),               (zx,     zy + 1),  // coordinates
          (zx + 1, zy - 1), (zx + 1, zy), (zx + 1, zy + 1)]
    
    ni= [f(x[0], x[1]) for x in nc]    // neighbor indices
    

2 个答案:

答案 0 :(得分:9)

在现代多级基于缓存的计算机系统中,空间局部性是优化数据元素访问时间的重要因素。

简而言之,这意味着如果您访问内存中的数据元素,那么访问附近的内存中的另一个数据元素(具有接近第一个的地址)可以比访问数据的几个数量级更便宜很远的元素。

当顺序访问1-d数据时,如简单的图像处理或声音处理,或迭代数据结构以相同的方式处理每个元素,然后按顺序将数据元素排列在内存中往往会实现空间局部性 - 即在访问元素N之后访问元素N + 1时,这两个元素应该在内存中彼此相邻放置。

标准c数组(以及许多其他数据结构)具有此属性。

Morton订购的要点是支持以尺寸方式访问数据两个而非一个的方案。换句话说,在访问元素(x,y)之后,您可以继续访问(x + 1,y)或(x,y + 1)或类似。

莫顿排序意味着(x,y),(x + 1,y)和(x,y + 1)在存储器中彼此接近。在标准的c多维数组中,不一定是这种情况。例如,在数组myArray [10000] [10000]中,(x,y)和(x,y + 1)相距10000个元素 - 相距太远以利用空间局部性。


在Morton排序中,标准c数组仍然可以用作数据的存储,但计算出(x,y)所在位置的计算不再像存储[x + y * rowsize]那么简单

要使用Morton排序实现您的应用程序,您需要弄清楚如何将坐标(x,y)转换为商店中的地址。换句话说,您需要一个函数f(x,y),可以像store[f(x,y)]一样用于访问商店。

看起来你需要做更多的研究 - 按照维基百科页面上的链接,特别是BIGMIN功能上的链接。

答案 1 :(得分:4)

是的,按顺序访问数组元素确实更快。 CPU将内存从RAM加载到缓存中。如果按顺序访问,CPU可以轻松地预加载下一个块,您将不会注意到加载时间。如果您随机访问,则不能。这称为缓存一致性,这意味着访问您已访问的内存附近的内存更快。

在您的示例中,当加载A [1],A [2],A [3]和A [4]时,处理器可能会同时加载其中几个索引,这使得它们非常简单。此外,如果你继续尝试访问A [5],它可以在你操作A [1]时预先加载那个块,等等,使加载时间没有任何效果。

但是,如果加载A [1023],处理器必须加载该块。然后它必须加载A [12] - 它尚未加载,因此必须加载一个新的块。等等,等等。但是,我不知道你的其他问题。