//You may need to change or remove this package declaration depending on where you put this file package skar.applications; /** A simple microbenchmark to look at a few different ways of getting temporary * objects (Vector3d in this case) for use in a fairly small method. As our test * case we use the computation of the cross product of two vectors which is then * returned in the first vector, overwriting it. The cross product routines need * some temporary space to store the cross product as it is being computed and we * test several different variations: * * - Use local variables. Should be very fast. Thread and recursion safe. * - Allocate using new. Depends on allocation and gc speed. Thread and recursion safe. * - Get temporary from a ThreadLocal. Thread safe, but not recursion safe. * - Store temporary in an object instance field. Not thread or recursion safe. * - Synchronized method using instance field. Thread safe, but not recursion safe. * - Use temp stack via ThreadLocal to get temporary. Thread and recursion safe. * - Pass temp stack as explicit parameter. Thread and recursion safe if used properly. * * I chose cross product as being a small operation that is still useful enough to be * put in its own method, complex enough that compiler is unlikely to be able to optimize * it away, and one that requires some temporary storage during the location. * * To make this class totally self-contained I've included two inner classes: a very * simplistic version of a Vector3d (instead of using javax.vecmath.Vector3d) and * a simple stack of reusable temporary objects called TempStack. * * Warning: this is a microbenchmark and the results may not correspond to the results * in real programs, though I expect it be indicative of some general performance * characteristics. * * Feel free to use or change this code for this class as you wish. * @author bjw (email: walter_bruce@hotmail.com) last change 8/19/03 */ public class CrossProductTest implements Runnable { private final Vector3d tempVector3d = new Vector3d(); ThreadLocal localVector3d = (new ThreadLocal() { protected Object initialValue() { return new Vector3d(); } }); ThreadLocal localTempStack = (new ThreadLocal() { protected Object initialValue() { return new CrossProductTest.TempStack(); } }); /** Creates a new instance of JNITest */ public CrossProductTest() { } /* All the crossProduceXXX() routines compute the cross product to two vectors and * then store the result in the first vector (overwriting it). The differences between * them is how they allocate the temporaries they need during the cross product computation. */ /** This version uses local double variable to hold the temporary values */ public void crossProductLocalVar(Vector3d a, Vector3d b) { double x = a.y*b.z - a.z*b.y; double y = a.z*b.x - a.x*b.z; double z = a.x*b.y - a.y*b.x; a.x = x; a.y = y; a.z = z; } /** This version allocates a new vector3d object to hold temporarily hold the cross product */ public void crossProductNew(Vector3d a, Vector3d b) { Vector3d temp = new Vector3d(); temp.x = a.y*b.z - a.z*b.y; temp.y = a.z*b.x - a.x*b.z; temp.z = a.x*b.y - a.y*b.x; a.x = temp.x; a.y = temp.y; a.z = temp.z; } /** This version retrieves a temporary Vector3d from a ThreadLocal object. Not recursion-safe. */ public void crossProductThreadLocal(Vector3d a, Vector3d b) { Vector3d temp = (Vector3d)localVector3d.get(); temp.x = a.y*b.z - a.z*b.y; temp.y = a.z*b.x - a.x*b.z; temp.z = a.x*b.y - a.y*b.x; a.x = temp.x; a.y = temp.y; a.z = temp.z; } /** This version uses a temporary Vector3d stored in a field of this object. Not thread or recursion-safe. */ public void crossProductInstanceField(Vector3d a, Vector3d b) { Vector3d temp = tempVector3d; temp.x = a.y*b.z - a.z*b.y; temp.y = a.z*b.x - a.x*b.z; temp.z = a.x*b.y - a.y*b.x; a.x = temp.x; a.y = temp.y; a.z = temp.z; } /** This is a synchronized version of the version above. Not recursion-safe. */ public synchronized void crossProductInstanceFieldSync(Vector3d a, Vector3d b) { Vector3d temp = tempVector3d; temp.x = a.y*b.z - a.z*b.y; temp.y = a.z*b.x - a.x*b.z; temp.z = a.x*b.y - a.y*b.x; a.x = temp.x; a.y = temp.y; a.z = temp.z; } /** This version retrieves a TempStack using a ThreadLocal object and then gets a * temporary Vector3d from the TempStack and puts it back when its done with it */ public void crossProductTempStack(Vector3d a, Vector3d b) { TempStack tempStack = (TempStack)localTempStack.get(); Vector3d temp = tempStack.getVector3d(); temp.x = a.y*b.z - a.z*b.y; temp.y = a.z*b.x - a.x*b.z; temp.z = a.x*b.y - a.y*b.x; a.x = temp.x; a.y = temp.y; a.z = temp.z; tempStack.putVector3d(temp); } /** This version takes an extra parameter which is a TempStack to use for getting * and putting a temporary Vector3d object. */ public void crossProductTempStackParam(Vector3d a, Vector3d b, TempStack tempStack) { Vector3d temp = tempStack.getVector3d(); temp.x = a.y*b.z - a.z*b.y; temp.y = a.z*b.x - a.x*b.z; temp.z = a.x*b.y - a.y*b.x; a.x = temp.x; a.y = temp.y; a.z = temp.z; tempStack.putVector3d(temp); } /** This method contains loops that call crossProductXXXX() methods repeatedly, * times them and optionally prints the timing results. Average time per call * is printed in microseconds (usecs). */ public void runTimingTests(boolean print) { final int reps = 30000000; final double denom = 1000.0f/(reps*4); //we call the crossProduct method four times in each loop iteration long time; Vector3d aInit = new Vector3d(1,0,0); Vector3d bInit = new Vector3d(0,1,0); // Using these initial vectors causes some JVMs to slow down dramatically (perhaps due to denormals?) // Vector3d aInit = new Vector3d(1,0,0); // Vector3d bInit = new Vector3d(0,0.6,0.8); Vector3d a = new Vector3d(aInit); Vector3d b = new Vector3d(bInit); time = System.currentTimeMillis(); Thread t; for(int i=0;i