/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ /* * @test * * @synopsis Exercise multithreaded maps, byt 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 probablility pinsert it inserts it, and if present, with * probablility premove it removes it. (pinsert and premove are expressed as percentages to simplify * parsing from command line.) */ /* * Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the * public domain. Use, modify, and redistribute this code in any way without acknowledgement. */ package org.apache.geode.internal.util.concurrent.cm; import java.util.Map; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.junit.experimental.categories.Category; import org.apache.geode.test.junit.categories.IntegrationTest; import org.apache.geode.util.JSR166TestCase; @Category(IntegrationTest.class) public class CountedMapLoopsJUnitTest extends JSR166TestCase { // TODO: reformat static int nkeys = 100000; static int pinsert = 60; static int premove = 2; static int maxThreads = 100; static int nops = 2000000; static int removesPerMaxRandom; static int insertsPerMaxRandom; static final ExecutorService pool = Executors.newCachedThreadPool(); @Test public void testCountedMapLoops() throws Exception { main(new String[0]); } 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 = MAP_CLASS; if (args.length > 1) maxThreads = Integer.parseInt(args[1]); if (args.length > 2) nkeys = Integer.parseInt(args[2]); if (args.length > 3) pinsert = Integer.parseInt(args[3]); if (args.length > 4) premove = Integer.parseInt(args[4]); if (args.length > 5) nops = Integer.parseInt(args[5]); // normalize probabilities wrt random number generator removesPerMaxRandom = (int) (((double) premove / 100.0 * 0x7FFFFFFFL)); insertsPerMaxRandom = (int) (((double) pinsert / 100.0 * 0x7FFFFFFFL)); System.out.print("Class: " + mapClass.getName()); System.out.print(" threads: " + maxThreads); System.out.print(" size: " + nkeys); System.out.print(" ins: " + pinsert); System.out.print(" rem: " + premove); System.out.print(" ops: " + nops); System.out.println(); final LoopHelpers.SimpleRandom rng = new LoopHelpers.SimpleRandom(); Integer[] key = new Integer[nkeys]; for (int i = 0; i < key.length; ++i) key[i] = new Integer(rng.next()); AtomicInteger counter; // warmup System.out.println("Warmup..."); for (int k = 0; k < 2; ++k) { Map map = (Map) mapClass.newInstance(); counter = new AtomicInteger(0); LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer(); CyclicBarrier barrier = new CyclicBarrier(1, timer); new Runner(map, key, barrier, counter).run(); int size = map.size(); if (size != counter.get()) throw new Error(); map.clear(); map = null; Thread.sleep(100); } System.gc(); int k = 1; for (int i = 1; i <= maxThreads;) { System.out.print("Threads: " + i + "\t:"); Map map = (Map) mapClass.newInstance(); counter = new AtomicInteger(0); LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer(); CyclicBarrier barrier = new CyclicBarrier(i + 1, timer); for (int t = 0; t < i; ++t) pool.execute(new Runner(map, key, barrier, counter)); 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"); int size = map.size(); if (size != counter.get()) throw new Error(); map.clear(); map = null; // System.gc(); Thread.sleep(100); if (i == k) { k = i << 1; i = i + (i >>> 1); } else i = k; } pool.shutdown(); } static class Runner implements Runnable { final Map map; final Integer[] key; final LoopHelpers.SimpleRandom rng = new LoopHelpers.SimpleRandom(); final CyclicBarrier barrier; final AtomicInteger counter; int position; int total; Runner(Map map, Integer[] key, CyclicBarrier barrier, AtomicInteger counter) { this.map = map; this.key = key; this.barrier = barrier; this.counter = counter; 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 = (Integer) map.get(k); if (x != null) { // if (x.intValue() != k.intValue()) // throw new Error("bad mapping: " + x + " to " + k); if (r < removesPerMaxRandom) { if (map.remove(k) != null) { counter.getAndDecrement(); position = total % key.length; // move from position return 2; } } } else if (r < insertsPerMaxRandom) { if (map.put(k, k) == null) counter.getAndIncrement(); return 2; } // total += LoopHelpers.compute1(k.intValue()); total += r; return 1; } public void run() { try { barrier.await(); int ops = nops; while (ops > 0) ops -= step(); barrier.await(); } catch (Exception ex) { ex.printStackTrace(); } } } }