c ++中的持久数据结构

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

c ++中是否存在类似于clojure中的持久性数据结构实现?

c++ clojure persistence immutability
3个回答
4
投票

我自己动手,但是immer library是一个相当全面的例子,它特别受到了clojure的启发。几年前,我听到约翰卡马克的一次演讲,在那里他跳过了各种功能编程的潮流,让我兴奋不已。他似乎能够想象一个围绕不可变数据结构的游戏引擎。虽然他没有详细说明,虽然它看起来似乎是一个朦胧的想法,但他认真考虑它并且似乎并没有认为开销会急剧降低到帧速率这一事实足以令我兴奋关于探索这个想法。

我实际上将它用作某种优化细节,这可能看似矛盾(存在不可变性的开销),但我的意思是在特定的上下文中。如果我绝对想要这样做:

// We only need to change a small part of this huge data structure.
HugeDataStructure transform(HugeDataStructure input);

...而且我绝对不希望该函数引起副作用,因此它可以是线程安全的并且从不容易被滥用,那么我别无选择,只能复制庞大的数据结构(可能跨越一个千兆字节)。

在那里我发现有一个小的不可变数据结构库在这样的上下文中非常有用,因为它通过浅层复制和引用未更改的部分使上述场景相对便宜。也就是说,我大多只使用一个基本上是随机访问序列的不可变数据结构,如下所示:

enter image description here

正如其他人所提到的,它确实需要一些关心和调整以及全面的测试和许多VTune会议,以使其线程安全和高效,但在我放入肘部油脂之后,它确实使整个事情变得更加容易。

除了自动线程安全之外,每当我们使用这些结构来编写没有副作用的函数时,您还会得到非破坏性编辑,琐碎的撤消系统,琐碎的异常安全(无需通过示波器防护来回滚副作用)在一个函数中,在异常路径中不会导致任何问题),并允许用户复制和粘贴数据并对其进行实例化而不需要占用太多内存,除非他们修改他们粘贴的内容作为奖励。实际上,我发现这些奖金每天比线程安全更有用。

我使用'transients'(又名'builders')来表达对数据结构的更改,如下所示:

Immutable transform(Immutable input)
{
    Transient transient(input);

    // make changes to mutable transient.
    ...

    // Commit the changes to get a new immutable
    // (this does not touch the input).
    return transient.commit();
}

我甚至有一个不可变的图像库,我用它来进行图像编辑,以简化非破坏性编辑。它使用与上述结构类似的策略,将图像视为瓦片,如下所示:

enter image description here

当修改瞬态并获得新的不可变时,只有更改的部分才是唯一的。其余的tile是浅层复制的(只有32位索引):

enter image description here

我确实在网格和视频处理等性能相关的领域中使用它们。关于每个块应该存储多少数据(太多并且我们浪费处理和内存深度复制太多数据,太少而且我们浪费处理和内存浅层复制太多指针和更频繁的线程锁,有一些微调) )。

我没有将这些用于光线追踪,因为这是可以想象的最极端的性能关键区域之一,用户可以注意到最微小的开销(他们实际上是基准并注意到2%范围内的性能差异),但是大多数时间,它们足够高效,当你可以左右复制这些庞大的数据结构以简化线程安全,撤消系统,非破坏性编辑等时,它是一个非常棒的好处,而不用担心爆炸性的内存使用并且明显的延迟花费了深刻的复制一切。


2
投票

获得持久数据结构的主要困难确实是缺乏垃圾收集。

如果你没有适当的垃圾收集方案,那么你可以得到一个糟糕的(即引用计数),但这意味着你需要格外小心不要创建循环引用。

它改变了结构的核心。例如,想想二叉树。如果您创建新版本的节点,则需要其父版本的新版本才能访问它(等等...)。现在,如果关系是双向的(子< - >父),那么实际上你将复制整个结构。这意味着您将拥有父 - >子关系,或相反(不常见)。

我可以想到实现二叉树或B树。我几乎没有看到如何获得一个正确的数组。

另一方面,我同意在多线程环境中拥有高效的产品会很棒。


0
投票

如果我正确地理解了这个问题,你所寻求的是能够复制一个对象,而不是在完成时实际支付复制费用,只有在需要时才能复制。可以在不损坏另一个对象的情况下对任一对象进行更改。这被称为“写入时复制”。

如果这是您正在寻找的东西,可以使用共享指针在C ++中相当容易地实现(参见Boost的shared_ptr,作为一个实现)。最初,副本将与源共享所有内容,但是一旦进行了更改,对象共享指针的相关部分将被指向新创建的深度复制对象的其他共享指针替换。 (我意识到这种解释是模糊的 - 如果这确实是你的意思,那么答案可以详细阐述)。

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