ORC – Nim的新内存管理

2020-12-09 19:48:38

1.4版附带了所谓的ORC内存管理算法.ORC是现有的ARC算法(1.2版中首次提供)以及acycle收集器。这也是名称的由来-“ O”代表循环,“ RC”代表“引用计数”,这是算法的基础。

循环收集器基于Lins和其他人众所周知的“尝试删除”算法。在这里,我不会描述该算法的工作原理-您可以阅读该论文以获得很好的描述。

像往常一样,我忍不住要改进算法并增加更多优化的诱惑:Nim编译器会分析涉及的类型,并且只有当它可能是循环的时,才会生成调用循环收集器的代码。可以通过将类型注释为非循环来帮助进行类型分析。例如,这是如何建模二叉树的方法:

不幸的是,周期收集器的开销是可以测量的。此注释对于使ORC的性能接近ARC至关重要。

ORC设计的创新之处在于,可以在固定时间O(1)内对循环根候选进行注册和注销。结果是,在运行时我们利用Nim中的数据很少是循环的这一事实。

ARC是Nim的纯引用计数GC,但是,许多引用计数操作已被优化:由于移动语义,数据结构的构建不涉及RC操作。而且由于Nim ARC实现的另一项创新“游标推断”,常见的数据结构遍历也不涉及RC操作! ARC和ORC的性能均与堆的大小无关。

为了给自己打个比方,我写了一个简单的基准来展示这些算法上的差异。请注意,基准测试是为了强调ORC与Nim其他GC之间的差异而编写的;它不应该为现实的工作负载建模(还!)。

导入asynchttpserver,asyncdispatch,strutils,json,表,流#约135 MB的实时数据:var会话:i中0的表[string,JsonNode]。 10:会话[$ i] = parseJson(newFileStream(" 1.json",fmRead)," 1.json")var serve = 0 var server = newAsyncHttpServer()proc cb(要求:要求){。异步} = inc待命req。如果服务模块10 == 0,则响应(Http200," Hello World"):未定义(memForSpeed)时:GC_fullCollect()waitFor服务器。服务(端口(8080),cb)

第10-18行是Nim标准库中的“ Hello World”异步HTTP服务器示例。

在第4-6行中,我们将约135MB的JSON数据加载到了全局session变量中.ORC永远不会触及该内存,即使它在程序其余的运行过程中仍然有效。较早的Nim GC确实需要触摸此内存。我将ORC与Nim的“标记并清除” GC(M& S)进行比较,因为M& S在此基准测试中表现最佳。

经常调用GC_fullCollect以使内存消耗接近程序理论上需要的135MB RAM。

没错,ORC比M& S GC快100倍以上。原因是ORC也只触及mutator触及的内存。这是一项关键功能,可以推理现代机器的性能。一个世代的GC可能会提供类似的保证。实际上,ORC可以看作是世代的和增量的GC,并附带保证无环结构一旦成为垃圾就被释放。

现在,M& S在吞吐量方面胜出,但在延迟方面却没有。但是,内存消耗增加到了约330MB。是程序真正需要的两倍多的内存!

ORC总是在延迟和内存消耗方面取胜;与析构函数配合很好,因此可以与自定义内存管理一起使用;与堆大小无关;精确地跟踪堆栈根,并与C / C ++生态系统提供的所有消毒剂一起干净地工作。

这些结果是我们在其他程序中看到的典型结果:延迟减少,抖动少且内存消耗保持接近程序所需的最低要求。嵌入式开发的出色结果!

循环收集算法本身的进一步改进正在开发中;事实证明,GC研究忽略了许多想法。

ORC可与Valgrind和其他C ++消毒剂一起使用(使用--gc:orc -g -d:useMalloc编译以进行精确的Valgrindchecking)。

当内存消耗很重要时,ORC的吞吐量可以快几个数量级。当内存消耗不那么重要时,它的吞吐量可比。

ORC不使用CPU特有的技巧。即使在有限的目标(如Webassembly)上,它也不会受到黑客攻击。

ORC提供亚毫秒级的延迟。它非常适合(硬)实时系统。没有“停止世界”阶段。

如果您喜欢本文以及我们如何发展Nim,请考虑捐赠。您可以通过以下方式捐款:

如果您是一家公司,我们还提供商业支持。 请通过[电子邮件保护]与我们联系。 作为商业支持者,您可以决定应优先考虑哪些功能和错误修正。