1 /*
2  * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @key stress randomness
27  *
28  * @summary converted from VM testbase nsk/stress/numeric/numeric003.
29  * VM testbase keywords: [stress, slow, nonconcurrent, quick]
30  * VM testbase readme:
31  * DESCRIPTION
32  *     This test calculates the product A*A for a square matrix A of the type
33  *     long[][]. Elements of the matrix A are initiated with random numbers,
34  *     so that optimizing compiler could not eliminate any essential portion
35  *     of calculations.
36  *     That product A*A is calculated twice: in a single thread, and in N
37  *     separate threads, where NxN is the size of square matrix A. When executing
38  *     in N threads, each thread calculate distinct row of the resulting matrix.
39  *     The test checks if the resulting product A*A is the same when calculated
40  *     in single thread and in N threads.
41  *     By the way, the test checks JVM performance. The test is treated failed
42  *     due to poor performance, if single-thread calculation is essentially
43  *     slower than N-threads calculation (surely, the number of CPUs installed
44  *     on the platform executing the test is taken into account for performance
45  *     testing). Note, that HotSpot may fail to adjust itself for better
46  *     performance in single-thread calculation.
47  * COMMENTS
48  *     The bug was filed referencing to the same numeric algorithm,
49  *     which is used by this test:
50  *         4242172 (P3/S5) 2.0: poor performance in matrix calculations
51  *
52  * @library /test/lib
53  * @run main/othervm nsk.stress.numeric.numeric003.numeric003 300 300
54  */
55 
56 package nsk.stress.numeric.numeric003;
57 
58 import java.io.PrintStream;
59 import java.util.Random;
60 import jdk.test.lib.Utils;
61 
62 /**
63  * This test calculates the product <b>A</b><sup>.</sup><b>A</b> for
64  * a square matrix <b>A</b> of the type <code>long[][]</code>.
65  * Elements of the matrix <b>A</b> are initiated with random numbers,
66  * so that optimizing compiler could not eliminate any essential portion
67  * of calculations.
68  * <p>
69  * <p>That product <b>A</b><sup>.</sup><b>A</b> is calculated twice: in
70  * a single thread, and in <i>N</i> separate threads, where <i>N</i>x<i>N</i>
71  * is the size of square matrix <b>A</b>. When executing in <i>N</i> threads,
72  * each thread calculate distinct row of the resulting matrix. The test checks
73  * if the resulting product <b>A</b><sup>.</sup><b>A</b> is the same when
74  * calculated in single thread and in <i>N</i> threads.
75  * <p>
76  * <p>By the way, the test checks JVM performance. The test is treated failed
77  * due to poor performance, if single-thread calculation is essentially
78  * slower than <i>N</i>-threads calculation (surely, the number of CPUs
79  * installed on the platform executing the test is taken into account for
80  * performance testing). Note, that HotSpot may fail to adjust itself for
81  * better performance in single-thread calculation.
82  * <p>
83  * <p>See the bug-report:
84  * <br>&nbsp;&nbsp;
85  * 4242172 (P3/S5) 2.0: poor performance in matrix calculations
86  */
87 public class numeric003 {
88     private static final Random RNG = Utils.getRandomInstance();
89     /**
90      * When testing performance, single thread calculation is allowed to
91      * be 10% slower than multi-threads calculation (<code>TOLERANCE</code>
92      * is assigned to 10 now).
93      */
94     public static final double TOLERANCE = 100; // 10;
95 
96     /**
97      * Re-assign this value to <code>true</code> for better
98      * diagnostics.
99      */
100     private static boolean verbose = false;
101 
102     private static PrintStream out = null;
103 
104     /**
105      * Print error-message to the <code>out<code>.
106      */
complain(Object x)107     private static void complain(Object x) {
108         out.println("# " + x);
109     }
110 
print(Object x)111     private static void print(Object x) {
112         if (verbose)
113             out.print(x);
114     }
115 
println(Object x)116     private static void println(Object x) {
117         print(x + "\n");
118     }
119 
120     /**
121      * Re-invoke <code>run(args,out)</code> in order to simulate
122      * JCK-like test interface.
123      */
main(String args[])124     public static void main(String args[]) {
125         int exitCode = run(args, System.out);
126         System.exit(exitCode + 95);
127         // JCK-like exit status
128     }
129 
130     /**
131      * Parse command-line parameters stored in <code>args[]</code> and run
132      * the test.
133      * <p>
134      * <p>Command-line parameters are:
135      * <br>&nbsp;&nbsp;
136      * <code>java numeric003 [-verbose] [-performance] [-CPU:<i>number</i>]
137      * <i>matrixSize</i> [<i>threads</i>]</code>
138      * <p>
139      * <p>Here:
140      * <br>&nbsp;&nbsp;<code>-verbose</code> -
141      * keyword, which alows to print execution trace
142      * <br>&nbsp;&nbsp;<code>-performance</code> -
143      * keyword, which alows performance testing
144      * <br>&nbsp;&nbsp;<code><i>number</i></code> -
145      * number of CPU installed on the computer just executing the test
146      * <br>&nbsp;&nbsp;<code><i>matrixSize</i></code> -
147      * number of rows (and columns) in square matrix to be tested
148      * <br>&nbsp;&nbsp;<code><i>threads</i></code> -
149      * for multi-thread calculation
150      * (default: <code><i>matrixSize</i></code>)
151      *
152      * @param args strings array containing command-line parameters
153      * @param out  the test log, usually <code>System.out</code>
154      */
run(String args[], PrintStream out)155     public static int run(String args[], PrintStream out) {
156         numeric003.out = out;
157 
158         boolean testPerformance = false;
159         int numberOfCPU = 1;
160 
161         int argsShift = 0;
162         for (; argsShift < args.length; argsShift++) {
163             String argument = args[argsShift];
164 
165             if (!argument.startsWith("-"))
166                 break;
167 
168             if (argument.equals("-performance")) {
169                 testPerformance = true;
170                 continue;
171             }
172 
173             if (argument.equals("-verbose")) {
174                 verbose = true;
175                 continue;
176             }
177 
178             if (argument.startsWith("-CPU:")) {
179                 String value =
180                         argument.substring("-CPU:".length(), argument.length());
181                 numberOfCPU = Integer.parseInt(value);
182 
183                 if (numberOfCPU < 1) {
184                     complain("Illegal number of CPU: " + argument);
185                     return 2; // failure
186                 }
187                 continue;
188             }
189 
190             complain("Cannot recognize argument: args[" + argsShift + "]: " + argument);
191             return 2; // failure
192         }
193 
194         if ((args.length < argsShift + 1) || (args.length > argsShift + 2)) {
195             complain("Illegal argument(s). Execute:");
196             complain(
197                     "    java numeric003 [-verbose] [-performance] [-CPU:number] " +
198                             "matrixSize [threads]");
199             return 2; // failure
200         }
201 
202         int size = Integer.parseInt(args[argsShift]);
203         if ((size < 100) || (size > 10000)) {
204             complain("Matrix size should be 100 to 1000 lines & columns.");
205             return 2; // failure
206         }
207 
208         int threads = size;
209         if (args.length >= argsShift + 2)
210             threads = Integer.parseInt(args[argsShift + 1]);
211         if ((threads < 1) || (threads > size)) {
212             complain("Threads number should be 1 to matrix size.");
213             return 2; // failure
214         }
215         if ((size % threads) != 0) {
216             complain("Threads number should evenly divide matrix size.");
217             return 2; // failure
218         }
219 
220         print("Preparing A[" + size + "," + size + "]:");
221         SquareMatrix A = new SquareMatrix(size);
222         SquareMatrix A1 = new SquareMatrix(size);
223         SquareMatrix Am = new SquareMatrix(size);
224         println(" done.");
225 
226         double singleThread = elapsedTime(out, A, A1, size, 1);
227         double multiThreads = elapsedTime(out, A, Am, size, threads);
228 
229         print("Checking accuracy:");
230         for (int line = 0; line < size; line++)
231             for (int column = 0; column < size; column++)
232                 if (A1.value[line][column] != Am.value[line][column]) {
233                     println("");
234                     complain("Test failed:");
235                     complain("Different results by single- and multi-threads:");
236                     complain("  line=" + line + ", column=" + column);
237                     complain("A1.value[line][column]=" + A1.value[line][column]);
238                     complain("Am.value[line][column]=" + Am.value[line][column]);
239                     return 2; // FAILED
240                 }
241         println(" done.");
242 
243         if (testPerformance) {
244             print("Checking performance: ");
245             double elapsed1 = singleThread;
246             double elapsedM = multiThreads * numberOfCPU;
247             if (elapsed1 > elapsedM * (1 + TOLERANCE / 100)) {
248                 println("");
249                 complain("Test failed:");
250                 complain("Single-thread calculation is essentially slower:");
251                 complain("Calculation time elapsed (seconds):");
252                 complain("  single thread: " + singleThread);
253                 complain("  multi-threads: " + multiThreads);
254                 complain("  number of CPU: " + numberOfCPU);
255                 complain("  tolerance: " + TOLERANCE + "%");
256                 return 2; // FAILED
257             }
258             println("done.");
259         }
260 
261         println("Test passed.");
262         return 0; // PASSED
263     }
264 
elapsedTime(PrintStream out, SquareMatrix A, SquareMatrix AA, int size, int threads)265     private static double elapsedTime(PrintStream out,
266                                       SquareMatrix A, SquareMatrix AA, int size, int threads) {
267 
268         print("Computing A*A with " + threads + " thread(s):");
269         long mark1 = System.currentTimeMillis();
270         AA.setSquareOf(A, threads);
271         long mark2 = System.currentTimeMillis();
272         println(" done.");
273 
274         double sec = (mark2 - mark1) / 1000.0;
275         double perf = size * size * (size + size) / sec;
276         println("Elapsed time: " + sec + " seconds");
277         println("Performance: " + perf / 1e6 + " MFLOPS");
278 
279         return sec;
280     }
281 
282     /**
283      * This class computes <code>A*A</code> for square matrix <code>A</code>.
284      */
285     private static class SquareMatrix {
286         volatile long value[][];
287 
288         /**
289          * New square matrix with random elements.
290          */
SquareMatrix(int size)291         public SquareMatrix(int size) {
292             value = new long[size][size];
293             for (int line = 0; line < size; line++)
294                 for (int column = 0; column < size; column++)
295                     value[line][column] = Math.round(RNG.nextDouble() * size);
296         }
297 
298         /**
299          * Update <code>value[][]</code> of <code>this</code> matrix.
300          *
301          * @param threads Split computation into the given number of threads.
302          */
setSquareOf(SquareMatrix A, int threads)303         public void setSquareOf(SquareMatrix A, int threads) {
304             if (this.value.length != A.value.length)
305                 throw new IllegalArgumentException(
306                         "this.value.length != A.value.length");
307 
308             int size = value.length;
309             if ((size % threads) != 0)
310                 throw new IllegalArgumentException("size%threads != 0");
311             int bunch = size / threads;
312 
313             Thread task[] = new Thread[threads];
314             for (int t = 0; t < threads; t++) {
315                 int line0 = bunch * t;
316                 MatrixComputer computer =
317                         new MatrixComputer(value, A.value, line0, bunch);
318                 task[t] = new Thread(computer);
319             }
320 
321             for (int t = 0; t < threads; t++)
322                 task[t].start();
323 
324             for (int t = 0; t < threads; t++)
325                 if (task[t].isAlive())
326                     try {
327                         task[t].join();
328                     } catch (InterruptedException exception) {
329                         throw new RuntimeException(exception.toString());
330                     }
331         }
332 
333         /**
334          * Thread to compute a bunch of lines of matrix square.
335          */
336         private static class MatrixComputer implements Runnable {
337             private long result[][];
338             private long source[][];
339             private int line0;
340             private int bunch;
341 
342             /**
343              * Register a task for matrix multiplication.
344              */
MatrixComputer( long result[][], long source[][], int line0, int bunch)345             public MatrixComputer(
346                     long result[][], long source[][], int line0, int bunch) {
347 
348                 this.result = result;   // reference to resulting matrix value
349                 this.source = source;   // reference to matrix to be squared
350                 this.line0 = line0;     // compute lines from line0 to ...
351                 this.bunch = bunch;     // number of resulting lines to compute
352             }
353 
354             /**
355              * Do execute the task just registered for <code>this</code> thread.
356              */
run()357             public void run() {
358                 int line1 = line0 + bunch;
359                 int size = result.length;
360                 for (int line = line0; line < line1; line++)
361                     for (int column = 0; column < size; column++) {
362                         long sum = 0;
363                         for (int i = 0; i < size; i++)
364                             sum += source[line][i] * source[i][column];
365                         result[line][column] = sum;
366                     }
367             }
368 
369         }
370 
371     }
372 
373 }
374