// $Id: performance.java,v 1.8 1998/09/23 06:30:18 oliva Exp $

/* Copyright 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
 *
 * This file is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

import java.util.Hashtable;
import java.util.Vector;

/**
 * This program times several Java operations.
 *
 * @author Alexandre Oliva
 * @version $Revision: 1.8 $
 */
public class performance {
    static finaltest object = statictest.createFinal();
    static int[] array = statictest.createArray();
    static class statictest {
	static int i;
	static void test() {}
	static finaltest createFinal() { return new finaltest(); }
	static Object createObject() { return new Object(); }
	static int[] createArray() { return new int[1]; }
	static long createObject(int count) {
	    Object o = new Object();
	    long start = System.currentTimeMillis();
	    while(count-- > 0) {
		o = new Object();
	    }
	    return System.currentTimeMillis() - start;
	}
	static long createArray(int count) {
	    Object o = new int[1];
	    long start = System.currentTimeMillis();
	    while(count-- > 0) {
		o = new int[1];
	    }
	    return System.currentTimeMillis() - start;
	}
	static long createMultiArray(int count) {
	    Object o = new int[1][1];
	    long start = System.currentTimeMillis();
	    while(count-- > 0) {
		o = new int[1][1];
	    }
	    return System.currentTimeMillis() - start;
	}
    }
    static interface interfacetest {
	void test();
    }
    static class virtualtest implements interfacetest {
	int i;
	public void test() {}
    }
    static class finaltest extends virtualtest {
	private void ftest() {}
	static long finalMethodRun(int count) {
	    finaltest o = object;
	    o.ftest();
	    long start = System.currentTimeMillis();
	    while(count-- > 0)
		o.ftest();
	    return System.currentTimeMillis() - start;
	}
    }
    static interface test { long run(int count); }
    static final test[] tests = new test[] {
	new test() {
	    public String toString() { return "getTime"; }
	    public long run(int count) {
		System.currentTimeMillis();
		long start = System.currentTimeMillis();
		while(--count > 0) {
		    /* --count, not count--, because we run it once more
		       in the return statement! */
		    System.currentTimeMillis();
		}
		return System.currentTimeMillis() - start;
	    }
	},
	new test() {
	    public String toString() { return "emptyloop"; }
	    public long run(int count) {
		System.currentTimeMillis();
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    ;
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "monitorenter/exit"; }
	    public long run(int count) {
		Class c = statictest.class;
		synchronized(c) {}
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    synchronized(c) {}
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "invokestatic"; }
	    public long run(int count) {
		statictest.test();
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    statictest.test();
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "invokespecial"; }
	    public long run(int count) {
		return finaltest.finalMethodRun(count);
	    }
	}, new test() {
	    public String toString() { return "invokevirtual"; }
	    public long run(int count) {
		virtualtest o = object;
		o.test();
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    o.test();
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "invokeinterface"; }
	    public long run(int count) {
		interfacetest o = object;
		o.test();
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    o.test();
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "getstatic"; }
	    public long run(int count) {
		int j = statictest.i;
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    j = statictest.i;
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "putstatic"; }
	    public long run(int count) {
		int j = 0;
		statictest.i = j;
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    statictest.i = j;
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "getfield"; }
	    public long run(int count) {
		virtualtest o = object;
		int j = o.i;
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    j = o.i;
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "putfield"; }
	    public long run(int count) {
		virtualtest o = object;
		int j = 0;
		o.i = j;
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    o.i = j;
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "arraylength"; }
	    public long run(int count) {
		int[] a = array;
		int j = a.length;
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    j = a.length;
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "iaload"; }
	    public long run(int count) {
		int[] a = array;
		int j = a[0];
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    j = a[0];
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "iastore"; }
	    public long run(int count) {
		int[] a = array;
		int j = 0;
		a[0] = j;
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    a[0] = j;
		}
		return System.currentTimeMillis() - start;
	    }
	}, new test() {
	    public String toString() { return "new/invokespecial"; }
	    public long run(int count) {
		return statictest.createObject(count);
	    }
	}, new test() {
	    public String toString() { return "newarray"; }
	    public long run(int count) {
		return statictest.createArray(count);
	    }
	}, new test() {
	    public String toString() { return "multianewarray"; }
	    public long run(int count) {
		return statictest.createMultiArray(count);
	    }
	}, new test() {
	    public String toString() { return "println"; }
	    public long run(int count) {
		System.out.println("Hello world!");
		long start = System.currentTimeMillis();
		while(count-- > 0) {
		    System.out.println("Hello world!");
		}
		return System.currentTimeMillis() - start;
	    }
	}
    };
    static final Hashtable table = new Hashtable();
    static {
	for(int i = 0, e = tests.length; i < e; ++i)
	    table.put(tests[i].toString(), tests[i]);
    }
    public static void main(String [ ] argv)
	throws NumberFormatException {
	if (argv.length == 0)
	    for(int i = 0, e = tests.length; i < e; ++i)
		runtestAndPrint(tests[i]);
	else if (argv.length == 1 && "--list".equals(argv[0]))
	    for(int i = 0, e = tests.length; i < e; ++i)
		System.out.println(tests[i]);
	else
	    for(int i = 0, e = argv.length; i < e; ++i) {
		if ("--total".equals(argv[i]))
		    total = Double.valueOf(argv[++i]).doubleValue();
		else if ("--cycle".equals(argv[i]))
		    cycle = Double.valueOf(argv[++i]).doubleValue();
		else if ("--time".equals(argv[i]))
		    esttime = 1e3*Double.valueOf(argv[++i]).doubleValue();
		else if ("--tolerance".equals(argv[i]))
		    tolerance = 1e3*Double.valueOf(argv[++i]).doubleValue();
		else if ("--all".equals(argv[i]))
		    main(new String[0]);
		else if ("--nostat".equals(argv[i]))
		    stat = false;
		else try {
		    runtestAndPrint((test)table.get(argv[i]));
		} catch (NullPointerException p) {
		    System.err.println("Ignoring invalid argument: " +
				       argv[i]); 
		}
	    }
    }
    /** The total overhead per full loop, in seconds. */
    static double total = 0;
    /** The overhead per loop iteration, in seconds.  */
    static double cycle = 0;
    /** The total loop time to aim at, in milliseconds.  */
    static double esttime = 1100;
    /** The minimum loop time to accept, in milliseconds.  */
    static double tolerance = 1000;
    /** Controls whether we're going to actually measure the tests and 
	print their results.  The --nostat command-line argument
	disables this variable, so that no output will be produced,
	and every test will be executed only once.  This is used by a
	separate script that produces the assembly code generated for
	each test.  */
    static boolean stat = true;
    static void runtestAndPrint(test t) {
	if (stat)
	    System.err.print(t + ": ");
	double avgtime = runtest(t, null);
	if (stat)
	    System.err.println(avgtime);
    }
    static double runtest(test t, Vector sizelist) {
	int count = 1;
	long time = 0;
	do {
	    if (sizelist != null)
		sizelist.addElement(new Integer(count));
	    if ((time = t.run(count)) > tolerance)
		break;
	    if (time == 0)
		count *= 10;
	    else
		count = (int)(count * esttime / (time - total - count * cycle));
	} while(stat);
	return (t.run(count)*1e-3 - total)/count - cycle;
    }
}
