Path: blob/master/test/hotspot/jtreg/vmTestbase/jit/escape/LockElision/MatMul/MatMul.java
41160 views
/*1* Copyright (c) 2010, 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 randomness26*27* @summary converted from VM Testbase jit/escape/LockElision/MatMul.28* VM Testbase keywords: [jit, quick]29* VM Testbase readme:30* DESCRIPTION31* The test multiplies 2 matrices, first, by directly calculating matrix product32* elements, and second, by calculating them parallelly in diffenent threads.33* The results are compared then.34* The test, in addition to required locks, introduces locks on local variables or35* variables not escaping from the executing thread, and nests them manifoldly.36* In case of a buggy compiler, during lock elimination some code, required by37* the calulation may be eliminated as well, or the code may be overoptimized in38* some other way, causing difference in the execution results.39* The test has one parameter, -dim, which specifies the dimensions of matrices.40*41* @library /vmTestbase42* /test/lib43* @run main/othervm jit.escape.LockElision.MatMul.MatMul -dim 30 -threadCount 1044*/4546package jit.escape.LockElision.MatMul;4748import java.util.*;49import java.util.concurrent.CountDownLatch;50import java.util.concurrent.ExecutorService;51import java.util.concurrent.Executors;5253import nsk.share.Consts;54import nsk.share.Log;55import nsk.share.Pair;56import nsk.share.test.StressOptions;57import vm.share.options.Option;58import vm.share.options.OptionSupport;59import vm.share.options.Options;6061import jdk.test.lib.Utils;6263public class MatMul {6465@Option(name = "dim", description = "dimension of matrices")66int dim;6768@Option(name = "verbose", default_value = "false",69description = "verbose mode")70boolean verbose;7172@Option(name = "threadCount", description = "thread count")73int threadCount;7475@Options76StressOptions stressOptions = new StressOptions();7778private Log log;7980public static void main(String[] args) {81MatMul test = new MatMul();82OptionSupport.setup(test, args);83System.exit(Consts.JCK_STATUS_BASE + test.run());84}8586public int run() {87log = new Log(System.out, verbose);88log.display("Parallel matrix multiplication test");8990Matrix a = Matrix.randomMatrix(dim);91Matrix b = Matrix.randomMatrix(dim);92long t1, t2;9394t1 = System.currentTimeMillis();95Matrix serialResult = serialMul(a, b);96t2 = System.currentTimeMillis();97log.display("serial time: " + (t2 - t1) + "ms");9899try {100t1 = System.currentTimeMillis();101Matrix parallelResult = parallelMul(a, b,102threadCount * stressOptions.getThreadsFactor());103t2 = System.currentTimeMillis();104log.display("parallel time: " + (t2 - t1) + "ms");105106if (!serialResult.equals(parallelResult)) {107log.complain("a = \n" + a);108log.complain("b = \n" + b);109110log.complain("serial: a * b = \n" + serialResult);111log.complain("serial: a * b = \n" + parallelResult);112return Consts.TEST_FAILED;113}114return Consts.TEST_PASSED;115116} catch (CounterIncorrectStateException e) {117log.complain("incorrect state of counter " + e.counter.name);118log.complain("expected = " + e.counter.expected);119log.complain("actual " + e.counter.state());120return Consts.TEST_FAILED;121}122}123124public static int convolution(Seq<Integer> one, Seq<Integer> two) {125int res = 0;126int upperBound = Math.min(one.size(), two.size());127for (int i = 0; i < upperBound; i++) {128res += one.get(i) * two.get(i);129}130return res;131}132133/**134* calculate chunked convolutuion of two sequences135* <p/>136* This special version of this method:137* <pre>{@code138* public static int chunkedConvolution(Seq<Integer> one, Seq<Integer> two, int from, int to) {139* int res = 0;140* int upperBound = Math.min(Math.min(one.size(), two.size()), to + 1);141* for (int i = from; i < upperBound; i++) {142* res += one.get(i) * two.get(i);143* }144* return res;145* }}</pre>146* <p/>147* that tries to fool the Lock Elision optimization:148* Most lock objects in these lines are really thread local, so related synchronized blocks (dummy blocks) can be removed.149* But several synchronized blocks (all that protected by Counter instances) are really necessary, and removing them we obtain150* an incorrect result.151*152* @param one153* @param two154* @param from - lower bound of sum155* @param to - upper bound of sum156* @param local - reference ThreadLocal that will be used for calculations157* @param bCounter - Counter instance, need to perfom checks158*/159public static int chunkedConvolutionWithDummy(Seq<Integer> one,160Seq<Integer> two, int from, int to, ThreadLocals local,161Counter bCounter) {162ThreadLocals conv_local1 = new ThreadLocals(local, "conv_local1");163ThreadLocals conv_local2 = new ThreadLocals(conv_local1, "conv_local2");164ThreadLocals conv_local3 = new ThreadLocals(null, "conv_local3");165int res = 0;166synchronized (local) {167local.updateHash();168int upperBound = 0;169synchronized (conv_local1) {170upperBound = local.min(one.size(), two.size());171synchronized (two) {172//int upperBound = Math.min(Math.min(one.size(), two.size()), to + 1) :173upperBound = conv_local1.min(upperBound, to + 1);174synchronized (bCounter) {175bCounter.inc();176}177}178for (int i = from; i < upperBound; i++) {179synchronized (conv_local2) {180conv_local1.updateHash();181int prod = 0;182synchronized (one) {183int t = conv_local2.mult(one.get(i), two.get(i));184synchronized (conv_local3) {185prod = t;186187}188//res += one.get(i) * two.get(i)189res = conv_local3.sum(res, prod);190}191}192}193}194return res;195}196}197198public boolean productCheck(Matrix a, Matrix b) {199if (a == null || b == null) {200log.complain("null matrix!");201return false;202}203204if (a.dim != b.dim) {205log.complain("matrices dimension are differs");206return false;207}208return true;209}210211public Matrix serialMul(Matrix a, Matrix b) {212if (!productCheck(a, b)) {213throw new IllegalArgumentException();214}215216Matrix result = Matrix.zeroMatrix(a.dim);217for (int i = 0; i < a.dim; i++) {218for (int j = 0; j < a.dim; j++) {219result.set(i, j, convolution(a.row(i), b.column(j)));220}221}222return result;223}224225226/**227* Parallel multiplication of matrices.228* <p/>229* This special version of this method:230* <pre>{@code231* public Matrix parallelMul1(final Matrix a, final Matrix b, int threadCount) {232* if (!productCheck(a, b)) {233* throw new IllegalArgumentException();234* }235* final int dim = a.dim;236* final Matrix result = Matrix.zeroMatrix(dim);237* <p/>238* ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);239* final CountDownLatch latch = new CountDownLatch(threadCount);240* List<Pair<Integer, Integer>> parts = splitInterval(Pair.of(0, dim - 1), threadCount);241* for (final Pair<Integer, Integer> part : parts) {242* threadPool.submit(new Runnable() {243* @Override244* public void run() {245* for (int i = 0; i < dim; i++) {246* for (int j = 0; j < dim; j++) {247* synchronized (result) {248* int from = part.first;249* int to = part.second;250* result.add(i, j, chunkedConvolution(a.row(i), b.column(j), from, to));251* }252* }253* }254* latch.countDown();255* }256* });257* }258* <p/>259* try {260* latch.await();261* } catch (InterruptedException e) {262* e.printStackTrace();263* }264* threadPool.shutdown();265* return result;266* }}</pre>267* Lines marked with NOP comments need to fool the Lock Elision optimization:268* All lock objects in these lines are really thread local, so related synchronized blocks (dummy blocks) can be removed.269* But several synchronized blocks (that are nested in dummy blocks) are really necessary, and removing them we obtain270* an incorrect result.271*272* @param a first operand273* @param b second operand274* @param threadCount number of threads that will be used for calculations275* @return product of matrices a and b276*/277public Matrix parallelMul(final Matrix a, final Matrix b, int threadCount)278throws CounterIncorrectStateException {279if (!productCheck(a, b)) {280throw new IllegalArgumentException();281}282final int dim = a.dim;283final Matrix result = Matrix.zeroMatrix(dim);284285ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);286final CountDownLatch latch = new CountDownLatch(threadCount);287List<Pair<Integer, Integer>> parts = splitInterval(Pair.of(0, dim - 1),288threadCount);289290final Counter lCounter1 = new Counter(threadCount, "lCounter1");291final Counter lCounter2 = new Counter(threadCount, "lCounter2");292final Counter lCounter3 = new Counter(threadCount, "lCounter3");293294final Counter bCounter1 = new Counter(threadCount * dim * dim,295"bCounter1");296final Counter bCounter2 = new Counter(threadCount * dim * dim,297"bCounter2");298final Counter bCounter3 = new Counter(threadCount * dim * dim,299"bCounter3");300301final Counter[] counters = {lCounter1, lCounter2, lCounter3,302bCounter1, bCounter2, bCounter3};303304final Map<Pair<Integer, Integer>, ThreadLocals> locals1305= CollectionsUtils.newHashMap();306final Map<Pair<Integer, Integer>, ThreadLocals> locals2307= CollectionsUtils.newHashMap();308final Map<Pair<Integer, Integer>, ThreadLocals> locals3309= CollectionsUtils.newHashMap();310311for (final Pair<Integer, Integer> part : parts) {312313ThreadLocals local1 = new ThreadLocals(null,314"locals1[" + part + "]");315ThreadLocals local2 = new ThreadLocals(local1,316"locals2[" + part + "]");317ThreadLocals local3 = new ThreadLocals(local2,318"locals3[" + part + "]");319320locals1.put(part, local1);321locals2.put(part, local2);322locals3.put(part, local3);323}324325for (final Pair<Integer, Integer> part : parts) {326threadPool.submit(new Runnable() {327@Override328public void run() {329ThreadLocals local1 = locals1.get(part);330ThreadLocals local2 = locals2.get(part);331ThreadLocals local3 = locals3.get(part);332ThreadLocals local4 = locals3.get(part);333synchronized (local1) {334local1.updateHash();335synchronized (lCounter1) {336lCounter1.inc();337}338synchronized (lCounter3) {339synchronized (local2) {340local2.updateHash();341lCounter3.inc();342}343}344synchronized (new Object()) {345synchronized (lCounter2) {346lCounter2.inc();347}348for (int i = 0; i < dim; i++) {349for (int j = 0; j < dim; j++) {350synchronized (bCounter1) {351synchronized (new Object()) {352bCounter1.inc();353}354}355synchronized (local3) {356local3.updateHash();357synchronized (bCounter2) {358bCounter2.inc();359}360synchronized (result) {361local1.updateHash();362synchronized (local2) {363local2.updateHash();364int from = part.first;365int to = part.second;366result.add(i, j,367chunkedConvolutionWithDummy(368a.row(i),369b.column(j),370from, to,371local4,372bCounter3));373}374}375}376}377}378}379}380latch.countDown();381}382});383}384385try {386latch.await();387} catch (InterruptedException e) {388e.printStackTrace();389}390391threadPool.shutdown();392for (final Pair<Integer, Integer> part : parts) {393log.display(394"hash for " + part + " = " + locals1.get(part).getHash());395}396397398for (Counter counter : counters) {399if (!counter.check()) {400throw new CounterIncorrectStateException(counter);401}402}403return result;404}405406/**407* Split interval into parts408*409* @param interval - pair than encode bounds of interval410* @param partCount - count of parts411* @return list of pairs than encode bounds of parts412*/413public static List<Pair<Integer, Integer>> splitInterval(414Pair<Integer, Integer> interval, int partCount) {415if (partCount == 0) {416throw new IllegalArgumentException();417}418419if (partCount == 1) {420return CollectionsUtils.asList(interval);421}422423int intervalSize = interval.second - interval.first + 1;424int partSize = intervalSize / partCount;425426List<Pair<Integer, Integer>> init = splitInterval(427Pair.of(interval.first, interval.second - partSize),428partCount - 1);429Pair<Integer, Integer> lastPart = Pair430.of(interval.second - partSize + 1, interval.second);431432return CollectionsUtils.append(init, lastPart);433}434435public static class Counter {436private int state;437438public final int expected;439public final String name;440441public void inc() {442state++;443}444445public int state() {446return state;447}448449public boolean check() {450return state == expected;451}452453public Counter(int expected, String name) {454this.expected = expected;455this.name = name;456}457}458459private static class CounterIncorrectStateException extends Exception {460public final Counter counter;461462public CounterIncorrectStateException(Counter counter) {463this.counter = counter;464}465}466467private static abstract class Seq<E> implements Iterable<E> {468@Override469public Iterator<E> iterator() {470return new Iterator<E>() {471private int p = 0;472473@Override474public boolean hasNext() {475return p < size();476}477478@Override479public E next() {480return get(p++);481}482483@Override484public void remove() {485}486};487}488489public abstract E get(int i);490491public abstract int size();492}493494private static class CollectionsUtils {495496public static <K, V> Map<K, V> newHashMap() {497return new HashMap<K, V>();498}499500public static <E> List<E> newArrayList() {501return new ArrayList<E>();502}503504public static <E> List<E> newArrayList(Collection<E> collection) {505return new ArrayList<E>(collection);506}507508public static <E> List<E> asList(E e) {509List<E> result = newArrayList();510result.add(e);511return result;512}513514public static <E> List<E> append(List<E> init, E last) {515List<E> result = newArrayList(init);516result.add(last);517return result;518}519}520521private static class Matrix {522523public final int dim;524private int[] coeffs;525526private Matrix(int dim) {527this.dim = dim;528this.coeffs = new int[dim * dim];529}530531public void set(int i, int j, int value) {532coeffs[i * dim + j] = value;533}534535public void add(int i, int j, int value) {536coeffs[i * dim + j] += value;537}538539public int get(int i, int j) {540return coeffs[i * dim + j];541}542543public Seq<Integer> row(final int i) {544return new Seq<Integer>() {545@Override546public Integer get(int j) {547return Matrix.this.get(i, j);548}549550@Override551public int size() {552return Matrix.this.dim;553}554};555}556557public Seq<Integer> column(final int j) {558return new Seq<Integer>() {559@Override560public Integer get(int i) {561return Matrix.this.get(i, j);562}563564@Override565public int size() {566return Matrix.this.dim;567}568};569}570571@Override572public String toString() {573StringBuilder builder = new StringBuilder();574for (int i = 0; i < dim; i++) {575for (int j = 0; j < dim; j++) {576builder.append((j == 0) ? "" : "\t\t");577builder.append(get(i, j));578}579builder.append("\n");580}581return builder.toString();582}583584@Override585public boolean equals(Object other) {586if (!(other instanceof Matrix)) {587return false;588}589590Matrix b = (Matrix) other;591if (b.dim != this.dim) {592return false;593}594for (int i = 0; i < dim; i++) {595for (int j = 0; j < dim; j++) {596if (this.get(i, j) != b.get(i, j)) {597return false;598}599}600}601return true;602}603604private static Random random = Utils.getRandomInstance();605606public static Matrix randomMatrix(int dim) {607Matrix result = new Matrix(dim);608for (int i = 0; i < dim; i++) {609for (int j = 0; j < dim; j++) {610result.set(i, j, random.nextInt(50));611}612}613return result;614}615616public static Matrix zeroMatrix(int dim) {617Matrix result = new Matrix(dim);618for (int i = 0; i < dim; i++) {619for (int j = 0; j < dim; j++) {620result.set(i, j, 0);621}622}623return result;624}625}626627/**628* All instances of this class will be used in thread local context629*/630private static class ThreadLocals {631private static final int HASH_BOUND = 424242;632633private ThreadLocals parent;634private int hash = 42;635public final String name;636637public ThreadLocals(ThreadLocals parent, String name) {638this.parent = parent;639this.name = name;640}641642public int min(int a, int b) {643updateHash(a + b + 1);644return Math.min(a, b);645}646647public int mult(int a, int b) {648updateHash(a + b + 2);649return a * b;650}651652public int sum(int a, int b) {653updateHash(a + b + 3);654return a + b;655}656657658public int updateHash() {659return updateHash(42);660}661662public int updateHash(int data) {663hash = (hash + data) % HASH_BOUND;664if (parent != null) {665hash = parent.updateHash(hash) % HASH_BOUND;666}667return hash;668}669670public int getHash() {671return hash;672}673}674}675676677