!cdd #18328 optimize VersionVector for size 1 (typical dots)
AFTER: [info] Benchmark (set1Size) Mode Cnt Score Error Units [info] ORSetMergeBenchmark.mergeAddFromBothNodes 1 thrpt 10 2007.939 ± 74.673 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 10 thrpt 10 337.110 ± 15.055 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 20 thrpt 10 223.600 ± 8.403 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 100 thrpt 10 46.697 ± 2.136 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 1 thrpt 10 2542.537 ± 120.697 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 10 thrpt 10 365.694 ± 17.571 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 20 thrpt 10 216.323 ± 9.446 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 100 thrpt 10 49.563 ± 2.725 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 1 thrpt 10 9883.186 ± 725.672 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 10 thrpt 10 3266.528 ± 189.993 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 20 thrpt 10 3206.017 ± 124.623 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 100 thrpt 10 2709.031 ± 162.182 ops/ms [info] ORSetMergeBenchmark.mergeComplex 1 thrpt 10 572.704 ± 21.504 ops/ms [info] ORSetMergeBenchmark.mergeComplex 10 thrpt 10 249.226 ± 12.324 ops/ms [info] ORSetMergeBenchmark.mergeComplex 20 thrpt 10 170.560 ± 10.320 ops/ms [info] ORSetMergeBenchmark.mergeComplex 100 thrpt 10 46.373 ± 1.800 ops/ms BEFORE: [info] Benchmark (set1Size) Mode Cnt Score Error Units [info] ORSetMergeBenchmark.mergeAddFromBothNodes 1 thrpt 10 885.664 ± 99.718 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 10 thrpt 10 304.617 ± 4.755 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 20 thrpt 10 200.977 ± 3.708 ops/ms [info] ORSetMergeBenchmark.mergeAddFromBothNodes 100 thrpt 10 47.879 ± 4.352 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 1 thrpt 10 1586.848 ± 27.476 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 10 thrpt 10 354.408 ± 4.772 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 20 thrpt 10 210.563 ± 32.914 ops/ms [info] ORSetMergeBenchmark.mergeAddFromOtherNode 100 thrpt 10 52.750 ± 0.698 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 1 thrpt 10 3915.817 ± 420.643 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 10 thrpt 10 2369.476 ± 250.336 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 20 thrpt 10 2378.924 ± 47.160 ops/ms [info] ORSetMergeBenchmark.mergeAddFromSameNode 100 thrpt 10 2167.841 ± 20.339 ops/ms [info] ORSetMergeBenchmark.mergeComplex 1 thrpt 10 387.261 ± 8.820 ops/ms [info] ORSetMergeBenchmark.mergeComplex 10 thrpt 10 212.661 ± 4.802 ops/ms [info] ORSetMergeBenchmark.mergeComplex 20 thrpt 10 151.512 ± 2.627 ops/ms [info] ORSetMergeBenchmark.mergeComplex 100 thrpt 10 40.976 ± 2.014 ops/ms * use subtype polymorphism for VersionVector tmp
This commit is contained in:
parent
94294c74a7
commit
c11b600cc1
9 changed files with 305 additions and 96 deletions
|
|
@ -3,25 +3,37 @@
|
|||
*/
|
||||
package akka.cluster.ddata
|
||||
|
||||
import scala.collection.immutable
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.immutable.TreeMap
|
||||
|
||||
import akka.cluster.Cluster
|
||||
import akka.cluster.UniqueAddress
|
||||
import akka.cluster.UniqueAddress
|
||||
|
||||
/**
|
||||
* VersionVector module with helper classes and methods.
|
||||
*/
|
||||
object VersionVector {
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] val emptyVersions: TreeMap[UniqueAddress, Long] = TreeMap.empty
|
||||
val empty: VersionVector = new VersionVector(emptyVersions)
|
||||
private val emptyVersions: TreeMap[UniqueAddress, Long] = TreeMap.empty
|
||||
val empty: VersionVector = ManyVersionVector(emptyVersions)
|
||||
|
||||
def apply(): VersionVector = empty
|
||||
|
||||
def apply(versions: TreeMap[UniqueAddress, Long]): VersionVector =
|
||||
if (versions.isEmpty) empty
|
||||
else if (versions.size == 1) apply(versions.head._1, versions.head._2)
|
||||
else ManyVersionVector(versions)
|
||||
|
||||
def apply(node: UniqueAddress, version: Long): VersionVector = OneVersionVector(node, version)
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] def apply(versions: List[(UniqueAddress, Long)]): VersionVector =
|
||||
if (versions.isEmpty) empty
|
||||
else if (versions.tail.isEmpty) apply(versions.head._1, versions.head._2)
|
||||
else apply(emptyVersions ++ versions)
|
||||
|
||||
/**
|
||||
* Java API
|
||||
*/
|
||||
|
|
@ -57,7 +69,8 @@ object VersionVector {
|
|||
*/
|
||||
def ConcurrentInstance = Concurrent
|
||||
|
||||
private object Timestamp {
|
||||
/** INTERNAL API */
|
||||
private[akka] object Timestamp {
|
||||
final val Zero = 0L
|
||||
final val EndMarker = Long.MinValue
|
||||
val counter = new AtomicLong(1L)
|
||||
|
|
@ -83,8 +96,7 @@ object VersionVector {
|
|||
* This class is immutable, i.e. "modifying" methods return a new instance.
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
final case class VersionVector private[akka] (
|
||||
private[akka] val versions: TreeMap[UniqueAddress, Long])
|
||||
sealed abstract class VersionVector
|
||||
extends ReplicatedData with ReplicatedDataSerialization with RemovedNodePruning {
|
||||
|
||||
type T = VersionVector
|
||||
|
|
@ -107,12 +119,28 @@ final case class VersionVector private[akka] (
|
|||
*/
|
||||
def increment(node: Cluster): VersionVector = increment(node.selfUniqueAddress)
|
||||
|
||||
def isEmpty: Boolean
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def size: Int
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
* Increment the version for the node passed as argument. Returns a new VersionVector.
|
||||
*/
|
||||
private[akka] def increment(node: UniqueAddress): VersionVector =
|
||||
copy(versions = versions.updated(node, Timestamp.counter.getAndIncrement()))
|
||||
private[akka] def increment(node: UniqueAddress): VersionVector
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def versionAt(node: UniqueAddress): Long
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def contains(node: UniqueAddress): Boolean
|
||||
|
||||
/**
|
||||
* Returns true if <code>this</code> and <code>that</code> are concurrent else false.
|
||||
|
|
@ -187,10 +215,15 @@ final case class VersionVector private[akka] (
|
|||
compareNext(nextOrElse(i1, cmpEndMarker), nextOrElse(i2, cmpEndMarker), Same)
|
||||
}
|
||||
|
||||
if ((this eq that) || (this.versions eq that.versions)) Same
|
||||
else compare(this.versions.iterator, that.versions.iterator, if (order eq Concurrent) FullOrder else order)
|
||||
if (this eq that) Same
|
||||
else compare(this.versionsIterator, that.versionsIterator, if (order eq Concurrent) FullOrder else order)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[akka] def versionsIterator: Iterator[(UniqueAddress, Long)]
|
||||
|
||||
/**
|
||||
* Compare two version vectors. The outcome will be one of the following:
|
||||
* <p/>
|
||||
|
|
@ -208,23 +241,128 @@ final case class VersionVector private[akka] (
|
|||
/**
|
||||
* Merges this VersionVector with another VersionVector. E.g. merges its versioned history.
|
||||
*/
|
||||
def merge(that: VersionVector): VersionVector = {
|
||||
var mergedVersions = that.versions
|
||||
for ((node, time) ← versions) {
|
||||
val mergedVersionsCurrentTime = mergedVersions.getOrElse(node, Timestamp.Zero)
|
||||
if (time > mergedVersionsCurrentTime)
|
||||
mergedVersions = mergedVersions.updated(node, time)
|
||||
def merge(that: VersionVector): VersionVector
|
||||
|
||||
override def needPruningFrom(removedNode: UniqueAddress): Boolean
|
||||
|
||||
override def prune(removedNode: UniqueAddress, collapseInto: UniqueAddress): VersionVector
|
||||
|
||||
override def pruningCleanup(removedNode: UniqueAddress): VersionVector
|
||||
|
||||
}
|
||||
|
||||
final case class OneVersionVector private[akka] (node: UniqueAddress, version: Long) extends VersionVector {
|
||||
import VersionVector.Timestamp
|
||||
|
||||
override def isEmpty: Boolean = false
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def size: Int = 1
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def increment(n: UniqueAddress): VersionVector = {
|
||||
val v = Timestamp.counter.getAndIncrement()
|
||||
if (n == node) copy(version = v)
|
||||
else ManyVersionVector(TreeMap(node -> version, n -> v))
|
||||
}
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def versionAt(n: UniqueAddress): Long =
|
||||
if (n == node) version
|
||||
else Timestamp.Zero
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def contains(n: UniqueAddress): Boolean =
|
||||
n == node
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def versionsIterator: Iterator[(UniqueAddress, Long)] =
|
||||
Iterator.single((node, version))
|
||||
|
||||
override def merge(that: VersionVector): VersionVector = {
|
||||
that match {
|
||||
case OneVersionVector(n2, v2) ⇒
|
||||
if (node == n2) if (version >= v2) this else OneVersionVector(n2, v2)
|
||||
else ManyVersionVector(TreeMap(node -> version, n2 -> v2))
|
||||
case ManyVersionVector(vs2) ⇒
|
||||
val v2 = vs2.getOrElse(node, Timestamp.Zero)
|
||||
val mergedVersions =
|
||||
if (v2 >= version) vs2
|
||||
else vs2.updated(node, version)
|
||||
VersionVector(mergedVersions)
|
||||
}
|
||||
}
|
||||
|
||||
override def needPruningFrom(removedNode: UniqueAddress): Boolean =
|
||||
node == removedNode
|
||||
|
||||
override def prune(removedNode: UniqueAddress, collapseInto: UniqueAddress): VersionVector =
|
||||
(if (node == removedNode) VersionVector.empty else this) + collapseInto
|
||||
|
||||
override def pruningCleanup(removedNode: UniqueAddress): VersionVector =
|
||||
if (node == removedNode) VersionVector.empty else this
|
||||
|
||||
override def toString: String =
|
||||
s"VersionVector($node -> $version)"
|
||||
|
||||
}
|
||||
|
||||
final case class ManyVersionVector(versions: TreeMap[UniqueAddress, Long]) extends VersionVector {
|
||||
import VersionVector.Timestamp
|
||||
|
||||
override def isEmpty: Boolean = versions.isEmpty
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def size: Int = versions.size
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def increment(node: UniqueAddress): VersionVector = {
|
||||
val v = Timestamp.counter.getAndIncrement()
|
||||
VersionVector(versions.updated(node, v))
|
||||
}
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def versionAt(node: UniqueAddress): Long = versions.get(node) match {
|
||||
case Some(v) ⇒ v
|
||||
case None ⇒ Timestamp.Zero
|
||||
}
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def contains(node: UniqueAddress): Boolean =
|
||||
versions.contains(node)
|
||||
|
||||
/** INTERNAL API */
|
||||
private[akka] override def versionsIterator: Iterator[(UniqueAddress, Long)] =
|
||||
versions.iterator
|
||||
|
||||
override def merge(that: VersionVector): VersionVector = {
|
||||
that match {
|
||||
case ManyVersionVector(vs2) ⇒
|
||||
var mergedVersions = vs2
|
||||
for ((node, time) ← versions) {
|
||||
val mergedVersionsCurrentTime = mergedVersions.getOrElse(node, Timestamp.Zero)
|
||||
if (time > mergedVersionsCurrentTime)
|
||||
mergedVersions = mergedVersions.updated(node, time)
|
||||
}
|
||||
VersionVector(mergedVersions)
|
||||
case OneVersionVector(n2, v2) ⇒
|
||||
val v1 = versions.getOrElse(n2, Timestamp.Zero)
|
||||
val mergedVersions =
|
||||
if (v1 >= v2) versions
|
||||
else versions.updated(n2, v2)
|
||||
VersionVector(mergedVersions)
|
||||
}
|
||||
VersionVector(mergedVersions)
|
||||
}
|
||||
|
||||
override def needPruningFrom(removedNode: UniqueAddress): Boolean =
|
||||
versions.contains(removedNode)
|
||||
|
||||
override def prune(removedNode: UniqueAddress, collapseInto: UniqueAddress): VersionVector =
|
||||
copy(versions = versions - removedNode) + collapseInto
|
||||
VersionVector(versions = versions - removedNode) + collapseInto
|
||||
|
||||
override def pruningCleanup(removedNode: UniqueAddress): VersionVector = copy(versions = versions - removedNode)
|
||||
override def pruningCleanup(removedNode: UniqueAddress): VersionVector =
|
||||
VersionVector(versions = versions - removedNode)
|
||||
|
||||
override def toString = versions.map { case ((n, t)) ⇒ n + " -> " + t }.mkString("VersionVector(", ", ", ")")
|
||||
override def toString: String =
|
||||
versions.map { case ((n, v)) ⇒ n + " -> " + v }.mkString("VersionVector(", ", ", ")")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue