在散列表上使用get()方法时的scala速度? (是生成临时的Option()对象吗?)

时间:2011-10-12 05:06:27

标签: scala optimization jvm hashtable temporary-objects

我正在将一些代码转换为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?有人知道吗?

2 个答案:

答案 0 :(得分:2)

如果您在jvm中启用escape analysis,可能会发生这种情况,启用时为:

-XX:+DoEscapeAnalysis

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

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

答案 1 :(得分:1)

是的,将创建Some个对象(None是单个对象)。当然,JVM除外 - 这取决于很多因素,包括JVM是否认为代码被调用了很多。

无论如何,那段代码并不是真正的标准习语。甚至还有一个关于它的模因:曾经,一位经验丰富的Scala开发人员编写了这样的代码,当另一个人回答“这是什么?业余时间?平面图那个sh * t!”

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

( 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参数都可以用不同的方法拆分。因为它们只返回没有输入的值,所以它们应该非常快。

现在,我们在OptionmapgetOrElse只调用两种方法。这是他们实施的开始:

@inline final def map
@inline final def getOrElse

由于getOrElse的参数是按名称传递的,因此它涉及匿名函数创建。当然,map的参数也是一个函数。除此之外,这些方法内联的可能性非常大。

所以,这是重构的代码,虽然我不太了解它的名字。

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
相关问题