Skip to content

Instantly share code, notes, and snippets.

@Hc747
Created August 23, 2018 06:17
Show Gist options
  • Save Hc747/b9218645293ef0d8eb67a5f62dfeff87 to your computer and use it in GitHub Desktop.
Save Hc747/b9218645293ef0d8eb67a5f62dfeff87 to your computer and use it in GitHub Desktop.
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