import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.Transparency; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Random; import junit.framework.TestCase; import edu.umd.cs.piccolo.PNode; import edu.umd.cs.piccolo.nodes.PPath; import edu.umd.cs.piccolo.util.PAffineTransform; import edu.umd.cs.piccolo.util.PBounds; public class PerformanceTests extends TestCase { private static PerformanceLog log = new PerformanceLog(); private static int NUMBER_NODES = 20000; public PerformanceTests(String name) { super(name); } public void testRunPerformanceTests() { // three times to warm up JVM for (int i = 0; i < 3; i++) { addNodes(); copyNodes(); createNodes(); createPaths(); fullIntersectsNodes(); memorySizeOfNodes(); //removeNodes(); translateNodes(); costOfNoBoundsCache(); // renderSpeed(); if (i != 2) { log.clear(); } } log.writeLog(); } public void createNodes() { PNode[] nodes = new PNode[NUMBER_NODES]; log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); } log.endTest("Create " + NUMBER_NODES + " new nodes"); } public void createPaths() { PNode[] nodes = new PNode[NUMBER_NODES]; log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = PPath.createRectangle(0, 0, 100, 80); } log.endTest("Create " + NUMBER_NODES + " new rect paths"); Random r = new Random(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i].translate(r.nextFloat() * 300, r.nextFloat() * 300); } } public void addNodes() { PNode parent = new PNode(); PNode[] nodes = new PNode[NUMBER_NODES]; for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); } log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { parent.addChild(nodes[i]); } log.endTest("Add " + NUMBER_NODES + " nodes to a new parent"); } public void removeNodes() { PNode parent = new PNode(); PNode[] nodes = new PNode[NUMBER_NODES]; ArrayList list = new ArrayList(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); } for (int i = 0; i < NUMBER_NODES; i++) { parent.addChild(nodes[i]); list.add(nodes[i]); } log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { parent.removeChild(nodes[i]); } log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() front to back"); parent.addChildren(list); log.startTest(); for (int i = NUMBER_NODES - 1; i >= 0; i--) { parent.removeChild(i); } log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() back to front by index"); log.startTest(); // for (int i = NUMBER_NODES - 1; i >= 0; i--) { // parent.removeChild(nodes[i]); // } log.endTest("Remove " + NUMBER_NODES + " nodes using removeChild() back to front by object, TO_SLOW"); parent.addChildren(list); log.startTest(); parent.removeChildren(list); log.endTest("Remove " + NUMBER_NODES + " nodes using removeChildren()"); parent.addChildren(list); log.startTest(); parent.removeAllChildren(); log.endTest("Remove " + NUMBER_NODES + " nodes using removeAllChildren()"); } public void translateNodes() { PNode parent = new PNode(); PNode[] nodes = new PNode[NUMBER_NODES]; PBounds b = new PBounds(); Random r = new Random(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); nodes[i].setBounds(1000 * r.nextFloat(), 1000 * r.nextFloat(), 100, 80); parent.addChild(nodes[i]); nodes[i].getFullBoundsReference(); } log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i].translate(1000 * r.nextFloat(), 1000 * r.nextFloat()); nodes[i].scale(1000 * r.nextFloat()); // nodes[i].translateBy(100.01, 100.2); // nodes[i].scaleBy(0.9); } log.endTest("Translate " + NUMBER_NODES + " nodes, not counting repaint or validate layout"); log.startTest(); //parent.validateFullBounds(); now protected. parent.getFullBoundsReference(); // calls validateFullBounds as a side effect. log.endTest("Validate Layout after translate " + NUMBER_NODES + " nodes"); log.startTest(); parent.validateFullPaint(); log.endTest("Validate Paint after translate " + NUMBER_NODES + " nodes"); log.startTest(); parent.computeFullBounds(b); log.endTest("Parent compute bounds of " + NUMBER_NODES + " children nodes"); } public void fullIntersectsNodes() { PNode parent = new PNode(); PNode[] nodes = new PNode[NUMBER_NODES]; PBounds b = new PBounds(0, 50, 100, 20); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); parent.addChild(nodes[i]); } //parent.validateFullBounds(); // now protected parent.getFullBoundsReference(); // calls validateFullBounds as a side effect. log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i].fullIntersects(b); } log.endTest("Do fullIntersects test for " + NUMBER_NODES + " nodes"); } public void memorySizeOfNodes() { PNode[] nodes = new PNode[NUMBER_NODES]; Runtime.getRuntime().gc(); long startTotalMemory = Runtime.getRuntime().totalMemory(); long startFree = Runtime.getRuntime().freeMemory(); long endFree; long endTotal; for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); } Runtime.getRuntime().gc(); endFree = Runtime.getRuntime().freeMemory(); endTotal = Runtime.getRuntime().totalMemory(); log.addEntry("Approximate k used by " + NUMBER_NODES + " nodes", ((endTotal - startTotalMemory) + (startFree - endFree)) / 1024); nodes[0].getPaint(); } public void copyNodes() { PNode parent = new PNode(); PNode[] nodes = new PNode[NUMBER_NODES]; for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); parent.addChild(nodes[i]); } log.startTest(); parent.clone(); log.endTest("Copy/Serialize " + NUMBER_NODES + " nodes"); } public void costOfNoBoundsCache() { PNode[] nodes = new PNode[NUMBER_NODES]; PBounds[] bounds = new PBounds[NUMBER_NODES]; PBounds pickRect = new PBounds(0, 0, 1, 1); Random r = new Random(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i] = new PNode(); nodes[i].translate(1000 * r.nextFloat(), 1000 * r.nextFloat()); nodes[i].scale(1000 * r.nextFloat()); bounds[i] = new PBounds(1000 * r.nextFloat(), 1000 * r.nextFloat(), 100, 80); } log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { bounds[i].intersects(pickRect); } log.endTest("Do intersects test for " + NUMBER_NODES + " bounds"); log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { nodes[i].localToParent(bounds[i]); } log.endTest("Transform " + NUMBER_NODES + " bounds from local to parent"); log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { pickRect.add(bounds[i]); } log.endTest("Sum " + NUMBER_NODES + " bounds"); PBounds b = new PBounds(r.nextDouble(), r.nextDouble(), r.nextDouble(), r.nextDouble()); log.startTest(); for (int i = 0; i < NUMBER_NODES * 10; i++) { b.clone(); } log.endTest("Clone " + NUMBER_NODES * 10 + " PBounds"); } public void renderSpeed() { Random r = new Random(); PAffineTransform at = new PAffineTransform(); at.setScale(r.nextFloat()); at.translate(r.nextFloat(), r.nextFloat()); try { log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { at.createInverse(); } log.endTest("Create inverse transform " + NUMBER_NODES + " times"); } catch (NoninvertibleTransformException e) { } int height = 400; int width = 400; double scale1 = 0.5; double scale2 = 2; boolean scaleFlip = true; PAffineTransform transorm1 = new PAffineTransform(); //transorm1.scale(0.5, 0.5); transorm1.translate(0.5, 10.1); PAffineTransform transorm2 = null; try { transorm2 = new PAffineTransform(transorm1.createInverse()); } catch (NoninvertibleTransformException e) {} GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); BufferedImage result = (BufferedImage)graphicsConfiguration.createCompatibleImage(width, height, Transparency.TRANSLUCENT); Graphics2D g2 = result.createGraphics(); log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { if (scaleFlip) { g2.scale(scale2, scale2); scaleFlip = !scaleFlip; } else { g2.scale(scale1, scale1); scaleFlip = !scaleFlip; } } log.endTest("Scale graphics context " + NUMBER_NODES + " times"); g2.setTransform(new AffineTransform()); log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { g2.translate(0.5, 0.5); } log.endTest("Translate graphics context " + NUMBER_NODES + " times"); g2.setTransform(new AffineTransform()); log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { if (scaleFlip) { g2.transform(transorm1); scaleFlip = !scaleFlip; } else { g2.transform(transorm2); scaleFlip = !scaleFlip; } } log.endTest("Transform graphics context " + NUMBER_NODES + " times"); Rectangle2D rect = new Rectangle2D.Double(0, 0, 100, 80); GeneralPath path = new GeneralPath(rect); log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { g2.fill(rect); } log.endTest("Fill " + NUMBER_NODES + " rects"); log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { g2.getTransform().getScaleX(); } log.endTest("Call g2.getTransform() " + NUMBER_NODES + " times"); log.startTest(); for (int i = 0; i < NUMBER_NODES; i++) { g2.fill(path); } log.endTest("Fill " + NUMBER_NODES + " paths"); } }