question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Insertions are not in constant time

See original GitHub issue

The documentation states that element insertion and deletion are in O(1). However, a quick benchmark using JMH reveals linear behaviour for adding vertices and edges.

[info] Benchmark                                                     (siz)  Mode  Cnt   Score   Error  Units
[info] LocalBenchmarks.List.ListBenchmark.addEdgeToImmutableGraph        0  avgt    2   0.017          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addEdgeToImmutableGraph      100  avgt    2   0.030          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addEdgeToImmutableGraph      500  avgt    2   0.094          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addEdgeToImmutableGraph     1000  avgt    2   0.150          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addEdgeToMutableGraph          0  avgt    2   0.017          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addEdgeToMutableGraph        100  avgt    2   0.027          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addEdgeToMutableGraph        500  avgt    2   0.068          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addEdgeToMutableGraph       1000  avgt    2   0.118          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addVertexToImmutableGraph      0  avgt    2  ≈ 10⁻⁴          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addVertexToImmutableGraph    100  avgt    2   0.013          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addVertexToImmutableGraph    500  avgt    2   0.076          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addVertexToImmutableGraph   1000  avgt    2   0.170          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addVertexToMutableGraph        0  avgt    2  ≈ 10⁻⁴          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addVertexToMutableGraph      100  avgt    2   0.018          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addVertexToMutableGraph      500  avgt    2   0.110          ms/op
[info] LocalBenchmarks.List.ListBenchmark.addVertexToMutableGraph     1000  avgt    2   0.254          ms/op
[success] Total time: 497 s (08:17), completed Mar 20, 2020 2:51:04 PM

As you can see, the execution time increases when the size of the graph (the number of vertices) increases. I didn’t test the effect of the number of edges on the execution time.

I don’t think we should change the documentation to say O(n) but rather fix the implementation such that it is indeed in constant time. However, I don’t know what causes this linear behaviour.


The benchmarking code I used:

import org.openjdk.jmh.annotations._
import scalax.collection.GraphEdge.DiEdge
import scalax.collection.GraphPredef.EdgeAssoc

@State(Scope.Benchmark)
class GraphState {
  import scalax.collection.Graph

  @Param(Array("0", "100", "500", "1000"))
  var siz: Int = 0

  var graph: Graph[String, DiEdge] = _

  @Setup(Level.Trial)
  def setup(): Unit = {
    graph = Graph.empty[String, DiEdge]
    (1 to siz)
      .foreach(i => graph = graph + s"Vertex-$i")
  }
}

@State(Scope.Benchmark)
class MutableGraphState {
  import scalax.collection.mutable.Graph

  @Param(Array("0", "100", "500", "1000"))
  var siz: Int = 0

  var graph: Graph[String, DiEdge] = _

  @Setup(Level.Invocation)
  def setup(): Unit = {
    graph = Graph.empty[String, DiEdge]
    (1 to siz)
      .foreach(i => graph += s"Vertex-$i")
    println(s"graph size: ${graph.size}")
  }
}

@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.MILLISECONDS)
class GraphBenchmark {
  @Benchmark
  def addVertexToImmutableGraph(state: GraphState): scalax.collection.Graph[String, DiEdge] = {
    state.graph + "vertex"
  }

  @Benchmark
  def addEdgeToImmutableGraph(state: GraphState): scalax.collection.Graph[String, DiEdge] = {
    state.graph + ("Vertex-1" ~> "Vertex-2")
  }

  @Benchmark
  def addVertexToMutableGraph(state: MutableGraphState): scalax.collection.mutable.Graph[String, DiEdge] = {
    state.graph + "vertex"
  }

  @Benchmark
  def addEdgeToMutableGraph(state: MutableGraphState): scalax.collection.mutable.Graph[String, DiEdge] = {
    state.graph + ("Vertex-1" ~> "Vertex-2")
  }
}

To run a quick benchmark:

sbt
jmh:run -wi 1 -i 2 -f1 -t1

To run a more precise benchmark: jmh:run

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
peter-empencommented, Mar 21, 2020

As with Scala 2.12 collections, + creates a new instance while += does not. So when benchmarking mutable graphs you should not use the + operator.

0reactions
peter-empencommented, Mar 21, 2020

Documentation fixed. Hope your benchmark now reflects the documented characteristics.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Is It Possible For a Data Structure to Have Constant Time ...
And for insertion/deletion to be in constant time, the data would need to be stored dynamically using pointers or some sort of lookup...
Read more >
Why do we say linked list inserts are constant time?
Inserting a new node in a linked list is constant, regardless of where in the list it occurs, not counting how you got...
Read more >
Design a data structure that supports insert, delete, search ...
Design a data structure that supports insert, delete, search and getRandom in constant time · Check if x is present by doing a...
Read more >
Array with Constant Time Access and Fast Insertion ... - Reddit
Long time ago I created and open sourced a random access data structure which like an array has fast constant-time access operation, but...
Read more >
Insertion Sort - Algorithm, Source Code, Time Complexity
Constant complexity is noted as O(1). The sorting method is stable because we only move elements that are greater than the element to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found