Scala 需要帮助找出性能瓶颈吗

Scala 需要帮助找出性能瓶颈吗,scala,Scala,我正在开发scala chops,并实现了一个小的graph Api来跟踪添加到graph中的顶点和边。我有基本的图形特征,有一个无向图类(UnDiGraph)和一个有向图类(DiGraph),它们扩展了图形特征。下面是一些清单 trait GraphLike[T] { val vertices: Map[T, VertexLike[T]] def addEdge( a:T, b:T ): GraphLike[T] def addVertex( t:T ): GraphL

我正在开发scala chops,并实现了一个小的graph Api来跟踪添加到graph中的顶点和边。我有基本的图形特征,有一个无向图类(
UnDiGraph
)和一个有向图类(
DiGraph
),它们扩展了
图形特征。下面是一些清单

trait GraphLike[T] {
    val vertices: Map[T, VertexLike[T]]
    def addEdge( a:T, b:T ): GraphLike[T]
    def addVertex( t:T ): GraphLike[T]
    def addVertex( vert: VertexLike[T] ): GraphLike[T]
    def adjacency( t:T ): Option[ List[T] ]  =
    {
      if ( vertices contains t )
        Some( vertices(t).adjList )
      else
        None
    }
    def vertNum: Integer = vertices size
    def edgeNum: Integer = 
    {
      def summer( sum: Integer, ms: Map[T, VertexLike[T] ] ): Integer =
      {
        if ( ms.isEmpty )
          sum
        else
          summer( sum + ms.head._2.adjList.size, ms.tail )
      }
      summer( 0, vertices )
    }
    def getVertex( t: T ): VertexLike[T] =
    {
      vertices( t )
    }
    def edgeExists( a:T, b:T ): Boolean =
    {
      try
      {
          if( vertices( a ).adjList contains b )
            true
          else
            false
      }catch {
        case ex: NoSuchElementException => false 
      }
    }
}
这是直接图的样子

class DiGraph[T](val vertices: Map[ T, VertexLike[ T ] ] = Map.empty ) extends GraphLike[T] {
    def makeVertex( t:T ): VertexLike[T] = new Vertex( t )

    def addEdge( a:T, b:T ): GraphLike[T] =
    {
      //Make sure vertices exist
      if( edgeExists(a, b) )
        this
      else {
        try {
          vertices(b)
          vertices(a)
        } catch {
          case ex: NoSuchElementException => println("Vertices not Found"); this
        }
        addVertex( vertices( a ) + b )
      }
    }
    def addVertex( t:T ): DiGraph[T] = 
    {
      if( vertices contains t ) this
      else
      new DiGraph[T]( vertices + ( t -> makeVertex(t) ) )
    }
    def addVertex( vert: VertexLike[T] ): DiGraph[T] =
    {
      new DiGraph[T]( vertices + ( vert.apply -> vert ) ) 
    }
}
顶点存储在从类型T到类顶点[T]的贴图中。类顶点基本上保留特定顶点的邻接列表。下面是顶点的样子:

trait VertexLike[T] 
{
  def addEdgeTo( dest: T ): VertexLike[T]
  def adjList: List[T]
  def +( dest: T) = addEdgeTo(dest)
  def apply: T
} 

class Vertex[T](t: T, adj: List[T] = List() ) extends VertexLike[T]
{
    def apply() = t
    def adjList = adj
    def addEdgeTo( dest: T ) = 
      if( adjList contains dest )
        this
      else
        new Vertex[T]( t, dest :: adjList )
}
(是的……我意识到类中的apply方法是无用的,它只对对象有效。稍后我才意识到)

无论如何,我有一个样本图,其中我有大约80000个顶点。将顶点添加到图形中的时间太长了。我试着以一种不变的方式做一些功能性的事情。无论何时向图中添加顶点或边,都会得到一个新的图(我试图确保图类型的构造函数做得不多)。这是我用来从数据创建图形的客户机代码

def GraphInstantiater: GraphLike[Int] =
{
  println( "Total number of Vertices: " + synMap.keys.size )
  def vertexAdder( ls: Iterable[Int], graph:GraphLike[Int] ): GraphLike[Int] = 
    if( ls.isEmpty) graph else vertexAdder( ls.tail, graph.addVertex( ls.head ) ) 

  val gr = vertexAdder( synMap.keys, new DiGraph[Int]( Map() ) )
  println( "Vertices added. Total: %d".format( gr.vertices.size ) )
  gr
}

我知道构建新的图需要周期,但考虑到我在构造函数中做的不多,这真的很好吗。重复创建顶点贴图会导致问题(它是graph类的参数之一)。任何关于这种方法中的瓶颈是什么的想法都将不胜感激。另外,如果您需要任何其他信息,请告诉我。

哦,哇。。。我知道发生了什么事。在GraphInstantiater方法中,传递synMap.keys的第一个调用keys返回一个iterable[Int]。看起来跟踪这是一个很长的过程,很可能每次都要经历一整套关键点

将呼叫更改为

val gr = vertexAdder( synMap.keys.toList, new DiGraph[Int]( Map() ) )

让一切都变得更快。有人知道当您在地图上调用
键时返回的容器的底层实现是什么吗?

噢,哇。。。我知道发生了什么事。在GraphInstantiater方法中,传递synMap.keys的第一个调用keys返回一个iterable[Int]。看起来跟踪这是一个很长的过程,很可能每次都要经历一整套关键点

将呼叫更改为

val gr = vertexAdder( synMap.keys.toList, new DiGraph[Int]( Map() ) )

让一切都变得更快。有人知道当你在地图上调用
keys
时返回的容器的底层实现是什么吗?

作为对你答案的补充:每次调用
ls.tail
时,你确实无意中遍历了整个
synMap.keys

发生的情况是:

  • Map.key
    返回
    Map.keySet
    的值,该值是一个值
  • Set
    覆盖了一些内容,但将
    tail
    drop
    保留为默认实现。它的
    tail
    实现(from)只调用
    drop
  • 这就是一切都不一样的地方:它从中获得了
    drop
    的实现,而这只做了
    Iterable
    :iterate可以做的事情。因此,将创建一个新的生成器,删除迭代器的头部,然后将迭代器添加到生成器中,将遍历所有键,并返回一个新集合(尾部)
通过使用迭代器,您可以完全避免转换为列表,例如:

def vertexAdder( ls: Iterator[Int], graph:GraphLike[Int] ): GraphLike[Int] = {
  if(!ls.hasNext) 
    graph 
  else
    val h = ls.next
    vertexAdder( ls, graph.addVertex(h) ) 
}
然后:

val gr = vertexAdder( synMap.keysIterator, new DiGraph[Int]( Map() ) )

作为旁注,有点遗憾的是,
Set
没有提供自己版本的
tail
。它可能只是取自己迭代器的头部,然后返回自己减去该元素的值。

作为对您答案的补充:每次调用
ls.tail
时,您确实会无意中遍历整个
synMap.keys

发生的情况是:

  • Map.key
    返回
    Map.keySet
    的值,该值是一个值
  • Set
    覆盖了一些内容,但将
    tail
    drop
    保留为默认实现。它的
    tail
    实现(from)只调用
    drop
  • 这就是一切都不一样的地方:它从中获得了
    drop
    的实现,而这只做了
    Iterable
    :iterate可以做的事情。因此,将创建一个新的生成器,删除迭代器的头部,然后将迭代器添加到生成器中,将遍历所有键,并返回一个新集合(尾部)
通过使用迭代器,您可以完全避免转换为列表,例如:

def vertexAdder( ls: Iterator[Int], graph:GraphLike[Int] ): GraphLike[Int] = {
  if(!ls.hasNext) 
    graph 
  else
    val h = ls.next
    vertexAdder( ls, graph.addVertex(h) ) 
}
然后:

val gr = vertexAdder( synMap.keysIterator, new DiGraph[Int]( Map() ) )

作为旁注,有点遗憾的是,
Set
没有提供自己版本的
tail
。它可能只是取自己的迭代器的头部,然后返回减去该元素的值。

如果图形的所有元素都是永久不变的,并且您通过添加/更新/删除节点来创建一个新图形,然后,您可以通过1)仅创建受更改影响的新节点2)否则所有内容都将引用原始图形来创建新图形。与写时复制语义类似的概念他已经在做(他使用的是不可变映射,通过引用共享它们的公共元素)应该发布在中吗?我不知道codereview。。。我将在那里发布。。。谢谢…如果图形的所有元素都是永久不变的,并且通过添加/更新/删除节点创建了一个新图形,那么您可以通过1)仅创建受更改影响的新节点2)否则所有内容都将引用原始图形来创建一个新图形。与写时复制语义类似的概念他已经在做(他使用的是不可变映射,通过引用共享它们的公共元素)应该发布在中吗?我不知道codereview。。。我将在那里发布。。。谢谢