为什么这段Scala代码运行缓慢?

7

我正在运行以下Scala代码:

import scala.util.parsing.json._
import scala.io._

object Main {
        def jsonStringMap(str: String) =
                JSON.parseFull(str) match {
                        case Some(m: Map[_,_]) => m collect {
                                        // If this doesn't match, we'll just ignore the value
                                        case (k: String, v: String) => (k,v)
                                } toMap
                        case _ => Map[String,String]()
                }

        def main(args: Array[String]) {
                val fh = Source.fromFile("listings.txt")
                try {
                        fh.getLines map(jsonStringMap) foreach { v => println(v) }
                } finally {
                        fh.close
                }
        }
}

在我的机器上,处理 http://sortable.com/blog/coding-challenge/ 文件大约需要 3 分钟的时间。我编写的等效 Haskell 和 Ruby 程序只需要不到 4 秒钟的时间。我做错了什么?
我尝试了没有 map(jsonStringMap) 的相同代码,速度非常快,所以 JSON 解析器真的很慢吗?
似乎默认的 JSON 解析器确实很慢,但是我尝试了 https://github.com/stevej/scala-json,虽然速度降至 35 秒,但仍比 Ruby 慢得多。
我现在使用 https://github.com/codahale/jerkson,它甚至更快!我的程序现在在我的数据上只需要 6 秒钟,比 Ruby 慢了 3 秒钟,这可能只是 JVM 启动时的差异。

也许更适合于codereview.stackexchange.com。 - Nettogrof
1
我相信答案很简单,就是“没有人费心编写快速的JSON解析器”。解析器组合器(parser combinators)是其中之一,包括JSON解析器在内,它们的目的是为了方便创建,而不是追求性能。如果你想要速度,最好使用Java JSON库。 - Rex Kerr
3
Lift JSON 应该是快速的,就像在这个问题的答案中所引用的那样:https://dev59.com/0nNA5IYBdhLWcg3whuR_ - Phil
你可以在这里引入整个Liftweb框架:http://liftweb.net/ 或者你可以使用Maven http://www.assembla.com/wiki/show/liftweb/Using_Maven 或者你可以尝试从这里获取jar包 http://www.jarvana.com/jarvana/browse/net/liftweb/,你需要使用正确的JAR来匹配你使用的Scala版本。我使用SBT进行构建而非Maven,这样可以很好地运行,但设置比较复杂。 - Phil
1
如果我们能看到Ruby/Haskell程序,回答会更容易些。 - Daniel C. Sobral
显示剩余3条评论
3个回答

8

3
使用 Jerkson。Jerkson 使用 Jackson,它始终是 JVM 上最快的 JSON 库(特别是在流读写大型文档时)。

根据链接显示,该项目已被放弃。 - null

2
使用我的JSON库,我几乎可以立即解析这两个文件:
import com.github.seanparsons.jsonar._
import scala.io.Source
def parseLines[T](file: String, transform: (Iterator[String]) => T): T = {
  val log = Source.fromFile(file)
  val logLines = log.getLines()
  try { transform(logLines) } finally { log.close }
}
def parseFile(file: String) = parseLines(file, (iterator) => iterator.map(Parser.parse(_)).toList)
parseFile("products.txt"); parseFile("listings.txt")

然而,正如有人提到的那样,将整个内容解析为JSONArray比这种方式更有用,因为这种方式会产生很多单独的行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接