Repository: incubator-systemml Updated Branches: refs/heads/master b0d3c6c85 -> 71013e758
[SYSTEMML-774] Robustness thread-unsafe default sparse blocks Closes #186. Project: http://git-wip-us.apache.org/repos/asf/incubator-systemml/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-systemml/commit/8cb28b82 Tree: http://git-wip-us.apache.org/repos/asf/incubator-systemml/tree/8cb28b82 Diff: http://git-wip-us.apache.org/repos/asf/incubator-systemml/diff/8cb28b82 Branch: refs/heads/master Commit: 8cb28b82a8b74aad7c318d252698ce438508f338 Parents: 01d9fdb Author: Nakul Jindal <[email protected]> Authored: Sat Jul 16 17:17:32 2016 -0700 Committer: Matthias Boehm <[email protected]> Committed: Sat Jul 16 17:17:32 2016 -0700 ---------------------------------------------------------------------- .../sysml/runtime/io/MatrixReaderFactory.java | 14 +++--- .../sysml/runtime/matrix/data/LibMatrixAgg.java | 14 +++--- .../sysml/runtime/matrix/data/LibMatrixDNN.java | 41 ++++++++++++----- .../runtime/matrix/data/LibMatrixDatagen.java | 11 +++-- .../runtime/matrix/data/LibMatrixMult.java | 47 +++++++++++++++----- .../runtime/matrix/data/LibMatrixReorg.java | 2 +- .../sysml/runtime/matrix/data/MatrixBlock.java | 21 +++++++++ 7 files changed, 114 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/8cb28b82/src/main/java/org/apache/sysml/runtime/io/MatrixReaderFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/sysml/runtime/io/MatrixReaderFactory.java b/src/main/java/org/apache/sysml/runtime/io/MatrixReaderFactory.java index 8da8fd7..f019107 100644 --- a/src/main/java/org/apache/sysml/runtime/io/MatrixReaderFactory.java +++ b/src/main/java/org/apache/sysml/runtime/io/MatrixReaderFactory.java @@ -24,6 +24,8 @@ import org.apache.sysml.conf.ConfigurationManager; import org.apache.sysml.runtime.DMLRuntimeException; import org.apache.sysml.runtime.matrix.data.CSVFileFormatProperties; import org.apache.sysml.runtime.matrix.data.InputInfo; +import org.apache.sysml.runtime.matrix.data.MatrixBlock; +import org.apache.sysml.runtime.matrix.data.SparseBlock; /** * @@ -44,14 +46,14 @@ public class MatrixReaderFactory if( iinfo == InputInfo.TextCellInputInfo || iinfo == InputInfo.MatrixMarketInputInfo ) { - if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_TEXTFORMATS) ) + if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_TEXTFORMATS) && MatrixBlock.DEFAULT_SPARSEBLOCK == SparseBlock.Type.MCSR ) reader = new ReaderTextCellParallel( iinfo ); else reader = new ReaderTextCell( iinfo ); } else if( iinfo == InputInfo.CSVInputInfo ) { - if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_TEXTFORMATS) ) + if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_TEXTFORMATS) && MatrixBlock.DEFAULT_SPARSEBLOCK == SparseBlock.Type.MCSR ) reader = new ReaderTextCSVParallel(new CSVFileFormatProperties()); else reader = new ReaderTextCSV(new CSVFileFormatProperties()); @@ -59,7 +61,7 @@ public class MatrixReaderFactory else if( iinfo == InputInfo.BinaryCellInputInfo ) reader = new ReaderBinaryCell(); else if( iinfo == InputInfo.BinaryBlockInputInfo ) { - if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_BINARYFORMATS) ) + if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_BINARYFORMATS) && MatrixBlock.DEFAULT_SPARSEBLOCK == SparseBlock.Type.MCSR ) reader = new ReaderBinaryBlockParallel( false ); else reader = new ReaderBinaryBlock( false ); @@ -89,13 +91,13 @@ public class MatrixReaderFactory InputInfo iinfo = props.inputInfo; if( iinfo == InputInfo.TextCellInputInfo || iinfo == InputInfo.MatrixMarketInputInfo ) { - if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_TEXTFORMATS) ) + if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_TEXTFORMATS) && MatrixBlock.DEFAULT_SPARSEBLOCK == SparseBlock.Type.MCSR ) reader = new ReaderTextCellParallel( iinfo ); else reader = new ReaderTextCell( iinfo ); } else if( iinfo == InputInfo.CSVInputInfo ) { - if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_TEXTFORMATS) ) + if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_TEXTFORMATS) && MatrixBlock.DEFAULT_SPARSEBLOCK == SparseBlock.Type.MCSR ) reader = new ReaderTextCSVParallel( props.formatProperties!=null ? (CSVFileFormatProperties)props.formatProperties : new CSVFileFormatProperties()); else reader = new ReaderTextCSV( props.formatProperties!=null ? (CSVFileFormatProperties)props.formatProperties : new CSVFileFormatProperties()); @@ -103,7 +105,7 @@ public class MatrixReaderFactory else if( iinfo == InputInfo.BinaryCellInputInfo ) reader = new ReaderBinaryCell(); else if( iinfo == InputInfo.BinaryBlockInputInfo ) { - if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_BINARYFORMATS) ) + if( ConfigurationManager.getCompilerConfigFlag(ConfigType.PARALLEL_CP_READ_BINARYFORMATS) && MatrixBlock.DEFAULT_SPARSEBLOCK == SparseBlock.Type.MCSR ) reader = new ReaderBinaryBlockParallel( props.localFS ); else reader = new ReaderBinaryBlock( props.localFS ); http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/8cb28b82/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixAgg.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixAgg.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixAgg.java index 21c40e6..1dc6a3c 100644 --- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixAgg.java +++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixAgg.java @@ -230,7 +230,8 @@ public class LibMatrixAgg { //fall back to sequential version if necessary if( k <= 1 || (long)in.rlen*in.clen < PAR_NUMCELL_THRESHOLD || in.rlen <= k - || (!(uaop.indexFn instanceof ReduceCol) && out.clen*8*k > PAR_INTERMEDIATE_SIZE_THRESHOLD ) ) { + || (!(uaop.indexFn instanceof ReduceCol) && out.clen*8*k > PAR_INTERMEDIATE_SIZE_THRESHOLD ) || + !out.isThreadSafe()) { aggregateUnaryMatrix(in, out, uaop); return; } @@ -255,6 +256,8 @@ public class LibMatrixAgg out.allocateDenseBlock(); } + + //core multi-threaded unary aggregate computation //(currently: always parallelization over number of rows) try { @@ -343,7 +346,7 @@ public class LibMatrixAgg //fall back to sequential if necessary or agg not supported if( k <= 1 || (long)in.rlen*in.clen < PAR_NUMCELL_THRESHOLD || in.rlen <= k - || out.clen*8*k > PAR_INTERMEDIATE_SIZE_THRESHOLD || uaop == null ) { + || out.clen*8*k > PAR_INTERMEDIATE_SIZE_THRESHOLD || uaop == null || !out.isThreadSafe()) { return cumaggregateUnaryMatrix(in, out, uop); } @@ -540,9 +543,12 @@ public class LibMatrixAgg public static void groupedAggregate(MatrixBlock groups, MatrixBlock target, MatrixBlock weights, MatrixBlock result, int numGroups, Operator op, int k) throws DMLRuntimeException { + //preprocessing + result.sparse = false; // Do not need to check for isThreadSafe, because dense is assumed to be thread safe + //fall back to sequential version if necessary boolean rowVector = (target.getNumRows()==1 && target.getNumColumns()>1); - if( k <= 1 || (long)target.rlen*target.clen < PAR_NUMCELL_THRESHOLD || rowVector || target.clen==1 ) { + if( k <= 1 || (long)target.rlen*target.clen < PAR_NUMCELL_THRESHOLD || rowVector || target.clen==1) { groupedAggregate(groups, target, weights, result, numGroups, op); return; } @@ -551,8 +557,6 @@ public class LibMatrixAgg throw new DMLRuntimeException("Invalid operator (" + op + ") encountered while processing groupedAggregate."); } - //preprocessing - result.sparse = false; result.allocateDenseBlock(); //core multi-threaded grouped aggregate computation http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/8cb28b82/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNN.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNN.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNN.java index 3014b49..83f60ee 100644 --- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNN.java +++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNN.java @@ -193,6 +193,10 @@ public class LibMatrixDNN { public void setReuseNonZeroedOutput(boolean reuseNonZeroedOutput) { this.reuseNonZeroedOutput = reuseNonZeroedOutput; } + + public boolean isOutputThreadSafe() { + return output.isThreadSafe(); + } } public static void conv2d_backward_filter(MatrixBlock input, MatrixBlock dout, MatrixBlock outputBlock, ConvolutionParameters params) throws DMLRuntimeException { @@ -475,7 +479,7 @@ public class LibMatrixDNN { } } else - runParallelConvTask(constrainedNumThreads, params.K, TaskType.LoopBasedConv2d, params); + runConvTask(constrainedNumThreads, params.K, TaskType.LoopBasedConv2d, params); } public static void maxpooling_backward(MatrixBlock input, MatrixBlock dout, MatrixBlock outputBlock, ConvolutionParameters params) throws DMLRuntimeException { @@ -508,7 +512,7 @@ public class LibMatrixDNN { } } else { - runParallelConvTask(constrainedNumThreads, params.C, TaskType.MaxPooling_Backward, params); + runConvTask(constrainedNumThreads, params.C, TaskType.MaxPooling_Backward, params); } } @@ -815,7 +819,7 @@ public class LibMatrixDNN { } } else { - runParallelConvTask(constrainedNumThreads, params.C, TaskType.MaxPooling_Forward, params); + runConvTask(constrainedNumThreads, params.C, TaskType.MaxPooling_Forward, params); } outputBlock.setNonZeros(params.outputNNZ.get()); } @@ -872,7 +876,7 @@ public class LibMatrixDNN { } } else { - runParallelConvTask(constrainedNumThreads, 1, TaskType.Rotate180, params); + runConvTask(constrainedNumThreads, 1, TaskType.Rotate180, params); } outputBlock.setNonZeros(input.getNonZeros()); // As number of non-zeros doesnot change for rotate180 } @@ -914,7 +918,7 @@ public class LibMatrixDNN { } } else { - runParallelConvTask(constrainedNumThreads, 1, TaskType.ReshapeCol, params); + runConvTask(constrainedNumThreads, 1, TaskType.ReshapeCol, params); } outputBlock.setNonZeros(input.getNonZeros()); // As number of non-zeros doesnot change for reshape_col } @@ -945,7 +949,25 @@ public class LibMatrixDNN { return ret; } - private static void runParallelConvTask(int constrainedNumThreads, int Z, TaskType type, ConvolutionParameters params) throws DMLRuntimeException { + private static void runConvTask(int constrainedNumThreads, int Z, TaskType type, ConvolutionParameters params) throws DMLRuntimeException { + if (params.isOutputThreadSafe() && constrainedNumThreads > 1) { + runParallelConvTask(constrainedNumThreads, Z, type, params); + } else { + runSequentialConvTask(Z, type, params); + } + } + + private static void runSequentialConvTask(int Z, TaskType type, ConvolutionParameters params) throws DMLRuntimeException { + ConvTask task = new ConvTask(0, params.N, 0, Z, type, params); + try { + task.call(); + } catch (Exception e) { + throw new DMLRuntimeException("Error while executing single-threaded " + type.name(), e); + } + } + + private static void runParallelConvTask(int constrainedNumThreads, int Z, TaskType type, + ConvolutionParameters params) throws DMLRuntimeException { ArrayList<ConvTask> tasks = new ArrayList<ConvTask>(); int [] taskSizes = getTaskSize(constrainedNumThreads, params.N, Z); for (int n = 0; n < params.N; n += taskSizes[0]) { @@ -967,7 +989,6 @@ public class LibMatrixDNN { } catch (ExecutionException e) { throw new DMLRuntimeException("Error while executing multi-threaded " + type.name(), e); } - } private static class ConvTask implements Callable<Object> { @@ -1085,7 +1106,7 @@ public class LibMatrixDNN { } } else { - runParallelConvTask(constrainedNumThreads, params.C, TaskType.Im2Col, params); + runConvTask(constrainedNumThreads, params.C, TaskType.Im2Col, params); } outputBlock.setNonZeros(params.outputNNZ.get()); } @@ -1106,7 +1127,7 @@ public class LibMatrixDNN { } else { // Parallel col2im - runParallelConvTask(constrainedNumThreads, params.C, TaskType.Col2Im, params); + runConvTask(constrainedNumThreads, params.C, TaskType.Col2Im, params); } } @@ -1207,4 +1228,4 @@ public class LibMatrixDNN { params.outputNNZ.addAndGet(tmpNNZ); } -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/8cb28b82/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDatagen.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDatagen.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDatagen.java index b3f27ea..19b2dff 100644 --- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDatagen.java +++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDatagen.java @@ -391,14 +391,19 @@ public class LibMatrixDatagen final long estnnz = ((min==0.0 && max==0.0) ? 0 : (long)(sparsity * rows * cols)); boolean lsparse = MatrixBlock.evalSparseFormatInMemory( rows, cols, estnnz ); - //fallback to sequential if single rowblock or too few cells - if( k<=1 || (rows <= rpb && lsparse) || (long)rows*cols < PAR_NUMCELL_THRESHOLD ) { + //fallback to sequential if single rowblock or too few cells or if MatrixBlock is not thread safe + if( k<=1 || (rows <= rpb && lsparse) || (long)rows*cols < PAR_NUMCELL_THRESHOLD) { generateRandomMatrix(out, rgen, nnzInBlocks, bigrand, bSeed); return; } out.reset(rows, cols, lsparse); + if (!out.isThreadSafe()) { + generateRandomMatrix(out, rgen, nnzInBlocks, bigrand, bSeed); + return; + } + //special case shortcuts for efficiency if ( rgen._pdf.equalsIgnoreCase(RAND_PDF_UNIFORM)) { if ( min == 0.0 && max == 0.0 ) { //all zeros @@ -418,7 +423,7 @@ public class LibMatrixDatagen out.allocateSparseRowsBlock(); else out.allocateDenseBlock(); - + int nrb = (int) Math.ceil((double)rows/rpb); int ncb = (int) Math.ceil((double)cols/cpb); http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/8cb28b82/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixMult.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixMult.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixMult.java index c875e45..e054dd8 100644 --- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixMult.java +++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixMult.java @@ -176,6 +176,11 @@ public class LibMatrixMult else ret.allocateSparseRowsBlock(); + if (!ret.isThreadSafe()){ + matrixMult(m1, m2, ret); + return; + } + //prepare row-upper for special cases of vector-matrix / matrix-matrix boolean pm2r = checkParMatrixMultRightInputRows(m1, m2, k); boolean pm2c = checkParMatrixMultRightInputCols(m1, m2, k, pm2r); @@ -281,10 +286,13 @@ public class LibMatrixMult return; } + //pre-processing + ret.sparse = false; // MatrixBlock is assumed to be thread safe if dense + //check too high additional memory requirements (fallback to sequential) //check too small workload in terms of flops (fallback to sequential too) if( 8L * mV.rlen * k > MEM_OVERHEAD_THRESHOLD - || 4L * mX.rlen * mX.clen < PAR_MINFLOP_THRESHOLD ) + || 4L * mX.rlen * mX.clen < PAR_MINFLOP_THRESHOLD) { matrixMultChain(mX, mV, mW, ret, ct); return; @@ -293,7 +301,6 @@ public class LibMatrixMult //Timing time = new Timing(true); //pre-processing - ret.sparse = false; ret.allocateDenseBlock(); //core matrix mult chain computation @@ -377,11 +384,14 @@ public class LibMatrixMult return; } + // pre-processing + ret.sparse = false; // MatrixBlock is assumed to be thread safe if dense + //check no parallelization benefit (fallback to sequential) //check too small workload in terms of flops (fallback to sequential too) if( ret.rlen == 1 || leftTranspose && 1L * m1.rlen * m1.clen * m1.clen < PAR_MINFLOP_THRESHOLD - || !leftTranspose && 1L * m1.clen * m1.rlen * m1.rlen < PAR_MINFLOP_THRESHOLD ) + || !leftTranspose && 1L * m1.clen * m1.rlen * m1.rlen < PAR_MINFLOP_THRESHOLD) { matrixMultTransposeSelf(m1, ret, leftTranspose); return; @@ -391,7 +401,6 @@ public class LibMatrixMult //pre-processing m1 = prepMatrixMultTransposeSelfInput(m1, leftTranspose); - ret.sparse = false; ret.allocateDenseBlock(); //core multi-threaded matrix mult computation @@ -476,6 +485,9 @@ public class LibMatrixMult if( pm1.isEmptyBlock(false) || m2.isEmptyBlock(false) ) return; + //pre-processing + ret1.sparse = false; // MatrixBlock is assumed to be thread safe if dense + //check no parallelization benefit (fallback to sequential) if (pm1.rlen == 1) { matrixMultPermute(pm1, m2, ret1, ret2); @@ -485,7 +497,6 @@ public class LibMatrixMult //Timing time = new Timing(true); //allocate first output block (second allocated if needed) - ret1.sparse = false; ret1.allocateDenseBlock(); try @@ -572,7 +583,7 @@ public class LibMatrixMult } //check no parallelization benefit (fallback to sequential) - if (mX.rlen == 1) { + if (mX.rlen == 1 || !ret.isThreadSafe()) { matrixMultWSLoss(mX, mU, mV, mW, ret, wt); return; } @@ -658,8 +669,11 @@ public class LibMatrixMult return; } + //pre-processing + ret.sparse = mW.sparse; + //check no parallelization benefit (fallback to sequential) - if (mW.rlen == 1) { + if (mW.rlen == 1 || !ret.isThreadSafe()) { matrixMultWSigmoid(mW, mU, mV, ret, wt); return; } @@ -667,7 +681,6 @@ public class LibMatrixMult //Timing time = new Timing(true); //pre-processing - ret.sparse = mW.sparse; ret.allocateDenseOrSparseBlock(); try @@ -778,6 +791,11 @@ public class LibMatrixMult ret.sparse = wt.isBasic()?mW.sparse:false; ret.allocateDenseOrSparseBlock(); + if (!ret.isThreadSafe()){ + matrixMultWDivMM(mW, mU, mV, mX, ret, wt); + return; + } + try { ExecutorService pool = Executors.newFixedThreadPool(k); @@ -876,6 +894,11 @@ public class LibMatrixMult ret.sparse = false; ret.allocateDenseBlock(); + if (!ret.isThreadSafe()){ + matrixMultWCeMM(mW, mU, mV, eps, ret, wt); + return; + } + try { ExecutorService pool = Executors.newFixedThreadPool(k); @@ -954,9 +977,12 @@ public class LibMatrixMult ret.examSparsity(); //turn empty dense into sparse return; } - + + //pre-processing + ret.sparse = mW.sparse; + //check no parallelization benefit (fallback to sequential) - if (mW.rlen == 1) { + if (mW.rlen == 1 || !ret.isThreadSafe()) { matrixMultWuMM(mW, mU, mV, ret, wt, fn); return; } @@ -964,7 +990,6 @@ public class LibMatrixMult //Timing time = new Timing(true); //pre-processing - ret.sparse = mW.sparse; ret.allocateDenseOrSparseBlock(); try http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/8cb28b82/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixReorg.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixReorg.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixReorg.java index c1b859f..c5674bb 100644 --- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixReorg.java +++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixReorg.java @@ -187,7 +187,7 @@ public class LibMatrixReorg //redirect small or special cases to sequential execution if( in.isEmptyBlock(false) || (in.rlen * in.clen < PAR_NUMCELL_THRESHOLD) || (SHALLOW_DENSE_VECTOR_TRANSPOSE && !in.sparse && !out.sparse && (in.rlen==1 || in.clen==1) ) - || (in.sparse && !out.sparse && in.rlen==1) || out.sparse ) + || (in.sparse && !out.sparse && in.rlen==1) || out.sparse || !out.isThreadSafe()) { return transpose(in, out); } http://git-wip-us.apache.org/repos/asf/incubator-systemml/blob/8cb28b82/src/main/java/org/apache/sysml/runtime/matrix/data/MatrixBlock.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/MatrixBlock.java b/src/main/java/org/apache/sysml/runtime/matrix/data/MatrixBlock.java index 9b40cbd..cfff1f8 100644 --- a/src/main/java/org/apache/sysml/runtime/matrix/data/MatrixBlock.java +++ b/src/main/java/org/apache/sysml/runtime/matrix/data/MatrixBlock.java @@ -6133,6 +6133,27 @@ public class MatrixBlock extends MatrixValue implements CacheBlock, Externalizab return (MatrixBlock) block; } + /** + * Whether concurrent modification operations are allowed + * This method is to be used by methods that attempt to do a task concurrently, + * like in {@link LibMatrixDatagen#generateRandomMatrix(MatrixBlock, RandomMatrixGenerator, long[], Well1024a, long, int)} + * @return + */ + public boolean isThreadSafe() { + if (sparse){ + if (sparseBlock == null){ + // It is assumed that MCSR is the only safe sparse block implementation available. + return DEFAULT_SPARSEBLOCK == SparseBlock.Type.MCSR; + } + else { + return sparseBlock.isThreadSafe(); + } + } + else { + return true; + } + } + public void print() { System.out.println("sparse = "+sparse);
