package org.infinispan.stress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import junit.framework.TestCase;
import org.infinispan.util.concurrent.BufferedConcurrentHashMap;
import org.infinispan.util.concurrent.BufferedConcurrentHashMap.Eviction;
/**
* Stress test different maps for container implementations
*
* @author Manik Surtani
* @since 4.0
*/
//@Test(testName = "stress.MapsStressTest", groups = "stress", enabled = true, description = "Disabled by default, designed to be run manually.")
public class MapStressTest extends TestCase {
volatile CountDownLatch latch;
final int MAP_CAPACITY = 512;
final float MAP_LOAD_FACTOR = 0.75f;
final int CONCURRENCY = 16;
final int RUN_TIME_MILLIS = 45 * 1000; // 1 min
final int WARMUP_TIME_MILLIS = 10 * 1000; // 10 sec
final int num_loops = 1000;
final int warmup_num_loops = 10;
final int NUM_KEYS = 50000;
final int LOOP_FACTOR=5;
private List<Integer> readOps = new ArrayList<Integer>(NUM_KEYS*LOOP_FACTOR);
private List<Integer> writeOps = new ArrayList<Integer>(NUM_KEYS*LOOP_FACTOR);
private List<Integer> removeOps = new ArrayList<Integer>(NUM_KEYS*LOOP_FACTOR);
private static final Random RANDOM_READ = new Random(12345);
private static final Random RANDOM_WRITE = new Random(34567);
private static final Random RANDOM_REMOVE = new Random(56789);
protected void setUp() throws Exception {
generateArraysForOps();
}
// @BeforeClass
private void generateArraysForOps() {
for(int i = 0;i<NUM_KEYS*LOOP_FACTOR;i++) {
readOps.add(RANDOM_READ.nextInt(NUM_KEYS));
writeOps.add(RANDOM_WRITE.nextInt(NUM_KEYS));
removeOps.add(RANDOM_REMOVE.nextInt(NUM_KEYS));
}
}
// @Test(invocationCount=5)
public void testConcurrentHashMap() throws Exception {
doTest(new ConcurrentHashMap<Integer, Integer>(MAP_CAPACITY,
MAP_LOAD_FACTOR, CONCURRENCY));
}
// @Test(invocationCount=5)
// public void testBufferedConcurrentHashMap() throws Exception {
// doTest(new BufferedConcurrentHashMap<Integer, Integer>(MAP_CAPACITY, MAP_LOAD_FACTOR, CONCURRENCY, EvictionStrategy.LRU,null));
// }
public void testBufferedConcurrentHashMapLRU() throws Exception {
doTest(new BufferedConcurrentHashMap<Integer, Integer>(
MAP_CAPACITY,
MAP_LOAD_FACTOR,
CONCURRENCY,
Eviction.LRU,
new BufferedConcurrentHashMap.EvictionListener<Integer, Integer>() {
public void evicted(Integer arg0, Integer arg1) {
}
}));
}
public void testBufferedConcurrentHashMapLIRS() throws Exception {
doTest(new BufferedConcurrentHashMap<Integer, Integer>(
MAP_CAPACITY,
MAP_LOAD_FACTOR,
CONCURRENCY,
Eviction.LIRS,
new BufferedConcurrentHashMap.EvictionListener<Integer, Integer>() {
public void evicted(Integer arg0, Integer arg1) {
}
}));
}
public void testLRUStress() throws Exception {
for (int i = 0; i < 20; i++) {
doTest(new BufferedConcurrentHashMap<Integer, Integer>(
MAP_CAPACITY, MAP_LOAD_FACTOR, CONCURRENCY, Eviction.LRU,
new BufferedConcurrentHashMap.EvictionListener<Integer, Integer>() {
public void evicted(Integer arg0, Integer arg1) {
}
}),
48,// nreaders
6, // nwriters
4, // nremoves
true // warmup
);
}
}
// public void testLIRSStress() throws Exception {
// for (int i = 0; i < 20; i++) {
// testBufferedConcurrentHashMapLIRS();
// }
// }
// @Test(invocationCount=5)
public void testHashMap() throws Exception {
doTest(Collections.synchronizedMap(new HashMap<Integer, Integer>(MAP_CAPACITY, MAP_LOAD_FACTOR)));
}
private void doTest(final Map<Integer, Integer> map) throws Exception {
doTest(map, 48, 6, 4, true);
}
private void doTest(final Map<Integer, Integer> map, int numReaders, int numWriters,
int numRemovers, boolean warmup) throws Exception {
latch = new CountDownLatch(1);
final Map<String, String> perf = new ConcurrentSkipListMap<String, String>();
final AtomicBoolean run = new AtomicBoolean(true);
List<Thread> threads = new LinkedList<Thread>();
for (int i = 0; i < numReaders; i++) {
Thread getter = new Thread() {
public void run() {
waitForStart();
long start = System.nanoTime();
int runs = 0;
while (run.get() && runs < readOps.size()) {
map.get(readOps.get(runs));
runs++;
}
perf.put("GET" + Thread.currentThread().getId(), opsPerMS(System.nanoTime()
- start, runs));
}
};
threads.add(getter);
}
for (int i = 0; i < numWriters; i++) {
Thread putter = new Thread() {
public void run() {
waitForStart();
long start = System.nanoTime();
int runs = 0;
while (run.get() && runs < writeOps.size()) {
map.put(writeOps.get(runs),runs);
runs++;
}
perf.put("PUT" + Thread.currentThread().getId(), opsPerMS(System.nanoTime()
- start, runs));
}
};
threads.add(putter);
}
for (int i = 0; i < numRemovers; i++) {
Thread remover = new Thread() {
public void run() {
waitForStart();
long start = System.nanoTime();
int runs = 0;
while (run.get() && runs < removeOps.size()) {
map.remove(removeOps.get(runs));
runs++;
}
perf.put("REM" + Thread.currentThread().getId(), opsPerMS(System.nanoTime()
- start, runs));
}
};
threads.add(remover);
}
for (Thread t : threads)
t.start();
latch.countDown();
// wait some time
Thread.sleep(warmup ? WARMUP_TIME_MILLIS : RUN_TIME_MILLIS);
run.set(false);
for (Thread t : threads)
t.join();
System.out.println("Size = " + map.size());
int puts = 0, gets = 0, removes = 0;
for (Entry<String, String> p : perf.entrySet()) {
if (p.getKey().startsWith("PUT")) {
puts += Integer.valueOf(p.getValue());
}
if (p.getKey().startsWith("GET")) {
gets += Integer.valueOf(p.getValue());
}
if (p.getKey().startsWith("REM")) {
removes += Integer.valueOf(p.getValue());
}
}
System.out.println("Performance for container " + map.getClass().getSimpleName());
System.out.println("Average get ops/ms " + (gets / numReaders));
System.out.println("Average put ops/ms " + (puts / numWriters));
System.out.println("Average remove ops/ms " + (removes / numRemovers));
}
private void waitForStart() {
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private String opsPerMS(long nanos, int ops) {
long totalMillis = TimeUnit.NANOSECONDS.toMillis(nanos);
if (totalMillis > 0)
return "" + ops / totalMillis;
else
return "NAN ops/ms";
}
}