如何以非线性方式在C ++中保存矩阵

问题描述 投票:1回答:3

我必须编写Levenshtein距离问题的优化多线程实现。它可以使用矩阵的动态编程来计算,wikipedia page on Levenshtein distance足够好。

现在,我可以同时计算对角线元素。这一切都没问题。

我现在的问题是缓存。 c ++中的矩阵一行一行地保存在内存中,对吗?嗯,这对我不好,因为我需要前一行的2个元素和当前行的1个元素来计算我的结果,这是可怕的缓存方式。缓存将保存当前行(或其中的一部分),然后我要求前一个可能不再保留的行。然后对于另一个,我需要对角线的不同部分,所以再一次,我要求完全不同的行,缓存将不会为我准备好那些。

因此,我想将块中的矩阵保存到块或对角线中。这将导致更少的缓存未命中,并使我的实现再次更快。

你是怎样做的?我试着在互联网上搜索,但我找不到能给我指路的东西。是否有可能告诉c ++如何在内存中订购该类型?

编辑:有些人似乎对我的问题的性质感到困惑。我想以自定义的方式保存一个矩阵(无论我将它是2D数组还是其他任何方式)都存入MEMORY。通常,2D数组将逐行保存,我需要使用对角线,因此缓存将在我将要工作的巨大矩阵(可能是数百万行和列)上遗漏很多。

performance caching matrix optimization memory-access
3个回答
0
投票

初步评论:“Levenshtein距离”是编辑距离(在通用定义下)。这是一个非常普遍的问题;你甚至可能不需要自己编写解决方案。寻找现有代码。

现在,最后,为了一个正确的答案...你根本不需要一个矩阵,你当然不需要“保存”它:它足以保持你的动态编程矩阵的“前面”而不是整个事情。

但是你选择什么“前线”,你如何推进?我建议你使用反对角线作为前面,并给出每个反对角线,同时计算下一个反对角线。因此它将是{(0,0)},然后是{(0,1),(1,0)},然后{(0,2),(1,1),(2,0)}等等上。每个反对角线最多需要两个早期的反对角线 - 如果我们将每个反对角线的值连续保持在内存中,那么上一个反对角线的访问模式是沿着先前的反对角线的线性进展 - 这对缓存很有用(参见我的other answer)。

所以,你将“计算”计算给每个线程一堆连续的反对角元素来计算;这应该够了吧。并且在任何时候你只会在内存中保留3个反对角线:你正在处理的那个以及之前的两个。您可以在三个这样的缓冲区之间循环,这样您就不会一直重新分配内存(但请确保预先分配具有最大反对角线长度的缓冲区)。

对于非方形情况,整个事情应该基本相同。


4
投票

我相信你可能对(CPU)缓存有误解。

确实,CPU缓存是线性的 - 也就是说,如果你访问内存中的地址,它会将一些先前和一些连续的内存位置带入缓存 - 这就像“猜测”后续访问将涉及一维关闭元素。但是,在微观层面上也是如此。 CPU的缓存由大量小“线”组成(在最近的Intel CPU中的所有缓存级别上为64字节)。该地区仅限于该线;不同的缓存行可以来自内存中完全不同的位置。

因此,如果您“需要矩阵的前一行的两个元素和当前行的一个元素”,那么缓存应该可以很好地为您工作:一些缓存将保存前一行的元素,有些将保留当前行的元素。当你前进到下一个元素时,整体缓存通常会包含你需要访问的矩阵元素。只需确保您的迭代顺序与缓存行中的进展顺序一致。

此外,在某些情况下,由于从主内存到缓存的映射,您可能会面临不同线程正在颠倒相同缓存行的情况。没有详细说明,这是你需要考虑的事情(但同样,与2D与1D数据无关)。

编辑:正如geza所说,如果矩阵的行很长,你仍然会用简单的方法读取每个内存位置两次:一次作为当前行,然后再作为前一行,因为每个值都将从中删除缓存在用作前一行值之前。如果你想避免这种情况,你可以遍历矩阵的瓦片,其大小(长度x宽度x sizeof(元素))适合L1缓存(以及其他需要的东西)。您也可以考虑将数据存储在磁贴中,但我认为这不会太有用。


0
投票

我不是很确定,但我认为矩阵作为一个长数组一个接一个地存储,并用指针算法映射到一个矩阵,所以你总是引用相同的地址并计算你在内存中的距离价值位于

否则,您可以轻松地将其实现为此类型,并为矩阵实现operator [int,int]

© www.soinside.com 2019 - 2024. All rights reserved.