| /* |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * This file is available under and governed by the GNU General Public |
| * License version 2 only, as published by the Free Software Foundation. |
| * However, the following notice accompanied the original version of this |
| * file: |
| * |
| * Written by Doug Lea with assistance from members of JCP JSR-166 |
| * Expert Group and released to the public domain, as explained at |
| * http://creativecommons.org/licenses/publicdomain |
| */ |
| |
| /* |
| * @test |
| * @bug 4486658 |
| * @compile MapLoops.java |
| * @run main/timeout=4700 MapLoops |
| * @summary Exercise multithreaded maps, by default ConcurrentHashMap. |
| * Multithreaded hash table test. Each thread does a random walk |
| * though elements of "key" array. On each iteration, it checks if |
| * table includes key. If absent, with probability pinsert it |
| * inserts it, and if present, with probability premove it removes |
| * it. (pinsert and premove are expressed as percentages to simplify |
| * parsing from command line.) |
| */ |
| |
| import java.util.*; |
| import java.util.concurrent.*; |
| |
| public class MapLoops { |
| static final int NKEYS = 100000; |
| static int pinsert = 60; |
| static int premove = 2; |
| static int maxThreads = 5; |
| static int nops = 1000000; |
| static int removesPerMaxRandom; |
| static int insertsPerMaxRandom; |
| |
| static final ExecutorService pool = Executors.newCachedThreadPool(); |
| |
| public static void main(String[] args) throws Exception { |
| |
| Class mapClass = null; |
| if (args.length > 0) { |
| try { |
| mapClass = Class.forName(args[0]); |
| } catch(ClassNotFoundException e) { |
| throw new RuntimeException("Class " + args[0] + " not found."); |
| } |
| } |
| else |
| mapClass = RWMap.class; |
| |
| if (args.length > 1) |
| maxThreads = Integer.parseInt(args[1]); |
| |
| if (args.length > 2) |
| nops = Integer.parseInt(args[2]); |
| |
| if (args.length > 3) |
| pinsert = Integer.parseInt(args[3]); |
| |
| if (args.length > 4) |
| premove = Integer.parseInt(args[4]); |
| |
| // normalize probabilities wrt random number generator |
| removesPerMaxRandom = (int)(((double)premove/100.0 * 0x7FFFFFFFL)); |
| insertsPerMaxRandom = (int)(((double)pinsert/100.0 * 0x7FFFFFFFL)); |
| |
| System.out.println("Using " + mapClass.getName()); |
| |
| Random rng = new Random(315312); |
| Integer[] key = new Integer[NKEYS]; |
| for (int i = 0; i < key.length; ++i) |
| key[i] = new Integer(rng.nextInt()); |
| |
| // warmup |
| System.out.println("Warmup..."); |
| for (int k = 0; k < 2; ++k) { |
| Map<Integer, Integer> map = (Map<Integer,Integer>)mapClass.newInstance(); |
| LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer(); |
| CyclicBarrier barrier = new CyclicBarrier(1, timer); |
| new Runner(map, key, barrier).run(); |
| map.clear(); |
| Thread.sleep(100); |
| } |
| |
| for (int i = 1; i <= maxThreads; i += (i+1) >>> 1) { |
| System.out.print("Threads: " + i + "\t:"); |
| Map<Integer, Integer> map = (Map<Integer,Integer>)mapClass.newInstance(); |
| LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer(); |
| CyclicBarrier barrier = new CyclicBarrier(i+1, timer); |
| for (int k = 0; k < i; ++k) |
| pool.execute(new Runner(map, key, barrier)); |
| barrier.await(); |
| barrier.await(); |
| long time = timer.getTime(); |
| long tpo = time / (i * (long)nops); |
| System.out.print(LoopHelpers.rightJustify(tpo) + " ns per op"); |
| double secs = (double)(time) / 1000000000.0; |
| System.out.println("\t " + secs + "s run time"); |
| map.clear(); |
| } |
| pool.shutdown(); |
| if (! pool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) |
| throw new Error(); |
| } |
| |
| static class Runner implements Runnable { |
| final Map<Integer,Integer> map; |
| final Integer[] key; |
| final LoopHelpers.SimpleRandom rng = new LoopHelpers.SimpleRandom(); |
| final CyclicBarrier barrier; |
| int position; |
| int total; |
| |
| Runner(Map<Integer,Integer> map, Integer[] key, CyclicBarrier barrier) { |
| this.map = map; |
| this.key = key; |
| this.barrier = barrier; |
| position = key.length / 2; |
| } |
| |
| int step() { |
| // random-walk around key positions, bunching accesses |
| int r = rng.next(); |
| position += (r & 7) - 3; |
| while (position >= key.length) position -= key.length; |
| while (position < 0) position += key.length; |
| |
| Integer k = key[position]; |
| Integer x = map.get(k); |
| |
| if (x != null) { |
| if (x.intValue() != k.intValue()) |
| throw new Error("bad mapping: " + x + " to " + k); |
| |
| if (r < removesPerMaxRandom) { |
| // get awy from this position |
| position = r % key.length; |
| map.remove(k); |
| return 2; |
| } |
| else |
| total += LoopHelpers.compute2(LoopHelpers.compute1(x.intValue())); |
| } |
| else { |
| if (r < insertsPerMaxRandom) { |
| map.put(k, k); |
| return 2; |
| } |
| } |
| return 1; |
| } |
| |
| public void run() { |
| try { |
| barrier.await(); |
| int ops = nops; |
| while (ops > 0) |
| ops -= step(); |
| barrier.await(); |
| } |
| catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| } |
| } |
| } |