/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.perf.test; import org.jboss.test.perf.interfaces.*; import java.util.*; public class Thrasher implements Runnable { static final int ACTION_SINGLE_READ = 0; static final int ACTION_SINGLE_WRITE = 1; static final int ACTION_BLOCK_READ = 2; static final int ACTION_BLOCK_WRITE = 3; static final int ACTION_FAILURE = 4; static final int ACTION_COUNT = 5; static int[] timers = new int[ACTION_COUNT]; static int[] counters = new int[ACTION_COUNT]; static final String[] ACTION_LABELS = { "single reads ", "single writes", "block reads ", "block writes ", "**FAILURES** ", }; static final int ARG_THREADS = 0; static final int ARG_ITERATIONS = 1; static final int ARG_READ_PERC = 2; static final int ARG_BLOCK_PERC = 3; static final int ARG_BLOCK_MIN = 4; static final int ARG_BLOCK_MAX = 5; static final int ARG_SLEEP_MIN = 6; static final int ARG_SLEEP_MAX = 7; static final int ARG_ID_MIN = 8; static final int ARG_ID_MAX = 9; static final int ARG_COUNT = 10; static final String[] ARG_LABELS = { "threads ", "How many threads to run", "iterations", "How many iterations per thread", "read_perc ", "What percentage of access is readonly", "block_perc", "What percentage of access is block oriented", "block_min ", "The minimum block size per access (inclusive)", "block_max ", "The maximum block size per access (exclusive)", "sleep_min ", "The minimum sleep period between accesses in ms (inclusive)", "sleep_max ", "The maximum sleep period between accesses in ms (exclusive)", "id_min ", "The minimum id accessed (inclusive)", "id_max ", "The maximum id accessed (exclusive)", }; static final int[] argValues = new int[ARG_COUNT]; static private void usage() { System.out.println("Usage: java Thrasher <jndi-name> [-options]\n\n" + " where options include:"); for(int a = 0; a < ARG_COUNT; a++) { System.out.println(" -" + ARG_LABELS[a * 2] + " <n> \t" + ARG_LABELS[a * 2 + 1]); } System.exit(1); } public static void main(String[] args) throws Exception { if(args.length < 1) { usage(); } String jndiName = args[0]; int[] argValues = new int[ARG_COUNT]; argValues[ARG_THREADS] = 1; argValues[ARG_ITERATIONS] = 20; argValues[ARG_READ_PERC] = 90; argValues[ARG_BLOCK_PERC] = 10; argValues[ARG_BLOCK_MIN] = 2; argValues[ARG_BLOCK_MAX] = 10; argValues[ARG_SLEEP_MIN] = 0; argValues[ARG_SLEEP_MAX] = 0; argValues[ARG_ID_MIN] = 0; argValues[ARG_ID_MAX] = 100; for(int i = 1; i < args.length; i++) { boolean found = false; for(int a = 0; a < ARG_COUNT; a++) { String argLabel = "-" + ARG_LABELS[a * 2].trim(); if(args[i].equals(argLabel)) { found = true; if(++i >= args.length) { System.out.println("Value expected for argument: " + argLabel); System.exit(1); } int value; try { value = Integer.parseInt(args[i]); } catch(NumberFormatException e) { System.out.println("Numeric value expected for argument: " + argLabel); System.out.println(" error: " + e); System.exit(1); return; } argValues[a] = value; break; } } if(!found) { usage(); } } Thrasher[] thrashers = new Thrasher[argValues[ARG_THREADS]]; { Thread[] threads = new Thread[argValues[ARG_THREADS]]; for(int i = 0; i < argValues[ARG_THREADS]; i++) { thrashers[i] = new Thrasher(jndiName, argValues); threads[i] = new Thread(thrashers[i], "Thrasher[" + i + "]"); if(i < 5 || i >= argValues[ARG_THREADS] - 5) { System.out.println("Starting thread: " + threads[i]); } threads[i].start(); } for(int i = 0; i < argValues[ARG_THREADS]; i++) { threads[i].join(); } } System.out.println("Configuration:"); for(int a = 0; a < ARG_COUNT; a++) { System.out.println("\t" + ARG_LABELS[a * 2] + "\t" + argValues[a]); } { System.out.println("Actions:"); for(int i = 0; i < ACTION_COUNT; i++) { if(counters[i] != 0) { float avg = (float) timers[i] / counters[i]; // round the value... avg = (long) (avg * 10) / 10f; System.out.println("\t" + counters[i] + "\t" + ACTION_LABELS[i] + "\tavg:\t" + avg + "\tms/Tx"); } } } System.out.println("Performance:"); long totalTime = 0; for(int i = 0; i < argValues[ARG_THREADS]; i++) { long thrasherTime = thrashers[i]._elapsedTime; totalTime += thrasherTime; if(i < 5 || i >= argValues[ARG_THREADS] - 5) { // only print out the first and last 5 enties... float msPerTx = (float) thrasherTime / argValues[ARG_ITERATIONS]; float txPerSec = 1000 / msPerTx; // round the values... msPerTx = (long) (msPerTx * 10) / 10f; txPerSec = (long) (txPerSec * 1000) / 1000f; System.out.println("\tThread[" + i + "]: \t" + txPerSec + "\tTx/s \t" + msPerTx + "\tms/Tx"); } } { float msPerTx = (float) totalTime / argValues[ARG_THREADS] / argValues[ARG_ITERATIONS]; float txPerSec = 1000 / msPerTx; // round the values... msPerTx = (long) (msPerTx * 10) / 10f; txPerSec = (long) (txPerSec * 1000) / 1000f; System.out.println("\tAverage: \t" + txPerSec + "\tTx/s \t" + msPerTx + "\tms/Tx"); float throughput = txPerSec * argValues[ARG_THREADS]; // round the value... throughput = (long) (throughput * 1000) / 1000f; System.out.println("\tThroughput: \t" + throughput + "\tTx/s"); } } private String _jndiName; private int[] _args; private long _elapsedTime; private Thrasher(String jndiName, int[] args) { _jndiName = jndiName; _args = args; } public void run() { try { javax.naming.Context context = new javax.naming.InitialContext(); Object ref = context.lookup("Session"); SessionHome sessionHome = (SessionHome) ref; /** CHANGES: Note that WebLogic does not support ** Spec Compliant PortableRemoteObject way of ** narrow //(SessionHome) javax.rmi.PortableRemoteObject.narrow(ref, SessionHome.class); **/ Session session = sessionHome.create(_jndiName); java.util.Random random = new java.util.Random(); for(int i = 0; i < _args[ARG_ITERATIONS]; i++) { boolean doBlocks = random.nextFloat() < _args[ARG_BLOCK_PERC] / 100f; boolean doReadonly = random.nextFloat() < _args[ARG_READ_PERC] / 100f; int id = getRandom(random, _args[ARG_ID_MIN], _args[ARG_ID_MAX]); int blockSize = doBlocks ? getRandom(random, _args[ARG_BLOCK_MIN], _args[ARG_BLOCK_MAX]) : 0; long before = System.currentTimeMillis(); int action; if(doBlocks) { if(id + blockSize > _args[ARG_ID_MAX]) { // if the blocksize were going to overrun the border, shift the start back id = _args[ARG_ID_MAX] - blockSize; } if(doReadonly) { session.read(id, id + blockSize); action = ACTION_BLOCK_READ; } else { session.write(id, id + blockSize); action = ACTION_BLOCK_WRITE; } } else { if(doReadonly) { session.read(id); action = ACTION_SINGLE_READ; } else { session.write(id); action = ACTION_SINGLE_WRITE; } } long after = System.currentTimeMillis(); _elapsedTime += after - before; timers[action] += after - before; counters[action]++; int sleep = getRandom(random, _args[ARG_SLEEP_MIN], _args[ARG_SLEEP_MAX]); if(sleep != 0) { try { Thread.currentThread().sleep(sleep); } catch(InterruptedException e) { // continue... } } } session.remove(); } catch(Exception e) { counters[ACTION_FAILURE]++; e.printStackTrace(); } } static int getRandom(java.util.Random random, int min, int max) { // first get a random non-negative number int result = Math.abs(random.nextInt()); // get it into the specified range int range = max - min; if(range <= 0) { return min; } result %= range; result += min; return result; } }