如何在树访问者中进行每节点缓存

时间:2014-06-02 13:10:51

标签: c++ caching c++11 visitor

我有一个应用程序,想要计算通过布尔运算(内部节点)组合的基元树(叶节点)的不同表示(网格,体素化,有符号距离函数......)。

我的第一个方法是为每个不同的表示编写一个带有虚拟getter函数的抽象基类,并在各个节点上缓存中间结果,只要它们的子树没有变化(这将刷新它们的子树)高速缓冲存储器)。

然而,我对树结构与每种不同表示的丑陋耦合感到不满意。为了缓解这个问题,我删除了抽象基类,而是为每个表示设置了一个访问者。

这将树与表示整齐地分离,但让我遇到了我现在需要在其他地方缓存中间结果的问题,这就是我的问题开始的地方。

TL; DR

如何在树的内部节点缓存(任意多种不同类型的)中间值,而不使树依赖于值类型?

我的方法

要求提供两种选择:

  • 将数据存储在树中但使用类型擦除
  • 将数据存储在树外,并以某种方式" connect"它到节点

第一个让我感到困惑的是一些效率问题:我可以轻松地在节点中添加boost::any(或类似的东西)的容器,但是每个访问者都必须在整个容器中搜索它['自己的数据。

第二个中的分离引入了将缓存保持为当前树的最新问题。如果树中有更改(删除,更改节点),则缓存的值必须至少无效。我的直觉是使用一些哈希函数和unordered_map,但我也遇到了一些问题:

  • 我不能将treenodes本身用作键,所以我需要引入另一个只引用树节点并在树中表示它们的类
  • 引用unordered_map键中的值需要删除所有删除了引用的条目,或者我们在unordered_map中有一个可能被触发的悬空引用(/指针)重新散列
  • 树中的更改需要重建unordered_map,因为密钥可能已更改

我错过了一些明显的解决方案吗? 你喜欢哪种方法(以及为什么)?

1 个答案:

答案 0 :(得分:2)

我曾遇到类似的问题,我的解决方案如下:

  1. 让每个节点都有唯一的标识符。
  2. 让每个节点都有一个版本号。使节点的计算值无效的修改只会增加版本号。
  3. 让每个访问者都有一个缓存地图,其中ID对是密钥,映射到版本/值对。
  4. 当(重新)走树时,在地图中查找节点的条目。如果版本正确,请使用缓存的值。如果它已过时,请计算新值并替换旧版本/值对。
  5. 首先,我使用节点的地址作为ID,但出于内存原因,我不得不重复使用子树并选择路径作为ID。这样的路径具有以下优点:可以由每个访问者计算并且不需要存储在节点处。在我的例子中,每个节点最多可以有两个子节点,因此路径只是一组左/右决策,可以存储在一个简单的无符号整数中,并带有一些位移(我的树永远不会达到32的深度) ,所以一个32位无符号绰绰有余的关键)。