!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:
Patrik Nordwall 2015-09-21 13:09:19 +02:00
parent 94294c74a7
commit c11b600cc1
9 changed files with 305 additions and 96 deletions

View file

@ -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(", ", ", ")")
}