Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/stress/numeric/numeric002.java
41159 views
/*1* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @key stress randomness26*27* @summary converted from VM testbase nsk/stress/numeric/numeric002.28* VM testbase keywords: [stress, slow, nonconcurrent, quick]29* VM testbase readme:30* DESCRIPTION31* This test calculates the product A*A for a square matrix A of the type32* float[][]. Elements of the matrix A are initiated with random numbers,33* so that optimizing compiler could not eliminate any essential portion34* of calculations.35* That product A*A is calculated twice: in a single thread, and in N36* separate threads, where NxN is the size of square matrix A. When executing37* in N threads, each thread calculate distinct row of the resulting matrix.38* The test checks if the resulting product A*A is the same when calculated39* in single thread and in N threads.40* By the way, the test checks JVM performance. The test is treated failed41* due to poor performance, if single-thread calculation is essentially42* slower than N-threads calculation (surely, the number of CPUs installed43* on the platform executing the test is taken into account for performance44* testing). Note, that HotSpot may fail to adjust itself for better45* performance in single-thread calculation.46* COMMENTS47* The bug was filed referencing to the same numeric algorithm,48* which is used by this test:49* 4242172 (P3/S5) 2.0: poor performance in matrix calculations50*51* @library /test/lib52* @run main/othervm nsk.stress.numeric.numeric002.numeric002 300 30053*/5455package nsk.stress.numeric.numeric002;5657import java.io.PrintStream;58import java.util.Random;59import jdk.test.lib.Utils;6061/**62* This test calculates the product <b>A</b><sup>.</sup><b>A</b> for63* a square matrix <b>A</b> of the type <code>float[][]</code>.64* Elements of the matrix <b>A</b> are initiated with random numbers,65* so that optimizing compiler could not eliminate any essential portion66* of calculations.67* <p>68* <p>That product <b>A</b><sup>.</sup><b>A</b> is calculated twice: in69* a single thread, and in <i>N</i> separate threads, where <i>N</i>x<i>N</i>70* is the size of square matrix <b>A</b>. When executing in <i>N</i> threads,71* each thread calculate distinct row of the resulting matrix. The test checks72* if the resulting product <b>A</b><sup>.</sup><b>A</b> is the same when73* calculated in single thread and in <i>N</i> threads.74* <p>75* <p>By the way, the test checks JVM performance. The test is treated failed76* due to poor performance, if single-thread calculation is essentially77* slower than <i>N</i>-threads calculation (surely, the number of CPUs78* installed on the platform executing the test is taken into account for79* performance testing). Note, that HotSpot may fail to adjust itself for80* better performance in single-thread calculation.81* <p>82* <p>See the bug-report:83* <br> 84* 4242172 (P3/S5) 2.0: poor performance in matrix calculations85*/86public class numeric002 {87private static final Random RNG = Utils.getRandomInstance();88/**89* When testing performance, single thread calculation is allowed to90* be 10% slower than multi-threads calculation (<code>TOLERANCE</code>91* is assigned to 10 now).92*/93public static final double TOLERANCE = 100; // 10;9495/**96* Re-assign this value to <code>true</code> for better97* diagnostics.98*/99private static boolean verbose = false;100101private static PrintStream out = null;102103/**104* Print error-message to the <code>out<code>.105*/106private static void complain(Object x) {107out.println("# " + x);108}109110private static void print(Object x) {111if (verbose)112out.print(x);113}114115private static void println(Object x) {116print(x + "\n");117}118119/**120* Re-invoke <code>run(args,out)</code> in order to simulate121* JCK-like test interface.122*/123public static void main(String args[]) {124int exitCode = run(args, System.out);125System.exit(exitCode + 95);126// JCK-like exit status127}128129/**130* Parse command-line parameters stored in <code>args[]</code> and run131* the test.132* <p>133* <p>Command-line parameters are:134* <br> 135* <code>java numeric002 [-verbose] [-performance] [-CPU:<i>number</i>]136* <i>matrixSize</i> [<i>threads</i>]</code>137* <p>138* <p>Here:139* <br> <code>-verbose</code> -140* keyword, which alows to print execution trace141* <br> <code>-performance</code> -142* keyword, which alows performance testing143* <br> <code><i>number</i></code> -144* number of CPU installed on the computer just executing the test145* <br> <code><i>matrixSize</i></code> -146* number of rows (and columns) in square matrix to be tested147* <br> <code><i>threads</i></code> -148* for multi-thread calculation149* (default: <code><i>matrixSize</i></code>)150*151* @param args strings array containing command-line parameters152* @param out the test log, usually <code>System.out</code>153*/154public static int run(String args[], PrintStream out) {155numeric002.out = out;156157boolean testPerformance = false;158int numberOfCPU = 1;159160int argsShift = 0;161for (; argsShift < args.length; argsShift++) {162String argument = args[argsShift];163164if (!argument.startsWith("-"))165break;166167if (argument.equals("-performance")) {168testPerformance = true;169continue;170}171172if (argument.equals("-verbose")) {173verbose = true;174continue;175}176177if (argument.startsWith("-CPU:")) {178String value =179argument.substring("-CPU:".length(), argument.length());180numberOfCPU = Integer.parseInt(value);181182if (numberOfCPU < 1) {183complain("Illegal number of CPU: " + argument);184return 2; // failure185}186continue;187}188189complain("Cannot recognize argument: args[" + argsShift + "]: " + argument);190return 2; // failure191}192193if ((args.length < argsShift + 1) || (args.length > argsShift + 2)) {194complain("Illegal argument(s). Execute:");195complain(196" java numeric002 [-verbose] [-performance] [-CPU:number] " +197"matrixSize [threads]");198return 2; // failure199}200201int size = Integer.parseInt(args[argsShift]);202if ((size < 100) || (size > 10000)) {203complain("Matrix size should be 100 to 1000 lines & columns.");204return 2; // failure205}206207int threads = size;208if (args.length >= argsShift + 2)209threads = Integer.parseInt(args[argsShift + 1]);210if ((threads < 1) || (threads > size)) {211complain("Threads number should be 1 to matrix size.");212return 2; // failure213}214if ((size % threads) != 0) {215complain("Threads number should evenly divide matrix size.");216return 2; // failure217}218219print("Preparing A[" + size + "," + size + "]:");220SquareMatrix A = new SquareMatrix(size);221SquareMatrix A1 = new SquareMatrix(size);222SquareMatrix Am = new SquareMatrix(size);223println(" done.");224225double singleThread = elapsedTime(out, A, A1, size, 1);226double multiThreads = elapsedTime(out, A, Am, size, threads);227228if (!A1.isConsistent) {229complain("Failed to execute 1-thread calculation.");230return 2; // test FAILED231}232if (!Am.isConsistent) {233complain("Failed to execute " + threads + "-threads calculation.");234if (testPerformance) {235complain("I.e.: VM failed to execute " + threads + " threads,");236complain("and this looks like a performance bug.");237return 2; // test FAILED238} else {239complain("This looks strange, but this is not a bug.");240complain("The test is thought passed.");241return 0; // test PASSED242}243}244245print("Checking accuracy:");246for (int line = 0; line < size; line++)247for (int column = 0; column < size; column++)248if (A1.value[line][column] != Am.value[line][column]) {249println("");250complain("Test failed:");251complain("Different results by single- and multi-threads:");252complain(" line=" + line + ", column=" + column);253complain("A1.value[line][column]=" + A1.value[line][column]);254complain("Am.value[line][column]=" + Am.value[line][column]);255return 2; // FAILED256}257println(" done.");258259if (testPerformance) {260print("Checking performance: ");261double elapsed1 = singleThread;262double elapsedM = multiThreads * numberOfCPU;263if (elapsed1 > elapsedM * (1 + TOLERANCE / 100)) {264println("");265complain("Test failed:");266complain("Single-thread calculation is essentially slower:");267complain("Calculation time elapsed (seconds):");268complain(" single thread: " + singleThread);269complain(" multi-threads: " + multiThreads);270complain(" number of CPU: " + numberOfCPU);271complain(" tolerance: " + TOLERANCE + "%");272return 2; // FAILED273}274println("done.");275}276277println("Test passed.");278return 0; // PASSED279}280281private static double elapsedTime(PrintStream out,282SquareMatrix A, SquareMatrix AA, int size, int threads) {283284print("Computing A*A with " + threads + " thread(s):");285long mark1 = System.currentTimeMillis();286AA.setSquareOf(A, threads);287long mark2 = System.currentTimeMillis();288println(" done.");289290double sec = (mark2 - mark1) / 1000.0;291double perf = size * size * (size + size) / sec;292println("Elapsed time: " + sec + " seconds");293println("Performance: " + perf / 1e6 + " MFLOPS");294295return sec;296}297298/**299* This class computes <code>A*A</code> for square matrix <code>A</code>.300*/301private static class SquareMatrix {302volatile float value[][];303boolean isConsistent = false;304305/**306* New square matrix with random elements.307*/308public SquareMatrix(int size) {309value = new float[size][size];310for (int line = 0; line < size; line++)311for (int column = 0; column < size; column++)312value[line][column] = (float) (RNG.nextDouble() * size);313isConsistent = true;314}315316/**317* Update <code>value[][]</code> of <code>this</code> matrix.318*319* @param threads Split computation into the given number of threads.320*/321public void setSquareOf(SquareMatrix A, int threads) {322if (this.value.length != A.value.length)323throw new IllegalArgumentException(324"this.value.length != A.value.length");325326int size = value.length;327if ((size % threads) != 0)328throw new IllegalArgumentException("size%threads != 0");329int bunch = size / threads;330331Thread task[] = new Thread[threads];332for (int t = 0; t < threads; t++) {333int line0 = bunch * t;334MatrixComputer computer =335new MatrixComputer(value, A.value, line0, bunch);336task[t] = computer;337}338339for (int t = 0; t < threads; t++)340task[t].start();341342isConsistent = true;343for (int t = 0; t < threads; t++) {344if (task[t].isAlive())345try {346task[t].join();347} catch (InterruptedException exception) {348throw new RuntimeException(exception.toString());349}350if (!((MatrixComputer) (task[t])).threadExecuted) {351complain("Thread #" + t + " was not actually executed.");352isConsistent = false;353}354}355}356357/**358* Thread to compute a bunch of lines of matrix square.359*/360private static class MatrixComputer extends Thread {361private float result[][];362private float source[][];363private int line0;364private int bunch;365366/**367* Register a task for matrix multiplication.368*/369public MatrixComputer(370float result[][], float source[][], int line0, int bunch) {371372this.result = result; // reference to resulting matrix value373this.source = source; // reference to matrix to be squared374this.line0 = line0; // compute lines from line0 to ...375this.bunch = bunch; // number of resulting lines to compute376}377378/**379* Do execute the task just registered for <code>this</code> thread.380*/381public void run() {382int line1 = line0 + bunch;383int size = result.length;384for (int line = line0; line < line1; line++)385for (int column = 0; column < size; column++) {386float sum = 0;387for (int i = 0; i < size; i++)388sum += source[line][i] * source[i][column];389result[line][column] = sum;390}391threadExecuted = true;392}393394/**395* Method <code>run()</code> sets this flag on is actually396* finishes to execute.397*/398boolean threadExecuted = false;399}400401}402403}404405406