Created
August 23, 2018 06:17
-
-
Save Hc747/b9218645293ef0d8eb67a5f62dfeff87 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.PrintStream | |
import java.text.DecimalFormat | |
import java.util.concurrent.ConcurrentHashMap | |
import java.util.concurrent.Executors | |
import java.util.concurrent.TimeUnit | |
import java.util.concurrent.atomic.AtomicReference | |
interface TemporaryStorage<K, V> { | |
fun compute(key: K, remapper: (K, V?) -> V?): V? | |
fun put(key: K, value: V?): V? | |
fun get(key: K): V? | |
fun clear() | |
} | |
@FunctionalInterface | |
interface UnitTestTS<K, V> { | |
fun test(store: TemporaryStorage<K, V>, key: K): Unit | |
} | |
class ConcurrentHashMapTS<K, V> : TemporaryStorage<K, V> { | |
private val map = ConcurrentHashMap<K, V?>() | |
override fun compute(key: K, remapper: (K, V?) -> V?): V? = map.compute(key, remapper) | |
override fun put(key: K, value: V?): V? = map.put(key, value) | |
override fun get(key: K): V? = map[key] | |
override fun clear(): Unit = map.clear() | |
} | |
class AtomicReferenceHashMapTS<K, V> : TemporaryStorage<K, V> { | |
private val map = AtomicReference<HashMap<K, V?>>(HashMap()) | |
override fun compute(key: K, remapper: (K, V?) -> V?): V? = map.get().compute(key, remapper) | |
override fun put(key: K, value: V?): V? = map.get().put(key, value) | |
override fun get(key: K): V? = map.get()[key] | |
override fun clear(): Unit = map.getAndSet(HashMap()).clear() | |
} | |
class MonitorLockedHashMapTS<K, V> : TemporaryStorage<K, V> { | |
private val map = HashMap<K, V?>() | |
private val mutex = Any() | |
override fun compute(key: K, remapper: (K, V?) -> V?): V? = synchronized(mutex) { map.compute(key, remapper) } | |
override fun put(key: K, value: V?): V? = synchronized(mutex) { map.put(key, value) } | |
override fun get(key: K): V? = synchronized(mutex) { map[key] } | |
override fun clear(): Unit = synchronized(mutex) { map.clear() } | |
} | |
class AtomicUnitTest : UnitTestTS<Int, Int?> { | |
override fun test(store: TemporaryStorage<Int, Int?>, key: Int) { | |
store.compute(key) { _, v -> (v ?: 0) + 1 } | |
} | |
} | |
class UnsafeUnitTest : UnitTestTS<Int, Int?> { | |
override fun test(store: TemporaryStorage<Int, Int?>, key: Int) { | |
store.put(key, (store.get(key) ?: 0) + 1) | |
} | |
} | |
object TestRunner { | |
@JvmStatic | |
fun main(args: Array<String>) { | |
val iterations = 1_000 | |
val keys = let { | |
val _keys = mutableListOf<Int>() | |
for (key in 1..iterations) { | |
_keys.add(key) | |
} | |
_keys.toList() | |
} | |
val expected = iterations | |
for (batch in 1..5) { | |
println("--- START BATCH $batch ---") | |
test(System.out, ConcurrentHashMapTS(), AtomicUnitTest(), keys, expected, iterations) | |
test(System.out, ConcurrentHashMapTS(), UnsafeUnitTest(), keys, expected, iterations) | |
test(System.out, AtomicReferenceHashMapTS(), AtomicUnitTest(), keys, expected, iterations) | |
test(System.out, AtomicReferenceHashMapTS(), UnsafeUnitTest(), keys, expected, iterations) | |
test(System.out, MonitorLockedHashMapTS(), AtomicUnitTest(), keys, expected, iterations) | |
test(System.out, MonitorLockedHashMapTS(), UnsafeUnitTest(), keys, expected, iterations) | |
println("--- END BATCH $batch ---") | |
println() | |
} | |
} | |
private fun <K, V> test(printer: PrintStream, store: TemporaryStorage<K, V>, work: UnitTestTS<K, V>, keys: List<K>, expected: V?, iterations: Int, parallelism: Int = Runtime.getRuntime().availableProcessors() * 4) { | |
val workers = Executors.newFixedThreadPool(parallelism) | |
val start = System.currentTimeMillis() | |
for (key in keys) { | |
for (iteration in 1..iterations) { | |
workers.execute { | |
try { | |
work.test(store, key) | |
} catch (e: Exception) { | |
//e.printStackTrace() //thrown by the AtomicReference<Map<K, V>> implementation | |
} | |
} | |
} | |
} | |
workers.shutdown() | |
workers.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS) | |
val finish = System.currentTimeMillis() | |
val formatter = DecimalFormat("###,###") | |
val correct = keys.filter { store.get(it) == expected }.count() | |
printer.println("Store '${store::class.simpleName}' performed ${formatter.format(iterations)} iterations of ${work::class.simpleName} across ${formatter.format(parallelism)} threads in ${formatter.format(finish - start)}ms. Accuracy: $correct / ${keys.size} (${(correct.toDouble() / keys.size.toDouble()) * 100}%)") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment