/* * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @key stress * * @summary converted from VM testbase nsk/stress/numeric/numeric010. * VM testbase keywords: [stress, slow, nonconcurrent, quick] * VM testbase readme: * DESCRIPTION * This test calculates the product A*A for a square matrix A, and checks * if such product is calculated correctly. Elements of the matrix A are * initiated with integer numbers, so that A*A must be the same if calculated * with double, float, long, or int precision. The test just checks, if * double, float, long, and int variants of the product calculation result * in the same A*A matrix. * The product A*A is calculated twice: in a single thread, and in N separate * threads, where NxN is the size of square matrix A. When executing in N * threads, each thread calculate distinct row of the resulting matrix. * HotSpot releases 1.0 and 1.3 seem to do not adjust JVM for better * performance in single-thread calculation, while milti-threads calculation * usually runs much faster. I guess, that the 1-thread calculation is probably * executed by HotSpot interpreter, and HotSpot compiler is probably involved * to execute N-threads calculation. So, the test apparently checks accuracy * of A*A calculation in both compilation and interpretation modes. * By the way, the test checks JVM performance. The test is treated failed * due to poor performance, if single-thread calculation is essentially * slower than N-threads calculation (surely, the number of CPUs installed * on the platform executing the test is taken into account for performance * testing). The calculation algorithm is encoded with 3-levels cycle like: * for (int line=0; lineA.A for a square * matrix A, and checks if such product is calculated correctly. * Elements of the matrix A are initiated with integer numbers, * so that A.A must be the same if calculated with * double, float, long, or * int precision. The test just checks, if double, * float, long, and int variants of * the product calculation result in the same A.A * matrix. *

*

The product A.A is calculated twice: in a single * thread, and in N separate threads, where NxN is * the size of square matrix A. When executing in N * threads, each thread calculate distinct row of the resulting matrix. HotSpot * releases 1.0 and 1.3 seem to do not adjust JVM for better performance in * single-thread calculation, while milti-threads calculation usually runs much * faster. I guess, that the 1-thread calculation is probably executed by HotSpot * interpreter, and HotSpot compiler is probably involved to execute * N-threads calculation. So, the test apparently checks accuracy * of A.A calculation in both compilation and * interpretation modes. *

*

By the way, the test checks JVM performance. The test is treated failed * due to poor performance, if single-thread calculation is essentially * slower than N-threads calculation (surely, the number of CPUs * installed on the platform executing the test is taken into account for * performance testing). The calculation algorithm is encoded with 3-levels * cycle like: *

 *     for (int line=0; line<N; line++)
 *         for (int column=0; column<N; column++) {
 *             float sum = 0;
 *             for (int k=0; k<N; k++)
 *                 sum += A[line][k] * A[k][column];
 *             AA[line][column] = sum;
 *         }
 * 
*

* In this test, N=200, so that A is 200x200 matrix; * and multiplication A[line][k]*A[k][column] is executed * 2003=8 millions times in this cycle. I believe, that this is HotSpot * bug to do not adjust JVM for best performance during such a huge series of * executions of the rather compact portion of program code. *

*

See the bug-report: *
   * 4242172 (P3/S5) 2.0: poor performance in matrix calculations */ public class numeric010 { /** * When testing performance, 1-thread calculation is allowed to be 10% * slower than multi-thread calculation (tolerance is * assigned to 10 now). */ public static double tolerance = 100; // 10; /** * Re-assign this value to true for better diagnostics. * * @see #print(Object) * @see #println(Object) */ private static boolean verbose = false; /** * Stream to print execution trace and/or error messages. * This stream usually equals to System.out */ private static PrintStream out = null; /** * Print error-message. * * @see #out */ private static void complain(Object x) { out.println("# " + x); } /** * Print to execution trace, if mode is verbose. * * @see #verbose * @see #out */ private static void print(Object x) { if (verbose) out.print(x); } /** * Print line to execution trace, if mode is verbose. * * @see #verbose * @see #out */ private static void println(Object x) { print(x + "\n"); } /** * Re-invoke run(args,out) in order to simulate * JCK-like test interface. */ public static void main(String args[]) { int exitCode = run(args, System.out); System.exit(exitCode + 95); // JCK-like exit status } /** * Parse command-line parameters stored in args[] and run * the test. *

*

Command-line parameters are: *
   * java numeric010 [-verbose] [-performance] * [-tolerance:percents] [-CPU:number] * matrixSize [threads] *

*

Here: *
  -verbose - * keyword, which alows to print execution trace *
  -performance - * keyword, which alows performance testing *
  -tolerance - * setup tolerance of performance checking *
  percents - * 1-thread calculation is allowed to be * percents% slower *
  number - * number of CPU installed on the computer just executing the test *
  matrixSize - * number of rows (and columns) in square matrix to be tested *
  threads - * for multi-thread calculation * (default: matrixSize) * * @param args strings array containing command-line parameters * @param out the test log, usually System.out */ public static int run(String args[], PrintStream out) { numeric010.out = out; boolean testPerformance = false; int numberOfCPU = 1; int argsShift = 0; for (; argsShift < args.length; argsShift++) { String argument = args[argsShift]; if (!argument.startsWith("-")) break; if (argument.equals("-performance")) { testPerformance = true; continue; } if (argument.equals("-verbose")) { verbose = true; continue; } if (argument.startsWith("-tolerance:")) { String percents = argument.substring("-tolerance:".length(), argument.length()); tolerance = Integer.parseInt(percents); if ((tolerance < 0) || (tolerance > 100)) { complain("Tolerance should be 0 to 100%: " + argument); return 2; // failure } continue; } if (argument.startsWith("-CPU:")) { String value = argument.substring("-CPU:".length(), argument.length()); numberOfCPU = Integer.parseInt(value); if (numberOfCPU < 1) { complain("Illegal number of CPU: " + argument); return 2; // failure } continue; } complain("Cannot recognize argument: args[" + argsShift + "]: " + argument); return 2; // failure } if ((args.length < argsShift + 1) || (args.length > argsShift + 2)) { complain("Illegal argument(s). Execute:"); complain( " java numeric010 [-verbose] [-performance] " + "[-tolerance:percents] [-CPU:number] matrixSize [threads]"); return 2; // failure } int size = Integer.parseInt(args[argsShift]); if ((size < 100) || (size > 10000)) { complain("Matrix size should be 100 to 1000 lines & columns."); return 2; // failure } int threads = size; if (args.length >= argsShift + 2) threads = Integer.parseInt(args[argsShift + 1]); if ((threads < 1) || (threads > size)) { complain("Threads number should be 1 to matrix size."); return 2; // failure } if ((size % threads) != 0) { complain("Threads number should evenly divide matrix size."); return 2; // failure } print("Preparing A[" + size + "," + size + "]:"); IntegerMatrix intA = new IntegerMatrix(size); IntegerMatrix intAA = new IntegerMatrix(size); LongMatrix longA = new LongMatrix(intA); LongMatrix longAA = new LongMatrix(intA); FloatMatrix floatA = new FloatMatrix(intA); FloatMatrix floatAA = new FloatMatrix(intA); DoubleMatrix doubleA = new DoubleMatrix(intA); DoubleMatrix doubleAA = new DoubleMatrix(intA); println(" done."); double elapsed[] = {0, 0}; for (int i = 0; i < 2; i++) { double seconds = elapsedTime((i == 0 ? 1 : threads), intA, intAA, longA, longAA, floatA, floatAA, doubleA, doubleAA); elapsed[i] = seconds; print("Checking accuracy:"); for (int line = 0; line < size; line++) for (int column = 0; column < size; column++) { if (intAA.value[line][column] != longAA.value[line][column]) { println(""); complain("Test failed:"); complain("Integer and Long results differ at:"); complain(" line=" + line + ", column=" + column); complain(" intAA.value[line][column]=" + intAA.value[line][column]); complain("longAA.value[line][column]=" + longAA.value[line][column]); return 2; // FAILED } if (intAA.value[line][column] != floatAA.value[line][column]) { println(""); complain("Test failed:"); complain("Integer and Float results differ at:"); complain(" line=" + line + ", column=" + column); complain(" intAA.value[line][column]=" + intAA.value[line][column]); complain("floatAA.value[line][column]=" + floatAA.value[line][column]); return 2; // FAILED } if (intAA.value[line][column] != doubleAA.value[line][column]) { println(""); complain("Test failed:"); complain("Integer and Double results differ at:"); complain(" line=" + line + ", column=" + column); complain(" intAA.value[line][column]=" + intAA.value[line][column]); complain("doubleAA.value[line][column]=" + doubleAA.value[line][column]); return 2; // FAILED } } println(" done."); } double overallTime = elapsed[0] + elapsed[1]; double averageTime = overallTime / 2; // 2 excutions double averagePerformance = 4 * size * size * (size + size) / averageTime / 1e6; println(""); println("Overall elapsed time: " + overallTime + " seconds."); println("Average elapsed time: " + averageTime + " seconds."); println("Average performance: " + averagePerformance + " MOPS"); if (testPerformance) { println(""); print("Checking performance: "); double elapsed1 = elapsed[0]; double elapsedM = elapsed[1] * numberOfCPU; if (elapsed1 > elapsedM * (1 + tolerance / 100)) { println(""); complain("Test failed:"); complain("Single-thread calculation is essentially slower:"); complain("Calculation time elapsed (seconds):"); complain(" single thread: " + elapsed[0]); complain(" multi-threads: " + elapsed[1]); complain(" number of CPU: " + numberOfCPU); complain(" tolerance: " + tolerance + "%"); return 2; // FAILED } println("done."); } println("Test passed."); return 0; // PASSED } /** * Return time (in seconds) elapsed for calculation of matrix * product A*A with int, long, * float, and double representations. */ private static double elapsedTime(int threads, IntegerMatrix intA, IntegerMatrix intAA, LongMatrix longA, LongMatrix longAA, FloatMatrix floatA, FloatMatrix floatAA, DoubleMatrix doubleA, DoubleMatrix doubleAA) { println(""); print("Computing A*A with " + threads + " thread(s):"); long mark1 = System.currentTimeMillis(); intAA.setSquareOf(intA, threads); longAA.setSquareOf(longA, threads); floatAA.setSquareOf(floatA, threads); doubleAA.setSquareOf(doubleA, threads); long mark2 = System.currentTimeMillis(); println(" done."); int size = intA.size(); double sec = (mark2 - mark1) / 1000.0; double perf = 4 * size * size * (size + size) / sec; println("Elapsed time: " + sec + " seconds"); println("Performance: " + perf / 1e6 + " MOPS"); return sec; } /** * Compute A*A for int matrix A. */ private static class IntegerMatrix { volatile int value[][]; /** * Number of lines and columns in this square matrix. */ public int size() { return value.length; } /** * New square matrix with random elements. */ public IntegerMatrix(int size) { value = new int[size][size]; for (int line = 0; line < size; line++) for (int column = 0; column < size; column++) value[line][column] = Math.round((float) ((1 - 2 * Math.random()) * size)); } /** * Assign this matrix with A*A. * * @param threads Split computation into the given number of threads. */ public void setSquareOf(IntegerMatrix A, int threads) { if (this.size() != A.size()) throw new IllegalArgumentException( "this.size() != A.size()"); if ((size() % threads) != 0) throw new IllegalArgumentException("size()%threads != 0"); int bunch = size() / threads; Thread task[] = new Thread[threads]; for (int t = 0; t < threads; t++) { int line0 = bunch * t; MatrixComputer computer = new MatrixComputer(value, A.value, line0, bunch); task[t] = new Thread(computer); } for (int t = 0; t < threads; t++) task[t].start(); for (int t = 0; t < threads; t++) if (task[t].isAlive()) try { task[t].join(); } catch (InterruptedException exception) { throw new RuntimeException(exception.toString()); } } /** * Thread to compute a bunch of lines of matrix square. */ private static class MatrixComputer implements Runnable { private int result[][]; private int source[][]; private int line0; private int bunch; /** * Register a task for matrix multiplication. */ public MatrixComputer( int result[][], int source[][], int line0, int bunch) { this.result = result; // reference to resulting matrix value this.source = source; // reference to matrix to be squared this.line0 = line0; // compute lines from line0 to ... this.bunch = bunch; // number of resulting lines to compute } /** * Do execute the task just registered for this thread. */ public void run() { int line1 = line0 + bunch; int size = result.length; for (int line = line0; line < line1; line++) for (int column = 0; column < size; column++) { int sum = 0; for (int i = 0; i < size; i++) sum += source[line][i] * source[i][column]; result[line][column] = sum; } } } } /** * Compute A*A for long matrix A. */ private static class LongMatrix { volatile long value[][]; /** * Number of lines and columns in this square matrix. */ public int size() { return value.length; } /** * New square matrix with the given integer elements. */ public LongMatrix(IntegerMatrix A) { int size = A.size(); value = new long[size][size]; for (int line = 0; line < size; line++) for (int column = 0; column < size; column++) value[line][column] = A.value[line][column]; } /** * Assign this matrix with A*A. * * @param threads Split computation into the given number of threads. */ public void setSquareOf(LongMatrix A, int threads) { if (this.size() != A.size()) throw new IllegalArgumentException( "this.size() != A.size()"); if ((size() % threads) != 0) throw new IllegalArgumentException("size()%threads != 0"); int bunch = size() / threads; Thread task[] = new Thread[threads]; for (int t = 0; t < threads; t++) { int line0 = bunch * t; MatrixComputer computer = new MatrixComputer(value, A.value, line0, bunch); task[t] = new Thread(computer); } for (int t = 0; t < threads; t++) task[t].start(); for (int t = 0; t < threads; t++) if (task[t].isAlive()) try { task[t].join(); } catch (InterruptedException exception) { throw new RuntimeException(exception.toString()); } } /** * Thread to compute a bunch of lines of matrix square. */ private static class MatrixComputer implements Runnable { private long result[][]; private long source[][]; private int line0; private int bunch; /** * Register a task for matrix multiplication. */ public MatrixComputer( long result[][], long source[][], int line0, int bunch) { this.result = result; // reference to resulting matrix value this.source = source; // reference to matrix to be squared this.line0 = line0; // compute lines from line0 to ... this.bunch = bunch; // number of resulting lines to compute } /** * Do execute the task just registered for this thread. */ public void run() { int line1 = line0 + bunch; int size = result.length; for (int line = line0; line < line1; line++) for (int column = 0; column < size; column++) { long sum = 0; for (int i = 0; i < size; i++) sum += source[line][i] * source[i][column]; result[line][column] = sum; } } } } /** * Compute A*A for float matrix A. */ private static class FloatMatrix { volatile float value[][]; /** * Number of lines and columns in this square matrix. */ public int size() { return value.length; } /** * New square matrix with the given integer elements. */ public FloatMatrix(IntegerMatrix A) { int size = A.size(); value = new float[size][size]; for (int line = 0; line < size; line++) for (int column = 0; column < size; column++) value[line][column] = A.value[line][column]; } /** * Assign this matrix with A*A. * * @param threads Split computation into the given number of threads. */ public void setSquareOf(FloatMatrix A, int threads) { if (this.size() != A.size()) throw new IllegalArgumentException( "this.size() != A.size()"); if ((size() % threads) != 0) throw new IllegalArgumentException("size()%threads != 0"); int bunch = size() / threads; Thread task[] = new Thread[threads]; for (int t = 0; t < threads; t++) { int line0 = bunch * t; MatrixComputer computer = new MatrixComputer(value, A.value, line0, bunch); task[t] = new Thread(computer); } for (int t = 0; t < threads; t++) task[t].start(); for (int t = 0; t < threads; t++) if (task[t].isAlive()) try { task[t].join(); } catch (InterruptedException exception) { throw new RuntimeException(exception.toString()); } } /** * Thread to compute a bunch of lines of matrix square. */ private static class MatrixComputer implements Runnable { private float result[][]; private float source[][]; private int line0; private int bunch; /** * Register a task for matrix multiplication. */ public MatrixComputer( float result[][], float source[][], int line0, int bunch) { this.result = result; // reference to resulting matrix value this.source = source; // reference to matrix to be squared this.line0 = line0; // compute lines from line0 to ... this.bunch = bunch; // number of resulting lines to compute } /** * Do execute the task just registered for this thread. */ public void run() { int line1 = line0 + bunch; int size = result.length; for (int line = line0; line < line1; line++) for (int column = 0; column < size; column++) { float sum = 0; for (int i = 0; i < size; i++) sum += source[line][i] * source[i][column]; result[line][column] = sum; } } } } /** * Compute A*A for float matrix A. */ private static class DoubleMatrix { volatile double value[][]; /** * Number of lines and columns in this square matrix. */ public int size() { return value.length; } /** * New square matrix with the given integer elements. */ public DoubleMatrix(IntegerMatrix A) { int size = A.size(); value = new double[size][size]; for (int line = 0; line < size; line++) for (int column = 0; column < size; column++) value[line][column] = A.value[line][column]; } /** * Assign this matrix with A*A. * * @param threads Split computation into the given number of threads. */ public void setSquareOf(DoubleMatrix A, int threads) { if (this.size() != A.size()) throw new IllegalArgumentException( "this.size() != A.size()"); if ((size() % threads) != 0) throw new IllegalArgumentException("size()%threads != 0"); int bunch = size() / threads; Thread task[] = new Thread[threads]; for (int t = 0; t < threads; t++) { int line0 = bunch * t; MatrixComputer computer = new MatrixComputer(value, A.value, line0, bunch); task[t] = new Thread(computer); } for (int t = 0; t < threads; t++) task[t].start(); for (int t = 0; t < threads; t++) if (task[t].isAlive()) try { task[t].join(); } catch (InterruptedException exception) { throw new RuntimeException(exception.toString()); } } /** * Thread to compute a bunch of lines of matrix square. */ private static class MatrixComputer implements Runnable { private double result[][]; private double source[][]; private int line0; private int bunch; /** * Register a task for matrix multiplication. */ public MatrixComputer( double result[][], double source[][], int line0, int bunch) { this.result = result; // reference to resulting matrix value this.source = source; // reference to matrix to be squared this.line0 = line0; // compute lines from line0 to ... this.bunch = bunch; // number of resulting lines to compute } /** * Do execute the task just registered for this thread. */ public void run() { int line1 = line0 + bunch; int size = result.length; for (int line = line0; line < line1; line++) for (int column = 0; column < size; column++) { double sum = 0; for (int i = 0; i < size; i++) sum += source[line][i] * source[i][column]; result[line][column] = sum; } } } } }