2

我正在将一些代码转换为 Scala。它是位于具有大量数据的内部循环中的代码,因此它需要快速,并且它涉及在哈希表中查找键和计算概率。它需要根据是否找到密钥来做不同的事情。使用“标准”习语,代码看起来像这样:

counts.get(word) match {
  case None => {
    WordDist.overall_word_probs.get(word) match {
      case None => (unseen_mass*WordDist.globally_unseen_word_prob
                    / WordDist.num_unseen_word_types)
      case Some(owprob) => unseen_mass * owprob / overall_unseen_mass
    }
  }
  case Some(wordcount) => wordcount.toDouble/total_tokens*(1.0 - unseen_mass)
}

但我担心这种代码会非常慢,因为所有这些临时 Some() 对象都被创建然后被垃圾收集。Scala2e 书声称智能 JVM“可能”优化这些内容,以便代码在效率方面做正确的事情,但是使用 Sun 的 JVM 真的会发生这种情况吗?有人知道吗?

4

2 回答 2

2

如果您在 jvm 中启用逃逸分析,则可能会发生这种情况,启用:

-XX:+DoEscapeAnalysis

在 JRE 1.6 上。本质上,它应该检测正在创建的对象,这些对象不会逃脱方法激活框架,并在不再需要它们后立即将它们分配到堆栈或 GC 上。

您可以做的一件事是使用scala.testing.Benchmarktrait 对您的代码进行微基准测试。只需使用单例对象扩展它并实现该run方法,编译并运行它。它将run多次运行该方法,并测量执行时间。

于 2011-10-12T09:19:50.290 回答
1

是的,Some将创建对象(None是单例)。当然,除非 JVM 忽略了这一点——这取决于许多因素,包括 JVM 是否认为代码被调用了那么多。

无论如何,该代码并不是真正的标准习语。甚至还有一个梗:有一次,一位经验丰富的 Scala 开发人员编写了这样的代码,而另一位则回答:“这是什么?业余时间?Flatmap 太糟糕了!”

无论如何,这就是我将如何重写它:

( counts 
  get word
  map (_.toDouble / total_tokens * (1.0 - unseen_mass))
  getOrElse (
    WordDist.overall_word_probs
    get word
    map (unseen_mass * _ / overall_unseen_mass)
    getOrElse (unseen_mass * WordDist.globally_unseen_word_prob
                / WordDist.num_unseen_word_types)
  )
)

然后你可以重构它——这两个getOrElse参数可以用不同的方法用好名字分割。因为它们只是返回一个没有输入的值,所以它们应该很快。

现在,我们在Option:mapgetOrElse. 这是他们实施的开始:

@inline final def map
@inline final def getOrElse

由于参数 togetOrElse是按名称传递的,因此它涉及匿名函数的创建。当然,参数 tomap也是一个函数。除此之外,这些方法被内联的机会非常好。

所以,这是重构的代码,虽然我对它的了解还不够,无法给出好名字。

def knownWordsFrequency = counts get word map computeKnownFrequency
def computeKnownFrenquency = 
  (_: Int).toDouble / total_tokens * (1.0 - unseen_mass)

def probableWordsFrequency = (
  WordDist.overall_word_probs 
  get word 
  map computeProbableFrequency
)
def computeProbableFrequency = unseen_mass * (_: Double) / overall_unseen_mass

def unknownFrequency = (unseen_mass * WordDist.globally_unseen_word_prob
  / WordDist.num_unseen_word_types)

def estimatedWordsFrequency = probablyWordsFrequency getOrElse unknownFrequency

knownWordsFrequency getOrElse estimatedWordsFrequency
于 2011-10-12T22:16:57.493 回答