在哈希表上使用get()方法时的Scala速度?(会生成临时的Option()对象吗?)



我正在将一些代码转换为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时,这真的发生了吗?有人知道吗?

如果您在jvm中启用转义分析,并使用:

-XX:+DoEscapeAnalysis

在JRE 1.6上。从本质上讲,它应该检测正在创建的对象,这些对象没有逃离方法激活框架,然后在堆栈上分配它们,或者在它们不再需要时立即GC它们。

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

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

无论如何,这段代码并不是真正的标准习惯用法。有一次,一个经验丰富的Scala开发人员写了这样的代码,另一个人回答说:"这是什么?"业余时间?平地图那坨屎!"

无论如何,我是这样重写的:

( 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

由于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

最新更新