【问题标题】:Is there a data structure with these characteristics?有没有具有这些特征的数据结构?
【发布时间】:2011-04-02 18:42:35
【问题描述】:

我正在寻找一种数据结构,它允许我在内存中连续存储一个M-by-N 2D 值矩阵,以便内存中任意两点之间的距离近似于之间的欧几里得距离矩阵中的那些点。也就是说,在作为M * N 元素的一维数组的典型行主要表示中,同一行中的相邻单元 (1) 和相邻行中的相邻单元 (N) 之间的内存距离不同。

我想要一种可以减少或消除这种差异的数据结构。真的,这样一个结构的名字就足够了——我可以自己实现它。如果答案碰巧涉及这类事情的库,那也是可以接受的,但它们应该可以与 C++ 一起使用。

我有一个应用程序需要在没有硬件加速的情况下执行快速图像卷积,虽然我知道这类事情的常用优化技术,但我觉得有一种专门的数据结构或数据排序可以提高性能。

【问题讨论】:

  • @Martin:不,这是他想要避免的。
  • @Martin, @Oli:当然不是功课。不仅我现在不上课,而且我大学的任何班级都不会布置如此有用的家庭作业。你认为我在哪里回答了我自己的问题?
  • @Larry:好点子。我认为任何拥有 1840 代表的 SO 用户都不会首先了解计算机。
  • @Larry:我一直在放暑假,直到 9 月初。你可以认为你喜欢我什么,你是否正确完全取决于你。我不需要得到社区成员的认可,尤其是那些似乎不尊重我的人。我真正需要的是解决我当前的问题,学习新的东西,享受编程的乐趣,就像我一生中大部分时间都在做的那样。让我们都文明一点。
  • @Larry:我同意 SO 声誉本身并不是衡量能力的可靠指标。无论如何,由于内存和处理器的相互作用,当前计算机中的内存绝对不是真正的随机访问:编写糟糕的代码太容易导致几乎每次操作都会导致缓存未命中,这可能会使性能下降一个数量级。这基本上是一个优化问题,而这个问题本身就暴露了我对计算的某种基本误解的想法简直是荒谬的。

标签: c++ data-structures image-processing matrix


【解决方案1】:

我猜“不”!如果答案恰好是“是”,那么它几乎可以肯定是非常不规则的,以至于对于卷积类型的操作来说会慢很多。

编辑

为了证明我的猜测,举个例子。假设我们首先存储a[0][0]。我们希望a[k][0]a[0][k] 的距离相似,并且与k 成正比,因此我们可能会选择交错存储第一行和第一列(即a[0][0], a[1][0], a[0][1], a[2][0], a[0][2] 等)但是我们现在该怎么做例如,相同a[1][0]?内存中它附近的所有位置现在都被a[0][0] 附近的东西占据了。

虽然除了我的示例之外还有其他可能性,但我敢打赌你总是会遇到这种问题。

编辑

如果您的数据是稀疏的,那么可能有做一些聪明的事情的空间(re Cubbi 对 R-trees 的建议)。但是,它仍然需要不规则的访问和指针追踪,因此对于任何给定数量的点,它都比直接卷积要慢得多。

【讨论】:

    【解决方案2】:

    您可以将二维矩阵想象成一个大螺旋,从中心开始向外延伸。展开螺旋,并按该顺序存储数据,地址之间的距离至少模糊地近似于它们所代表的点之间的欧几里得距离。虽然它不会很准确,但我很确定你也不能做得更好。同时,我认为即使在最好的情况下,它对您的卷积代码的帮助也很小。

    【讨论】:

      【解决方案3】:

      这听起来像是R-tree. 或其变体之一可以提供帮助。 C++ 标准库中没有类似的东西,但看起来在 boost 候选库 Boost.Geometry 中有一个 R-tree(还不是 boost 的一部分)。在写我自己的之前,我会先看看。

      【讨论】:

      • +1 用于链接到 R-trees,这导致我使用 Hilbert R-trees,这导致我使用 Hilbert ordering。
      【解决方案4】:

      鉴于您希望将值连续存储在内存中的要求,我强烈建议您研究space-filling curves,尤其是Hilbert curves

      为了提供一些上下文,此类曲线有时用于数据库索引中以提高多维范围查询的局部性(例如,“查找此矩形中具有 x/y 坐标的所有项目”),从而旨在减少数量访问的不同页面。有点类似于这里已经建议的 R-trees。

      无论哪种方式,看起来您都绑定到内存中的 M*N 值数组,所以整个问题是关于如何排列该数组中的值,我想。 (除非我误解了这个问题。)

      所以事实上,这样的排序可能仍然只会改变距离分布的特征。从矩阵中随机选择的任意两个点的平均距离不应该改变,所以我必须同意 Oli 的观点。我想,潜在的好处很大程度上取决于您的具体用例。

      【讨论】:

      • 我刚刚看到希尔伯特曲线文章。看来这可能只是门票。你是对的,问题在于顺序,而不是严格来说,数据结构。我想知道与希尔伯特排序之间的转换是否会太慢,但对于我的应用程序来说,卷积本身要快更重要。我将对此进行试验。
      • 您可能不需要完整的希尔伯特曲线。您可以通过位交错获得大部分好处(这可以通过使用查找表来查找您的坐标并将结果组合在一起来完成)。此外,无论您使用什么曲线,请注意您可以使用排序来利用新的空间一致性:以曲线顺序遍历数组将比使用原始行顺序更好地利用缓存。
      • 同意 ig2r 的编辑。对于您移动到“正确”距离的每个坐标,您已经将另一个坐标移动到了错误的坐标中。虽然我了解空间填充曲线用于数据索引(您只是在寻找局部性的高概率)之类的事情,但在卷积之类的事情中,访问模式是确定性的。但是,如果您确实发现了改进,我很想听听(卷积是我经常做的事情!)。
      【解决方案5】:

      不可能将 2D 结构“线性化”为 1D 结构并在两个方向上保持邻近关系不变。这是世界的基本拓扑性质之一。

      既然如此,当您需要(尽可能地)保持接近度时,通常用于 2D 数组表示的标准逐行或逐列存储顺序确实不是最好的存储顺序。您可以通过使用分形曲线(空间填充曲线)的各种离散近似来获得更好的结果。

      Z 阶曲线是此应用程序的常用曲线:http://en.wikipedia.org/wiki/Z-order_(curve)

      请记住,无论您使用哪种方法,总会有一些元素违反您的距离要求。

      【讨论】:

      • 感谢您的链接。我知道完全保留元素之间的邻近关系是不可能的,但我只是在寻找平均情况下的改进,以及相对较大的输入。
      【解决方案6】:

      您可能会查看空间填充曲线,特别是 Z 阶曲线,它(大部分)保留了空间局部性。但是,查找索引可能在计算上很昂贵。

      如果您使用它来尝试提高缓存性能,您可以尝试一种称为“变砖”的技术,它有点像一两级空间填充曲线。本质上,您将矩阵细分为 nxn 个图块(其中 nxn 恰好适合您的 L1 缓存)。您还可以存储另一个级别的切片以适应更高级别的缓存。与空间填充曲线相比,它的优点是索引可以相当快地计算。此处的论文中包含一个参考:http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.8959

      【讨论】:

      • 第一次听说变砖,你一定会喜欢这个网站...谢谢马修!
      【解决方案7】:

      我认为您忘记了计算机内存中的距离不会被步行操作的计算机 cpu 访问:) 所以距离几乎无关紧要。

      它是随机存取存储器,所以你真的必须弄清楚你需要做什么操作,并为此优化访问。

      【讨论】:

        【解决方案8】:

        答案是否定的。想想看——记忆是一维的。你的矩阵是二维的。你想在没有损失的情况下压缩那个额外的维度吗?这不会发生。

        更重要的是,一旦你离开了一定的距离,加载到缓存中需要同样的时间。如果您有缓存未命中,那么它是 100 距离还是 100000 距离都没关系。从根本上说,您无法获得比简单数组更连续/更好的性能,除非您想为您的数组获取 LRU。

        【讨论】:

        • 但是如果从一个内存位置到另一个内存位置太远,cpu 可能会感到疲倦,这不会减慢它的速度吗? jk
        【解决方案9】:

        您需要将地址从内存空间重新转换为原始数组空间来完成此操作。另外,你只强调了距离,这仍然可能会给你带来一些问题(没有方向)

        如果我有一个 R x C 数组,并且在位置 [r,c] 和 [c,r] 有两个单元格,则到某个任意点的距离,例如 [0,0] 是相同的。而且你不可能让一个内存地址拥有两件东西,除非你有一个新奇的量子比特机器。

        但是,您可以考虑在 R x C 的行主数组中,每行的长度为 C * sizeof(yourdata) 字节。反之,可以说数组边界内任意内存地址的原始坐标为

        r = (地址/C) c = (地址%C)

        所以

        r1 = (address1 / C)

        r2 = (address2 / C)

        c1 = (address1 % C)

        c2 = (address2 % C)

        dx = r1 - r2

        dy = c1 - c2

        dist = sqrt(dx^2 + dy^2)

        (这是假设您使用的是基于零的数组) (将所有这些混合在一起以使其运行得更优化)

        这里有更多的想法,去寻找任何使用称为“步幅”的计算值的 2D 图像处理代码,这基本上表明它们在内存地址和数组地址之间来回跳转

        【讨论】:

          【解决方案10】:

          这与亲密关系并不完全相关,但可能会有所帮助。它当然有助于最小化磁盘访问。

          获得更好的“接近度”的一种方法是平铺图像。如果你的卷积核小于一个瓦片的大小,你通常最多触摸 4 个瓦片。您可以递归地平铺更大的部分,以便改进本地化。类似斯托克斯(至少我认为是斯托克斯)的论点(或一些变分法)可以表明,对于矩形来说,最好的(用于检查任意子矩形的意思)形状是具有相同纵横比的较小矩形。

          快速直觉 - 考虑一个正方形 - 如果您将较大的正方形与较小的正方形平铺,那么正方形包围给定周长的最大面积这一事实意味着正方形瓷砖的边界长度最小。当你变换大正方形时,我认为你可以展示你应该以同样的方式变换瓷砖。 (或许也能做一个简单的多元微分)

          经典示例是放大间谍卫星数据图像并对其进行卷积以进行增强。如果您保留数据并返回到它,那么平铺的额外计算确实是值得的。

          对于余弦变换等不同的压缩方案,它也非常值得。 (这就是为什么当您下载图像时,它经常出现在越来越小的方块中,直到达到最终分辨率。

          有很多这方面的书籍,它们很有帮助。

          【讨论】:

            猜你喜欢
            • 2017-06-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多