1/*
2 * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 *   - Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 *
11 *   - Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 *
15 *   - Neither the name of Oracle nor the names of its
16 *     contributors may be used to endorse or promote products derived
17 *     from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * This source code is provided to illustrate the usage of a given feature
34 * or technique and has been deliberately simplified. Additional steps
35 * required for a production-quality application, such as security checks,
36 * input validation and proper error handling, might not be present in
37 * this sample code.
38 */
39
40
41package j2dbench;
42
43public abstract class Test extends Option.Enable {
44    private DependentLink dependencies;
45
46    public Test(Group parent, String nodeName, String description) {
47        super(parent, nodeName, description, false);
48    }
49
50    public void addDependency(Modifier mod) {
51        addDependency(mod, null);
52    }
53
54    public void addDependency(Modifier mod, Modifier.Filter filter) {
55        dependencies = DependentLink.add(dependencies, mod, filter);
56    }
57
58    public void addDependencies(Group g, boolean recursive) {
59        addDependencies(g, recursive, null);
60    }
61
62    public void addDependencies(Group g, boolean recursive,
63                                Modifier.Filter filter)
64    {
65        if (g instanceof Modifier) {
66            addDependency((Modifier) g, filter);
67        }
68        for (Node n = g.getFirstChild(); n != null; n = n.getNext()) {
69            if (n instanceof Modifier) {
70                addDependency((Modifier) n, filter);
71            } else if (recursive && n instanceof Group) {
72                addDependencies((Group) n, recursive, filter);
73            }
74        }
75    }
76
77    public void runTest(TestEnvironment env) {
78        if (!env.isStopped() && isEnabled()) {
79            dependencies.recurseAndRun(env, this);
80        }
81    }
82
83    public void runOneTest(TestEnvironment env) {
84        if (!env.isStopped()) {
85            Result result = new Result(this);
86            env.erase();
87            Object ctx = initTest(env, result);
88            result.setModifiers(env.getModifiers());
89            try {
90                runTestLoop(env, result, ctx);
91            } catch (Throwable t) {
92                result.setError(t);
93            }
94            cleanupTest(env, ctx);
95            // Skip recording results if we were interrupted before
96            // anything interesting happened...
97            if (result.getError() != null || result.getNumRuns() != 0) {
98                if (J2DBench.printresults.isEnabled()) {
99                    result.summarize();
100                }
101                env.record(result);
102            }
103            ctx = null;
104            result = null;
105            env.idle();  // Also done after this method returns...
106        }
107    }
108
109    public abstract Object initTest(TestEnvironment env, Result result);
110    public abstract void runTest(Object context, int numReps);
111    public abstract void cleanupTest(TestEnvironment env, Object context);
112
113    public void runTestLoop(TestEnvironment env, Result result, Object ctx) {
114        // Prime the pump
115        runTest(ctx, 1);
116
117        // Determine the number of reps
118        int numReps = env.getRepCount();
119        if (numReps == 0) {
120            numReps = calibrate(env, ctx);
121        }
122        result.setReps(numReps);
123
124        int numRuns = env.getRunCount();
125        for (int i = 0; i < numRuns; i++) {
126            if (env.idle()) {
127                break;
128            }
129
130            env.sync();
131            env.startTiming();
132            runTest(ctx, numReps);
133            env.sync();
134            env.stopTiming();
135            result.addTime(env.getTimeMillis());
136
137            env.flushToScreen();
138        }
139    }
140
141    public int calibrate(TestEnvironment env, Object ctx) {
142        long testTime = env.getTestTime();
143        int numReps = 0;
144        int totalReps = 0;
145
146        // First do one at a time until we get to 1 second elapsed
147        // But, if we get to 1000 reps we'll start ramping up our
148        // reps per cycle and throwing sync() calls in to make sure
149        // we aren't spinning our gears queueing up graphics calls
150        env.idle();
151        long now = System.currentTimeMillis();
152        long startTime = now;
153        while (numReps < 1000 && now < startTime + 1000) {
154            runTest(ctx, 1);
155            numReps++;
156            now = System.currentTimeMillis();
157        }
158
159        // Time to shift gears into an exponential number of tests
160        // sync() each time in case batching at a lower level is
161        // causing us to spin our gears
162        env.sync();
163        now = System.currentTimeMillis();
164        int reps = 250;
165        while (now < startTime + 1000) {
166            runTest(ctx, reps);
167            env.sync();
168            numReps += reps;
169            reps *= 2;
170            now = System.currentTimeMillis();
171        }
172
173        // Now keep estimating how many reps it takes to hit our target
174        // time exactly, trying it out, and guessing again.
175        while (now < startTime + testTime) {
176            int estimate = (int) (numReps * testTime / (now - startTime));
177            if (estimate <= numReps) {
178                estimate = numReps+1;
179            }
180            runTest(ctx, estimate - numReps);
181            numReps = estimate;
182            env.sync();
183            now = System.currentTimeMillis();
184        }
185
186        // Now make one last estimate of how many reps it takes to
187        // hit the target exactly in case we overshot.
188        int estimate = (int) (numReps * testTime / (now - startTime));
189        if (estimate < 1) {
190            estimate = 1;
191        }
192        return estimate;
193    }
194
195    /*
196     * Finds a new width (w2) such that
197     *     (w-2) <= w2 <= w
198     *     and w2 is not a multiple of 3 (the X step size)
199     *     and GCD(w2, h) is as small as possible
200     */
201    static int prevw;
202    public static int adjustWidth(int w, int h) {
203        int bestv = w;
204        int bestw = w;
205        boolean verbose = (prevw != w && J2DBench.verbose.isEnabled());
206        for (int i = 0; i < 3; i++) {
207            int w2 = w-i;
208            int u = w2;
209            int v = h;
210            while (u > 0) {
211                if (u < v) { int t = u; u = v; v = t; }
212                u -= v;
213            }
214            if (verbose) {
215                System.out.println("w = "+w2+", h = "+h+
216                                   ", w % 3 == "+(w2 % 3)+
217                                   ", gcd(w, h) = "+v);
218            }
219            if (v < bestv && (w2 % 3) != 0) {
220                bestv = v;
221                bestw = w2;
222            }
223        }
224        if (verbose) {
225            System.out.println("using "+bestw+" (gcd = "+bestv+")");
226            prevw = w;
227        }
228        return bestw;
229    }
230
231    public String toString() {
232        return "Test("+getTreeName()+")";
233    }
234
235    public static class DependentLink {
236        public static DependentLink add(DependentLink d, Modifier mod,
237                                        Modifier.Filter filter)
238        {
239            DependentLink dl = new DependentLink(mod, filter);
240            if (d == null) {
241                d = dl;
242            } else {
243                DependentLink last = d;
244                while (last.next != null) {
245                    last = last.next;
246                }
247                last.next = dl;
248            }
249            return d;
250        }
251
252        private DependentLink next;
253        private Modifier mod;
254        private Modifier.Filter filter;
255
256        private DependentLink(Modifier mod, Modifier.Filter filter) {
257            this.mod = mod;
258            this.filter = filter;
259        }
260
261        public Modifier getModifier() {
262            return mod;
263        }
264
265        public Modifier.Filter getFilter() {
266            return filter;
267        }
268
269        public DependentLink getNext() {
270            return next;
271        }
272
273        public void recurseAndRun(TestEnvironment env, Test test) {
274            Modifier.Iterator iter = mod.getIterator(env);
275            while (iter.hasNext()) {
276                Object val = iter.next();
277                if (filter == null || filter.isCompatible(val)) {
278                    mod.modifyTest(env, val);
279                    if (next == null) {
280                        test.runOneTest(env);
281                        env.idle();  // One more time outside of runOneTest()
282                    } else {
283                        next.recurseAndRun(env, test);
284                    }
285                    mod.restoreTest(env, val);
286                }
287            }
288        }
289    }
290}
291