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> 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> 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> <code>-verbose</code> - 141 * keyword, which alows to print execution trace 142 * <br> <code>-performance</code> - 143 * keyword, which alows performance testing 144 * <br> <code><i>number</i></code> - 145 * number of CPU installed on the computer just executing the test 146 * <br> <code><i>matrixSize</i></code> - 147 * number of rows (and columns) in square matrix to be tested 148 * <br> <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