在scala combinatorparser中访问位置信息会导致性能下降

sch*_*cht 5 performance parsing scala parser-combinators

我在scala中为我的解析器编写了一个新的组合子.

它是^^组合子的变体,它传递位置信息.但访问输入元素的位置信息确实是性价比.

在我的情况下,解析一个大的例子需要大约3秒没有位置信息,它需要超过30秒.

我编写了一个可运行的示例,其中运行时在访问位置时大约多50%.

这是为什么?我怎样才能获得更好的运行时间?

例:

import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.combinator.Parsers
import scala.util.matching.Regex
import scala.language.implicitConversions
object FooParser extends RegexParsers with Parsers {
  var withPosInfo = false
  def b: Parser[String] = regexB("""[a-z]+""".r)  ^^@ { case (b, x) => b + " ::" + x.toString }
  def regexB(p: Regex): BParser[String] = new BParser(regex(p))
  class BParser[T](p: Parser[T]) {
    def ^^@[U](f: ((Int, Int), T) => U): Parser[U] = Parser { in =>
      val source = in.source
      val offset = in.offset
      val start = handleWhiteSpace(source, offset)
      val inwo = in.drop(start - offset)
      p(inwo) match {
        case Success(t, in1) =>
          {
            var a = 3
            var b = 4
            if(withPosInfo)
            { // takes a lot of time
              a = inwo.pos.line
              b = inwo.pos.column
            }            
            Success(f((a, b), t), in1)
          }
        case ns: NoSuccess => ns
      }
    }
  }
  def main(args: Array[String]) = {
    val r = "foo"*50000000
    var now = System.nanoTime

    parseAll(b, r) 
    var us = (System.nanoTime - now) / 1000
    println("without: %d us".format(us))
    withPosInfo = true
    now = System.nanoTime
    parseAll(b, r)
    us = (System.nanoTime - now) / 1000
    println("with   : %d us".format(us))
  }
}
Run Code Online (Sandbox Code Playgroud)

输出:

没有:2952496我们

用:4591070我们

Rex*_*err 4

不幸的是,我认为您不能使用相同的方法。问题是行号最终实现为每次创建时scala.util.parsing.input.OffsetPosition都会构建每个换行符的列表。因此,如果它最终以字符串输入结束,它将在每次调用时解析整个内容(在您的示例中两次)。有关更多详细信息,请参阅CharSequenceReaderOffsetPosition的代码。pos

您可以做一件事来加快速度:

val ip = inwo.pos
a = ip.line
b = ip.column
Run Code Online (Sandbox Code Playgroud)

至少避免创建pos两次。但这仍然给你留下了很多多余的工作。恐怕要真正解决这个问题,您必须像OffsetPosition自己一样构建索引,只需一次,然后继续引用它。

您还可以提交错误报告/提出增强请求。这不是实现该功能的好方法。