import ox.cads.util.Profiler import ox.cads.testing.{LinTester, LinearizabilityTester, WGLinearizabilityTester, DFSLinearizabilityTester, CompetitionLinearizabilityTester} import ox.cads.collection.ShardedMap import scala.util.Random import scala.reflect.ClassTag /** A linearizabilty tester for a Map[A,A]. * @param makeDatum a function that produces random data * @param default default datum * @param p the number of workers * @param iters the number of operations performed by a worker in each run * @param reps the number of runs to perform; a negative value indicates to * run until an error is found * @param maxKey the size of the key space * @param concMapType a string describing the type of concurrent map to use * @param testerType a string indicating the linearizability algorithm to use, * either "linear", "WG" or "DFS". * @param quiet should the testing be silent? * @param maxConfigs the maximum number of configurations to consider * (or -1 if no limit) */ class MapTest[A: ClassTag]( makeDatum: (Int, Random) => A, default: A, p: Int, iters: Int, reps: Int, maxKey: Int, concMapType: String, testerType: String, quiet: Boolean, maxConfigs: Long) { // Probabilities of different operations val (readProb, writeProb, gOEUProb, delProb) = (0.25, 0.25, 0.25, 0.25) // (0.5, 0.5, 0.0, 0.0) // (0.3, 0.3, 0.0, 0.4) // var p = 4 // Number of workers val MaxDatum = 4 assert(readProb + writeProb + gOEUProb + delProb == 1.0) // Types of concurrent and sequential maps type CMap = ox.cads.collection.HashMap[A, A] type SMap = scala.collection.immutable.Map[A, A] type WGSMap = UndoableMap[A, A] // Operations upon the sequential maps def seqGetOrElse(key: A, datum: A)(m: SMap) : (A, SMap) = (m.getOrElse(key, datum), m) def seqUpdate(key: A, datum: A)(m: SMap) : (Unit, SMap) = ((), m.updated(key, datum)) def seqGetOrElseUpdate(key: A, datum: A)(m: SMap) : (A, SMap) = if(m.contains(key)) (m(key), m) else (datum, m.updated(key, datum)) def seqDelete(key: A)(m: SMap) : (Unit, SMap) = ((), m - key) // A worker def worker(me: Int, tester: LinTester[SMap,CMap]) = { val random = new scala.util.Random for(i <- 0 until iters){ val key = makeDatum(maxKey, random); val rand = random.nextFloat if(rand < readProb) // getOrElse tester.log(me, _.getOrElse(key, default), "getOrElse("+key+", "+default+")", seqGetOrElse(key, default)) else if(rand < readProb+writeProb){ // update val datum = makeDatum(MaxDatum, random) tester.log(me, _.update(key, datum), "update("+key+", "+datum+")", seqUpdate(key, datum)) } else if(rand < readProb+writeProb+gOEUProb){ // getOrElseUpdate val datum = makeDatum(MaxDatum, random) tester.log(me, _.getOrElseUpdate(key, datum), "getOrElseUpdate("+key+", "+datum+")", seqGetOrElseUpdate(key, datum)) } else // delete tester.log(me, _.delete(key), "delete("+key+")", seqDelete(key)) } } // A worker for the WG Linearizer def WGworker(me: Int, tester: WGLinearizabilityTester[WGSMap, CMap]) = { val random = new scala.util.Random for(i <- 0 until iters){ val key = makeDatum(maxKey, random); val rand = random.nextFloat if(rand < readProb) // getOrElse tester.log(me, _.getOrElse(key, default), "getOrElse("+key+", "+default+")", _.getOrElse(key, default)) else if(rand < readProb+writeProb){ // update val datum = makeDatum(MaxDatum, random) tester.log(me, _.update(key, datum), "update("+key+", "+datum+")", _.update(key, datum)) } else if(rand < readProb+writeProb+gOEUProb){ // getOrElseUpdate val datum = makeDatum(MaxDatum, random) tester.log(me, _.getOrElseUpdate(key, datum), "getOrElseUpdate("+key+", "+datum+")", _.getOrElseUpdate(key, datum)) } else // delete tester.log(me, _.delete(key), "delete "+key, _.delete(key)) } } type CompTester = CompetitionLinearizabilityTester[SMap, WGSMap, CMap] // A worker for the WG Linearizer def compWorker(me: Int, tester: CompTester) = { val random = new scala.util.Random for(i <- 0 until iters){ val key = makeDatum(maxKey, random); val rand = random.nextFloat if(rand < readProb) // getOrElse tester.log(me, _.getOrElse(key, default), "getOrElse("+key+", "+default+")", seqGetOrElse(key, default), _.getOrElse(key, default)) else if(rand < readProb+writeProb){ // update val datum = makeDatum(MaxDatum, random) tester.log(me, _.update(key, datum), "update("+key+", "+datum+")", seqUpdate(key, datum), _.update(key, datum)) } else if(rand < readProb+writeProb+gOEUProb){ // getOrElseUpdate val datum = makeDatum(MaxDatum, random) tester.log(me, _.getOrElseUpdate(key, datum), "getOrElseUpdate("+key+", "+datum+")", seqGetOrElseUpdate(key, datum), _.getOrElseUpdate(key, datum)) } else // delete tester.log(me, _.delete(key), "delete "+key, seqDelete(key), _.delete(key)) } } /** Choose size to make LvdPW hash table */ private def mkSize : Int = { // Estimate of the size needed val cap = ((iters * (writeProb + gOEUProb) * p) min maxKey) * 1.6 var s = 2; while(s < cap) s *= 2; s } /** Run the tester */ def apply() = { val invocs = iters * p var r = 0L; var done = false while(r != reps && !done){ val concMap : CMap = concMapType match{ case "Sharded" => new ShardedMap[A, A] case "LvdPWFaulty" => new LvdPWFaultyMap[A,A](mkSize) case "LvdPWResizeMapFaulty" => new LvdPWResizeMapFaulty[A,A](mkSize) case "LvdPWBadHash" => new LvdPWBadHashMap[A,A](4) case "LockFreeReadShardedFaulty" => new LockFreeReadShardedMapFaulty[A,A](2,4) case "RSO" => new ox.cads.collection.RecursiveSplitOrderingMap[A,A](2) // case "LvdPWResize" => new LvdPWResizeMap[A, A](8) // case "SimpleSharded" => new SimpleShardedMap[A, A] // case "Sharded" => new ShardedMap[A, A] // case "LockFreeReadSharded" => new LockFreeReadShardedMap[A, A] // case "LockFreeReadAtomicSharded" => // new LockFreeReadAtomicShardedMap[A, A] } if(testerType == "WG"){ val seqMap : WGSMap = new UndoableMap[A, A] val tester = new WGLinearizabilityTester(seqMap, concMap, p, WGworker, invocs, maxConfigs, false) done = (tester() < 0) } else if (testerType == "DFS"){ val seqMap : SMap = scala.collection.immutable.Map[A, A]() val tester = new DFSLinearizabilityTester(seqMap, concMap, p, worker, invocs, maxConfigs, false) done = (tester() < 0) } else if (testerType == "comp"){ val seqMap : SMap = scala.collection.immutable.Map[A, A]() val WGSeqMap : WGSMap = new UndoableMap[A, A] val tester = new CompetitionLinearizabilityTester( seqMap, WGSeqMap, concMap, p, compWorker, invocs, maxConfigs, false) done = (tester() < 0) } else{ assert(testerType == "linear") val seqMap : SMap = scala.collection.immutable.Map[A, A]() val tester = new LinearizabilityTester(seqMap, concMap, p, worker, invocs, maxConfigs, false) done = (tester() < 0) } if(!quiet && r%50 == 0) print(".") r += 1 } } } // ------------------------------------------------------------------ object MapTest{ val usage = """|scala -J-Xmx10g MapTest [--WG | --DFS | --comp] |[--LvdPWFaulty | --LvdPWBadHash | --Sharded | --RSO] |[--timing] [--reps n | --untilError] [--maxKey n] [--iters n] [--badHash] [--maxConfigs n] [-p p]""".stripMargin def main(args: Array[String]) = { // Parse arguments var p = 4 // # workers var iters = 200 // # iterations var maxKey = 20 // maximum key size var timing = false; var i = 0 var reps = 500 // Number of repetitions var tester = "linear" // type of linearizability tester var concMapType = "Sharded" // type of concurrent map var badHash = false // are we using bad hashes var maxConfigs = -1L // maximum # configurations while(i < args.length){ if(args(i) == "--timing"){ timing = true; i += 1 } else if(args(i) == "--WG"){ tester = "WG"; i += 1 } else if(args(i) == "--DFS"){ tester = "DFS"; i += 1 } else if(args(i) == "--comp"){ tester = "comp"; i += 1 } else if(args(i) == "--iters"){ iters = args(i+1).toInt; i += 2 } else if(args(i) == "--reps"){ reps = args(i+1).toInt; i += 2 } else if(args(i) == "--untilError"){ reps = -1; i += 1 } else if(args(i) == "--maxKey"){ maxKey = args(i+1).toInt; i += 2 } else if(args(i) == "--maxConfigs"){ maxConfigs = args(i+1).toLong; i += 2 } else if(args(i) == "--badHash"){ badHash = true; i += 1 } // choice of hash map else if(args(i) == "--LvdPWFaulty"){ concMapType = "LvdPWFaulty"; i += 1 } else if(args(i) == "--LvdPWResizeMapFaulty"){ concMapType = "LvdPWResizeMapFaulty"; i += 1 } else if(args(i) == "--LockFreeReadShardedFaulty"){ concMapType = "LockFreeReadShardedFaulty"; i += 1 } else if(args(i) == "--LvdPWBadHash"){ concMapType = "LvdPWBadHash"; i += 1 } else if(args(i) == "--RSO"){ concMapType = "RSO"; i += 1 } // else if(args(i) == "--LvdPW"){ cMapType = "LvdPW"; i += 1 } // else if(args(i) == "--LvdPWdel"){ cMapType = "LvdPWdel"; i += 1 } // else if(args(i) == "--LvdPWResize"){ cMapType = "LvdPWResize"; i += 1 } // else if(args(i) == "--SimpleSharded"){ // cMapType = "SimpleSharded"; i += 1 // } else if(args(i) == "--Sharded"){ concMapType = "Sharded"; i += 1 } // else if(args(i) == "--LockFreeReadSharded"){ // cMapType = "LockFreeReadSharded"; i += 1 // } // else if(args(i) == "--LockFreeReadAtomicSharded"){ // cMapType = "LockFreeReadAtomicSharded"; i += 1 // } else if(args(i) == "-p"){ p = args(i+1).toInt; i += 2 } else sys.error(usage) } // Run the test val t0 = java.lang.System.nanoTime if(!badHash){ def makeDatum(size: Int, random: Random) = random.nextInt(size).toString val test = new MapTest[String](makeDatum, "X", p, iters, reps, maxKey, concMapType, tester, timing, maxConfigs) test() } else{ def makeDatum(size: Int, random: Random) = new BadHashString(random.nextInt(size).toString) val test = new MapTest[BadHashString]( makeDatum, new BadHashString("X"), p, iters, reps, maxKey, concMapType, tester, timing, -1) test() } val t1 = java.lang.System.nanoTime if(timing) println(t1-t0) else println("\n"+(t1-t0)/1000000) Profiler.report } }