//import java.net.*;
//import java.io.*;

/**
 * 
 * @author Bruce Boyes, based on code from Imsys (www.imsys.se)
 * 
 * @version 1.1a
 *
 * <ul>
 * <li>1.1a 2003 Feb 19 bboyes adding byte math
 * <li>1.0f 2003 Feb 18 bboyes making float and double more realistic, with 
 * actual float and double operands
 * <li>1.0e 2003 Feb 18 bboyes total time and operations reporting
 * <li>1.0d 2003 Feb 18 bboyes made int test better by using an int operand, 
 * added ops per second to report method.
 * <li>1.0c 2003 Feb 17 bboyes attempting to improve math tests. Integer divide
 * was just 3/3 then 1/1 forever, not very interesting. Using the iteration
 * index in the test.
 * <li>1.0 2003 Feb 08 bboyes Cleaning up this code a bit and fixing some
 * problems such as a bug in arrayPerformance() which made byte and int array
 * copies identical. Running on JStamp.
 * <ul>
 * <hr>
 * This code is based on BenchMark.java from the Imsys SNAP examples.
 */
public class BenchMark {
	public BenchMark()
	{
	}

	private void dummy()
	{
	}
	
	static String VERSION = new String ("1.1a bboyes");
	static Runtime rt = Runtime.getRuntime();
	private static boolean VERBOSE = false;
	static long startAll, endAll, countAll=0;

	/**
	 * Print out the time it took to complete a task
	 * Also calculate the rate per second = (count * 1000) / time <br>
	 * where time is in msec, hence the factor of 1000 
	 * @param count How many iterations
	 * @param time How many msec it took
	 * @param task String description of the task
	 */
	public void report(long count, long time, String task)
	{
		if (0==time) time=1L;	// on the PC time is often zero
		System.out.println(count + " " + task + ": " + time + " ms  "
		+ (count * 1000)/time +"/sec");
	}
	
	/**
	 * Count the time it takes to iterate through a loop so that
	 * we can deduct this time from the total loop + operation time
	 * to get just the operation time.
	 * 
	 *  @param count - the number of iterations
	 */
	long getIterationTime (int count)
	{
		long start, end;
		int i;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)			;
		end = System.currentTimeMillis();
		if (VERBOSE) System.out.println("IterationTime= " + (end - start) + " msec");
		return (end - start);
	}	
	
	public void showMemory(String comment) {
		if (false == VERBOSE) return;
		if (null != comment) System.out.print (comment + ": ");
		System.out.print("Memory total=0x" + Integer.toHexString((int)rt.totalMemory())
			+ "/" + rt.totalMemory() );
		System.out.println(" free=0x" + Integer.toHexString((int)rt.freeMemory())
			+ "/" + rt.freeMemory()	);
	}

	public static void main(String args[]) {
		
		System.out.println("Benchmark " + VERSION);

		startAll = System.currentTimeMillis();

		BenchMark b = new BenchMark();
		
		b.showMemory("At start");
		int chunkSize = 0x8000;		// Must be power of 2
		int count = chunkSize * 8;	// Must be dividable by chunkSize
		
		final int iterate = 200000;
		int tests=0;

		//b = new BenchMark();
		//b.tcpPerformance(0x100000, 0x8000, "192.168.1.243");

		
		b.arrayPerformanceByte(count, chunkSize);
		countAll += count;
		
		b.arrayCopyByte(count, chunkSize);
		countAll += count;		
		
		// on TINI, max array size is 64 KBytes
		b.arrayPerformanceInt(count/2, (chunkSize/2)-1);
		countAll += count/2;
		
		// on TINI, max array size is 64 KBytes
		b.arrayCopyInt(count/2, (chunkSize/2)-1);
		countAll += count/2;		
		
		b.bytePerformance(iterate);
		countAll += iterate;
		b.showMemory("");		
		
		b.intPerformance(iterate);
		countAll += iterate;
		b.showMemory("");
		
		b.floatPerformance(iterate);
		countAll += iterate;
		b.showMemory("");
		
		b.doublePerformance(iterate);
		countAll += iterate;
		b.showMemory("");
		
		tests = iterate/10;
		b.stringPerformance(tests);
		countAll += tests;
		b.showMemory("");
		
		b.callPerformance(iterate);
		countAll += iterate;
		b.showMemory("");
		
		System.gc();
		
		tests = iterate/10;
		b.objectPerformance(tests);
		countAll += tests;
		b.showMemory("");
		
		endAll = System.currentTimeMillis();
		b.report(countAll, endAll - startAll, "Total Loop Executions");
		System.out.println("Note: each Loop Execution includes multiple Java operations");
		
	}

	/**
	 * 
	 * @param count
	 * @param chunkSize 
	 */
	public void arrayPerformanceByte(int count, int chunkSize)
	{
		long start, end, nullTime;
		int i;
		if (true == VERBOSE) System.out.println("  byte array with " + chunkSize + " elements");
		showMemory("Before byte array alloc");
		byte b1[] = new byte[chunkSize];
		byte b2[] = new byte[chunkSize];
		showMemory("After byte array alloc");

		nullTime = getIterationTime (count);
		
		// byte array copy
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			b1[i & (chunkSize - 1)] = b2[i & (chunkSize - 1)];
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "byte array access");
		
		/**
		 * Call the garbage collector to free up the byte array memory
		 * since we're done with it and need the space
		 */
		showMemory("  Before GC");
		System.gc();
		try {Thread.sleep(1000);}
		catch (Exception e) {
			System.out.println (e.toString());
		}
		showMemory("  After GC");
	}


	/**
	 * 
	 * @param count
	 * @param chunkSize 
	 */
	public void arrayPerformanceInt(int count, int chunkSize)
	{
		long start, end, nullTime;
		int i;
		if (true == VERBOSE) System.out.println("  int array with " + chunkSize + " elements");
		showMemory("Before int array alloc");
		int i1[] = new int[chunkSize];
		int i2[] = new int[chunkSize];
		showMemory("  After int array alloc");
		
		nullTime = getIterationTime (count);

		// int array access/copy
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			i1[i & (chunkSize - 1)] = i2[i & (chunkSize - 1)];
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "integer array access");
	}	
	
	/**
	 * We want to "touch" the same number of array elements as in the 
	 * array access test, but using System.arraycopy
	 * 
	 * @param count
	 * @param chunkSize
	 */
	public void arrayCopyByte (int count, int chunkSize)
	{
		long start, end, nullTime;
		int i;
		if (true == VERBOSE) System.out.println("  byte array with " + chunkSize + " elements");
		byte b1[] = new byte[chunkSize];
		byte b2[] = new byte[chunkSize];	
		/**
		 * Each time we iterate over arraycopy we access chunkSize elements.
		 * We want to access a total of 'count' elements
		 */
		int loopCount = count/chunkSize;
		
		nullTime = getIterationTime (loopCount);
		
		start = System.currentTimeMillis();
		for (i = 0; i < loopCount; i++)
		System.arraycopy(b1, 0, b2, 0, chunkSize);
		end = System.currentTimeMillis();
		
		report(count, end - start - nullTime, "byte array copies");		
	}
	
	public void arrayCopyInt (int count, int chunkSize)
	{
		long start, end, nullTime;
		int i;
		if (true == VERBOSE) System.out.println("  int array with " + chunkSize + " elements");
		int i1[] = new int[chunkSize];
		int i2[] = new int[chunkSize];	
		int loopCount = count/chunkSize;
		
		nullTime = getIterationTime (loopCount);
		
		start = System.currentTimeMillis();
		for (i = 0; i < loopCount; i++)
		System.arraycopy(i1, 0, i2, 0, chunkSize);
		end = System.currentTimeMillis();
		
		report(count, end - start - nullTime, "int array copies");		
	}	
	
	
	
	

	public void stringPerformance(int count)
	{
		long start, end, nullTime;
		int i;
		String s = null;
		String s1 = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
		String s2 = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";

		nullTime = getIterationTime (count);

		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			s = s1 + s2;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "string concats");

		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			s.equals(s1);
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "string compares");
	}

	public void objectPerformance(int count)
	{
		long start=0, end=0, nullTime=0;
		BenchMark b;
		int i;

		nullTime = getIterationTime (count);

		// Object creations
		try {
			start = System.currentTimeMillis();
			for (i = 0; i < count; i++)
				b = new BenchMark();
			end = System.currentTimeMillis();
		}
		catch (Exception e) {
			showMemory(e.toString());
		}

		report(count, end - start - nullTime, "object creations");
	}


	public void callPerformance(int count)
	{
		long start, end, nullTime;
		int i;

		nullTime = getIterationTime (count);
		
		// Function calls
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			dummy();
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "method calls");
	}

	public void doublePerformance(int count)
	{
		long start, end, nullTime;
		int i;
		double d;
		double e, f;

		nullTime = getIterationTime (count);
		
		// Double Add
		d = (double) 3.14e12;
		e = (double) 111.1313131313e10;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			f = d + e;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "double add");

		// Double sub
		d = (double) 3.14e12;
		e = (double) 111.1313131313e10;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			f = d - e;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "double sub");

		// Double Mul
		d = (double) 3.14e12;
		e = (double) 111.1313131313e10;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			f = d * e;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "double mul");

		// Double Div
		d = (double) 3.14e12;
		e = (double) 111.1313131313e10;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			f = d / e;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "double div");
	}

	public void floatPerformance(int count)
	{
		long start, end, nullTime;
		int i;
		float f, g, h;

		nullTime = getIterationTime (count);		
		
		// Float Add
		f = (float) 3.14;
		g = (float) 111.1313131313;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			h = f + g;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "float add");

		// Float sub
		f = (float) 3.14;
		g = (float) 111.1313131313;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			h = f - g;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "float sub");

		// Float Mul
		f = (float) 3.14;
		g = (float) 111.1313131313;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			h = f * g;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "float mul");

		// Float Div
		f = (float) 3.14;
		g = (float) 111.1313131313;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			h = f / g;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "float div");
		
	}

	/**
	 * int primitive performance: add, sub, mul, div.
	 * I recoded this to use the index in the calculation and an odd 32-bit
	 * constant as the other operand.
	 * @param count the number of iterations of each operation
	 */
	public void intPerformance(int count)
	{
		long start, end, nullTime;
		int i;
		final int J = 0x44332211;
		int k;

		nullTime = getIterationTime (count);

		// Integer Add
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			//j = j + j;
			k = J + i;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "int add");

		// Integer sub
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			//j = j - i;
			k = J - i;
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "int sub");

		// Integer Mul
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++) {
			// This will quickly reach MAX_VALUE? j = j * j;
			// actually it clips at '1' after 28 iterations
			k = J * i;
			// System.out.println(i + ":" + k);
			/**
			if (Integer.MAX_VALUE == j) {
				System.out.println("Result overflow at i=" + i);				
			}
			*/
		}
		end = System.currentTimeMillis();

		report(i, end - start - nullTime, "int mul");

		// Integer Div
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++) {
			// Imsys original code: j = j / j;
			// With seed of 3, this is 3/3, then 1/1 forever
			// Now divide the index by three
			k = i / J;
		}
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "int div");
	}
	
	/**
	 * byte primitive performance: add, sub, mul, div. 
	 * @param count the number of iterations of each operation
	 */
	public void bytePerformance(int count)
	{
		long start, end, nullTime;
		byte a = (byte) 0x77;
		byte b = (byte) 0x11;
		byte c;
		int i;

		nullTime = getIterationTime (count);

		// Add
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			c = (byte) (a + b);
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "byte add");

		//  sub
		a = (byte) 0xff;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++)
			c = (byte) (a - b);
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "byte sub");

		//  Mul
		a= (byte) 0x0f;
		b= (byte) 0x11;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++) {
			c = (byte) (a * b);
		}
		end = System.currentTimeMillis();

		report(i, end - start - nullTime, "byte mul");

		//  Div
		a = (byte) 0xfe;
		b = (byte) 0x0e;
		start = System.currentTimeMillis();
		for (i = 0; i < count; i++) {
			c = (byte) (a / b);
		}
		end = System.currentTimeMillis();

		report(count, end - start - nullTime, "byte div");	
	}

/**
	public void tcpPerformance(int count, int chunkSize, String host)
	{
		long start, end, nullTime;
		Socket s;
		InputStream is;
		OutputStream os;
		int i;
		int left;
		byte b[] = new byte[chunkSize];

		try {
			s = new Socket(host, 4321);
			is = s.getInputStream();
			os = s.getOutputStream();
			b[0] = (byte)(count >> 24);
			b[1] = (byte)(count >> 16);
			b[2] = (byte)(count >> 8);
			b[3] = (byte)count;
			os.write(b, 0, 4);

			start = System.currentTimeMillis();
			for (i = 0; i < count / chunkSize; i++)
				os.write(b);
			end = System.currentTimeMillis();

			report(count, end - start, "byte send TCP/IP");

			try {
				Thread.sleep(1000);
			} catch (InterruptedException ie) {
			}

			left = count;
			start = System.currentTimeMillis();
			while (left != 0) {
				if (left < chunkSize)
					left -= is.read(b, 0, left);
				else
					left -= is.read(b);
			}			
			end = System.currentTimeMillis();

			report(count, end - start, "byte receive TCP/IP");
			os.close();
			is.close();
			s.close();
		} catch (IOException ioe) {
			System.out.println("Exception: " + ioe.getMessage());
		}
	}
*/
}







