diff --git a/selekt-java/src/jmh/kotlin/com/bloomberg/selekt/collections/map/benchmarks/FastLruStringMapBenchmark.kt b/selekt-java/src/jmh/kotlin/com/bloomberg/selekt/collections/map/benchmarks/FastStampedStringMapBenchmark.kt similarity index 78% rename from selekt-java/src/jmh/kotlin/com/bloomberg/selekt/collections/map/benchmarks/FastLruStringMapBenchmark.kt rename to selekt-java/src/jmh/kotlin/com/bloomberg/selekt/collections/map/benchmarks/FastStampedStringMapBenchmark.kt index 8c0019cc29..6fb0f98fa2 100644 --- a/selekt-java/src/jmh/kotlin/com/bloomberg/selekt/collections/map/benchmarks/FastLruStringMapBenchmark.kt +++ b/selekt-java/src/jmh/kotlin/com/bloomberg/selekt/collections/map/benchmarks/FastStampedStringMapBenchmark.kt @@ -16,7 +16,7 @@ package com.bloomberg.selekt.collections.map.benchmarks -import com.bloomberg.selekt.collections.map.FastAccessOrderedStringMap +import com.bloomberg.selekt.collections.map.FastStampedStringMap import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.BenchmarkMode import org.openjdk.jmh.annotations.Level @@ -26,32 +26,32 @@ import org.openjdk.jmh.annotations.Setup import org.openjdk.jmh.annotations.State @State(Scope.Thread) -open class LruMapInput { - internal lateinit var map: FastAccessOrderedStringMap +open class StampedMapInput { + internal lateinit var map: FastStampedStringMap @Setup(Level.Iteration) fun setUp() { - map = FastAccessOrderedStringMap(1) + map = FastStampedStringMap(1) } } open class FastLruStringMapBenchmark { @Benchmark @BenchmarkMode(Mode.Throughput) - fun getEntry(input: LruMapInput) = input.map.run { + fun getEntry(input: StampedMapInput) = input.map.run { getEntryElsePut("1") { "" } } @Benchmark @BenchmarkMode(Mode.Throughput) - fun getEntryWithCollision(input: LruMapInput) = input.map.run { + fun getEntryWithCollision(input: StampedMapInput) = input.map.run { getEntryElsePut("1") { "" } getEntryElsePut("2") { "" } } @Benchmark @BenchmarkMode(Mode.Throughput) - fun getThenRemoveEntry(input: LruMapInput) = input.map.run { + fun getThenRemoveEntry(input: StampedMapInput) = input.map.run { getEntryElsePut("1") { "" } removeEntry("1") } diff --git a/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastAccessOrderedStringMap.kt b/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMap.kt similarity index 66% rename from selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastAccessOrderedStringMap.kt rename to selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMap.kt index e222bc0f03..ba478ad9a4 100644 --- a/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastAccessOrderedStringMap.kt +++ b/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMap.kt @@ -19,11 +19,11 @@ package com.bloomberg.selekt.collections.map import javax.annotation.concurrent.NotThreadSafe @NotThreadSafe -class FastAccessOrderedStringMap( +class FastStampedStringMap( capacity: Int ) : FastStringMap(capacity) { - private var accessNumber = Int.MIN_VALUE - private var spare: AOEntry? = null + private var currentStamp = Int.MIN_VALUE + private var spare: StampedEntry? = null inline fun getElsePut( key: String, @@ -32,7 +32,7 @@ class FastAccessOrderedStringMap( val hashCode = hash(key) val index = hashIndex(hashCode) entryMatching(index, hashCode, key)?.let { - (it as AOEntry).accessNumber = nextAccessCount() + (it as StampedEntry).stamp = nextStamp() return it.value!! } return addAssociation(index, hashCode, key, supplier()).value!! @@ -46,9 +46,9 @@ class FastAccessOrderedStringMap( ): Entry { spare?.let { spare = null - return it.update(index, hashCode, key, value, nextAccessCount(), store[index]) + return it.update(index, hashCode, key, value, nextStamp(), store[index]) } - return AOEntry(index, hashCode, key, value, nextAccessCount(), store[index]) + return StampedEntry(index, hashCode, key, value, nextStamp(), store[index]) } override fun clear() { @@ -56,23 +56,13 @@ class FastAccessOrderedStringMap( spare = null } - internal fun asLinkedMap( - capacity: Int = size - ) = FastLinkedStringMap(capacity = capacity).apply { - entries().sortedBy { - (it as AOEntry).accessNumber - }.forEach { - addAssociation(it.index, it.hashCode, it.key, it.value!!) - } - } - @PublishedApi - internal fun nextAccessCount(): Int { - accessNumber += 1 - if (accessNumber == Int.MIN_VALUE) { - resetAllAccessCounts() + internal fun nextStamp(): Int { + if (Int.MAX_VALUE == currentStamp) { + resetAllStamps() } - return accessNumber + currentStamp += 1 + return currentStamp } private fun entries(): Iterable> = store.flatMap { @@ -85,21 +75,24 @@ class FastAccessOrderedStringMap( } }.asIterable() - private fun resetAllAccessCounts() { + private fun resetAllStamps() { entries().sortedBy { - (it as AOEntry).accessNumber - }.forEachIndexed { index, it -> - (it as AOEntry).accessNumber = Int.MIN_VALUE + index + (it as StampedEntry).stamp + }.run { + currentStamp = Int.MIN_VALUE + maxOf(0, size - 1) + forEachIndexed { index, it -> + (it as StampedEntry).stamp = Int.MIN_VALUE + index + } } } @PublishedApi - internal class AOEntry( + internal class StampedEntry( index: Int, hashCode: Int, key: String, value: T, - var accessNumber: Int, + var stamp: Int, after: Entry? ) : Entry(index, hashCode, key, value, after) { @Suppress("NOTHING_TO_INLINE") @@ -108,14 +101,14 @@ class FastAccessOrderedStringMap( hashCode: Int, key: String, value: T, - accessCount: Int, + stamp: Int, after: Entry? ) = apply { this.index = index this.hashCode = hashCode this.key = key this.value = value - this.accessNumber = accessCount + this.stamp = stamp this.after = after } } diff --git a/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastAccessOrderedStringMapTest.kt b/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMapTest.kt similarity index 82% rename from selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastAccessOrderedStringMapTest.kt rename to selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMapTest.kt index 07c397068d..c0cb7fe458 100644 --- a/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastAccessOrderedStringMapTest.kt +++ b/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMapTest.kt @@ -30,18 +30,18 @@ import kotlin.test.assertSame import kotlin.test.assertTrue import kotlin.test.fail -internal class FastAccessOrderedStringMapTest { +internal class FastStampedStringMapTest { @Test fun get() { val first = Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) assertSame(first, map.getElsePut("1") { first }) } @Test fun sizeOne() { val first = Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1") { first } assertEquals(1, map.size) } @@ -49,7 +49,7 @@ internal class FastAccessOrderedStringMapTest { @Test fun getTwice() { val first = Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1") { first } assertSame(first, map.getElsePut("1") { fail() }) } @@ -58,7 +58,7 @@ internal class FastAccessOrderedStringMapTest { fun getWhenAbsent() { val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) val item = map.getElsePut("1", supplier) verify(supplier, times(1)).invoke() assertSame(item, map.getElsePut("1", supplier)) @@ -69,7 +69,7 @@ internal class FastAccessOrderedStringMapTest { fun getTwo() { val first = Any() val second = Any() - val map = FastAccessOrderedStringMap(64) + val map = FastStampedStringMap(64) map.getElsePut("1") { first } map.getElsePut("2") { second } assertEquals(2, map.size) @@ -79,7 +79,7 @@ internal class FastAccessOrderedStringMapTest { fun getTwoWithCollisions() { val first = Any() val second = Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1") { first } map.getElsePut("2") { second } assertSame(first, map.getElsePut("1") { fail() }) @@ -90,7 +90,7 @@ internal class FastAccessOrderedStringMapTest { fun sizeTwo() { val first = Any() val second = Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1") { first } map.getElsePut("2") { second } assertSame(first, map.getElsePut("1") { fail() }) @@ -100,7 +100,7 @@ internal class FastAccessOrderedStringMapTest { @Test fun removeOne() { val first = Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1") { first } assertSame(first, map.removeEntry("1").value) } @@ -109,7 +109,7 @@ internal class FastAccessOrderedStringMapTest { fun removeTwo() { val first = Any() val second = Any() - val map = FastAccessOrderedStringMap(2) + val map = FastStampedStringMap(2) map.getElsePut("1") { first } map.getElsePut("2") { second } assertSame(first, map.removeEntry("1").value) @@ -120,7 +120,7 @@ internal class FastAccessOrderedStringMapTest { fun removeTwoWithCollisions() { val first = Any() val second = Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1") { first } map.getElsePut("2") { second } assertSame(first, map.removeEntry("1").value) @@ -130,7 +130,7 @@ internal class FastAccessOrderedStringMapTest { @Test fun removeThenSize() { val first = Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1") { first } map.removeEntry("1") assertEquals(0, map.size) @@ -138,7 +138,7 @@ internal class FastAccessOrderedStringMapTest { @Test fun removeWhenEmpty() { - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) assertThrows { map.removeEntry("1") } @@ -147,7 +147,7 @@ internal class FastAccessOrderedStringMapTest { @Test fun clear() { - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1") { Any() } assertEquals(1, map.size) map.clear() @@ -156,7 +156,7 @@ internal class FastAccessOrderedStringMapTest { @Test fun clearWhenEmpty() { - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.clear() assertTrue(map.isEmpty()) } @@ -165,7 +165,7 @@ internal class FastAccessOrderedStringMapTest { fun containsFalse() { val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1", supplier) assertFalse(map.containsKey("2")) } @@ -174,7 +174,7 @@ internal class FastAccessOrderedStringMapTest { fun containsTrue() { val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val map = FastAccessOrderedStringMap(1) + val map = FastStampedStringMap(1) map.getElsePut("1", supplier) assertTrue(map.containsKey("1")) }