我必须对Levenshtein距离问题的一个优化的多线程实现进行编程。可以使用带有矩阵的动态编程来计算它,wikipedia page on Levenshtein distance覆盖得足够好。
现在,我可以同时计算对角线元素。没关系。
我的问题现在与缓存有关。通常将c ++中的矩阵逐行保存在内存中,对吗?嗯,这对我不利,因为我需要前一行的2个元素和当前行的1个元素来计算我的结果,这在缓存方面是可怕的。缓存将保存当前行(或其中的一部分),然后我要求它可能不再保存的上一行。 然后,对于另一条,我需要对角线的不同部分,因此,我再次要求完全不同的行,并且缓存将无法为我准备好这些行。
因此,我想将矩阵以块或diagoals的形式保存到内存中。这将减少缓存丢失,并使我的实现再次更快。
您如何做到的?我尝试搜索互联网,但找不到任何能给我指明方向的东西。可以告诉c ++如何在内存中排序该类型吗?
编辑:你们中有些人似乎对我的问题的性质感到困惑。我想以自定义方式将矩阵保存(无论我将其制成2D数组还是其他方式都没有关系)到MEMORY中。通常,一个2D数组将逐行保存,我需要使用对角线,因此高速缓存将在我将要使用的巨大矩阵(可能有数百万行和列)上丢失很多。
答案 0 :(得分:4)
我相信您可能对(CPU)缓存有误解。
确实,CPU缓存是线性的-也就是说,如果您访问内存中的地址,它将把某些先前和某些连续的内存位置带入缓存-就像“猜测”那样,后续访问将涉及一维-close元素。但是,这在微观层面上是正确的。 CPU的高速缓存由大量小的“行”(在最近的Intel CPU中所有高速缓存级别上为64字节)组成。地点仅限于该行;不同的缓存行可能来自内存中完全不同的位置。
因此,如果您“需要矩阵的上一行的两个元素和当前行的一个元素”,则高速缓存应该对您非常有效:某些高速缓存将保存上一行的元素,并且有些将保存当前行的元素。当您前进到下一个元素时,缓存整体通常将包含您需要访问的矩阵元素。只要确保您的迭代顺序与缓存行中的进度顺序一致即可。
此外,在某些情况下,由于从主内存到缓存的映射,不同的线程可能会遇到相同的缓存行。无需详细说明,您需要考虑的是 (但同样,与2D和1D数据无关)。
编辑:如geza所述,如果矩阵行很长,您仍将使用简单的方法读取每个内存位置两次:一次是当前行,然后是上一行-line,因为每个值在用作上一行值之前都会从缓存中逐出。如果要避免这种情况,可以遍历矩阵的 tiles ,其大小(长x宽x sizeof(element))适合L1高速缓存(以及那里需要的所有对象) 。您还可以考虑将数据存储到磁贴中,但是我认为这没什么用。
答案 1 :(得分:0)
我不确定,但是我认为矩阵以长数组的形式存储在另一行之后,并使用指针算法将其映射到矩阵,因此您始终引用相同的地址并在其中计算距离您的值所在的内存
否则,您可以轻松地将其实现为这种类型,并为矩阵实现operator [int,int]
答案 2 :(得分:0)
初步评论:“ Levenshtein距离”是编辑距离(在通用定义下)。这是一个非常普遍的问题。您甚至可能不需要自己编写解决方案。查找现有代码。
现在,最后,对于一个正确的答案……您实际上根本不需要矩阵,并且您当然不需要“保存”矩阵:仅保留矩阵的“前面”就足够了动态编程矩阵而不是整个程序。
但是您应该选择什么“前沿”,以及如何提升它?我建议您使用反对角线作为前面,并给定每个反对角线,同时计算下一个反对角线。因此它将是{(0,0)},然后是{(0,1),(1,0)},然后是{(0,2),(1,1),(2,0)},依此类推上。每个反对角线最多需要两个较早的反对角线-如果我们将每个反对角线的值连续保存在内存中,则下一个反对角线的访问模式将沿着先前的反对角线线性发展-这对缓存非常有用(请参阅我的other answer)。
因此,您将“并行化”计算,为每个线程提供一堆连续的反对角元素以进行计算;这应该够了吧。而且在任何时候,您只会在记忆中保留3个对角线:您正在处理的那个和前两个。您可以在三个这样的缓冲区之间循环,这样就不必一直都在分配内存(但是请确保预先分配具有最大反对角线长度的缓冲区)。
对于非正方形情况,整个过程应该基本相同。