gpt4 book ai didi

Scala 编译器优化以实现不变性

转载 作者:行者123 更新时间:2023-12-04 17:17:18 25 4
gpt4 key购买 nike

scala 编译器是否通过删除对 val 的引用来优化内存使用? s 在一个块内只使用一次?

想象一个对象聚集了一些巨大的数据 - 达到克隆数据或其派生数据的大小可能会为 JVM/机器带来最大的内存量。

一个最小的代码示例,但想象一下更长的数据转换链:

val huge: HugeObjectType
val derivative1 = huge.map(_.x)
val derivative2 = derivative1.groupBy(....)

编译器是否会例如离开 hugederivative1 之后标记为符合垃圾收集条件已计算?或者在退出包装块之前它会保持事件状态吗?

理论上,不变性很好,我个人觉得它很容易上瘾。但是为了适合不能在当前操作系统上逐项进行流式处理的大数据对象 - 我会声称它与合理的内存利用率天生阻抗不匹配,因为 JVM 上的大数据应用程序是不是吗,除非编译器针对这种情况进行优化。

最佳答案

首先:只要JVM GC认为有必要,就会实际释放未使用的内存。所以scalac对此无能为力。

scalac 唯一能做的就是将引用设置为 null,不仅在它们超出范围时,而且在它们不再使用时立即设置。

基本上

val huge: HugeObjectType
val derivative1 = huge.map(_.x)
huge = null // inserted by scalac
val derivative2 = derivative1.groupBy(....)
derivative1 = null // inserted by scalac

根据 this thread在 Scala 内部,它目前是 不是 这样做,最新的热点 JVM 也不提供补救措施。请参阅 scalac 黑客 Grzegorz Kossakowski 的帖子和该线程的其余部分。

对于 JVM JIT 编译器正在优化的方法,JIT 编译器会尽快将引用归零。但是,对于只执行一次的 main 方法,JVM 永远不会尝试对其进行完全优化。

上面链接的线程包含对该主题和所有权衡的非常详细的讨论。

请注意,在典型的大数据计算框架(例如 apache spark)中,您使用的值不是对数据的直接引用。所以在这些框架中,引用的生命周期通常不是问题。

对于上面给出的示例,所有中间值都只使用一次。所以一个简单的解决方案是将所有中间结果定义为 defs。
def huge: HugeObjectType
def derivative1 = huge.map(_.x)
def derivative2 = derivative1.groupBy(....)
val result = derivative2.<some other transform>

一种不同但非常有效的方法是使用迭代器!链接函数,如 mapfilter通过迭代器逐项处理它们,导致没有中间集合被物化......这非常适合场景!这对 groupBy 之类的函数没有帮助但可能会显着减少前一个函数和类似函数的内存分配。以上内容归功于 Simon Schafer。

关于Scala 编译器优化以实现不变性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33853321/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com