You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@systemml.apache.org by mb...@apache.org on 2018/02/10 06:23:55 UTC

[1/2] systemml git commit: [SYSTEMML-2041] Performance/memory-efficiency ultra-sparse conv2d

Repository: systemml
Updated Branches:
  refs/heads/master 5c1ac17cc -> 401b79657


[SYSTEMML-2041] Performance/memory-efficiency ultra-sparse conv2d 

This patch improves the performance and memory-efficiency of
ultra-sparse conv2d operations by allocating the output in sparse
format, forcing ultra-sparse matrix multiplications (with sparse output)
and generalizing all related operations to handle sparse intermediates. 

Furthermore, this also includes a fix for ultra-sparse matrix
multiplications where the rhs is much larger than the lhs which triggers
parallelization over rows in the rhs instead of the lhs, which is not
supported by ultra-sparse matrix multiplications.


Project: http://git-wip-us.apache.org/repos/asf/systemml/repo
Commit: http://git-wip-us.apache.org/repos/asf/systemml/commit/08c7f00d
Tree: http://git-wip-us.apache.org/repos/asf/systemml/tree/08c7f00d
Diff: http://git-wip-us.apache.org/repos/asf/systemml/diff/08c7f00d

Branch: refs/heads/master
Commit: 08c7f00d5b3d7383ca0cf0e893736026c8ee7151
Parents: 5c1ac17
Author: Matthias Boehm <mb...@gmail.com>
Authored: Fri Feb 9 21:40:22 2018 -0800
Committer: Matthias Boehm <mb...@gmail.com>
Committed: Fri Feb 9 21:40:22 2018 -0800

----------------------------------------------------------------------
 .../cp/ConvolutionCPInstruction.java            |  4 +-
 .../runtime/matrix/data/LibMatrixDNNConv2d.java | 58 ++++++++++++-------
 .../runtime/matrix/data/LibMatrixDNNHelper.java |  2 +-
 .../runtime/matrix/data/LibMatrixDNNIm2Col.java | 16 +++--
 .../runtime/matrix/data/LibMatrixMult.java      | 61 +++++++++-----------
 .../runtime/matrix/data/LibMatrixNative.java    |  2 +-
 6 files changed, 76 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/systemml/blob/08c7f00d/src/main/java/org/apache/sysml/runtime/instructions/cp/ConvolutionCPInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/runtime/instructions/cp/ConvolutionCPInstruction.java b/src/main/java/org/apache/sysml/runtime/instructions/cp/ConvolutionCPInstruction.java
index e32c3cf..34daf33 100644
--- a/src/main/java/org/apache/sysml/runtime/instructions/cp/ConvolutionCPInstruction.java
+++ b/src/main/java/org/apache/sysml/runtime/instructions/cp/ConvolutionCPInstruction.java
@@ -440,7 +440,9 @@ public class ConvolutionCPInstruction extends UnaryCPInstruction {
 				outputBlock = new MatrixBlock(N, K*P*Q, true);
 			}
 			else {
-				outputBlock = new MatrixBlock(N, K*P*Q, false).allocateBlock();
+				boolean sparse = matBlock.isUltraSparse(false) && params.bias == null
+					&& matBlock.getInMemorySize() < MatrixBlock.estimateSizeDenseInMemory(N, K*P*Q);
+				outputBlock = new MatrixBlock(N, K*P*Q, sparse).allocateBlock();
 				if(params.enableNative && !isFilterSparse(filter) && !matBlock.isInSparseFormat())
 					LibMatrixNative.conv2d(matBlock, filter, outputBlock, params);
 				else

http://git-wip-us.apache.org/repos/asf/systemml/blob/08c7f00d/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNConv2d.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNConv2d.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNConv2d.java
index e30de2c..22a5e1a 100644
--- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNConv2d.java
+++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNConv2d.java
@@ -52,7 +52,7 @@ public class LibMatrixDNNConv2d
 		
 		MatrixBlock in1 = params.input1;
 		boolean isEmptyDenseInput = !in1.isInSparseFormat() && in1.denseBlock == null;
-		boolean isTransPref = in1.sparse && !params.input2.sparse && 
+		boolean isTransPref = in1.sparse && !params.input2.sparse && !params.output.sparse &&
 			MatrixBlock.evalSparseFormatInMemory(in1.clen, in1.rlen, in1.nonZeros);
 		boolean applyNative = isEligibleForConv2dSparse(params)
 			&& !(!isEmptyDenseInput && isTransPref);
@@ -171,8 +171,8 @@ public class LibMatrixDNNConv2d
 		@Override
 		public Long call() throws Exception {
 			final int PQ = _params.P*_params.Q, K = _params.K, CRS = _params.C*_params.R*_params.S;
-			MatrixBlock outIm2col = new MatrixBlock(CRS, PQ, false);
-			MatrixBlock outMM = new MatrixBlock(K, PQ, false);
+			MatrixBlock outIm2col = new MatrixBlock(CRS, PQ, _params.input1.sparse);
+			MatrixBlock outMM = new MatrixBlock(K, PQ, _params.output.sparse);
 			Im2colWorker im2ColWorker = Im2colWorker.getWorker( _params.input1, outIm2col, _params, false);
 			long time1 = 0; long time2 = 0;
 			for(int n = _rl; n < _ru; n++)  {
@@ -182,7 +182,7 @@ public class LibMatrixDNNConv2d
 				long t2 = DMLScript.FINEGRAINED_STATISTICS ? System.nanoTime() : 0;
 				
 				// filter %*% _im2ColOutBlock => matMultOutBlock
-				outMM.reset(outMM.rlen, outMM.clen, false);
+				outMM.reset(outMM.rlen, outMM.clen, _params.output.sparse);
 				LibMatrixDNNHelper.singleThreadedMatMult(_params.input2, outIm2col, outMM, false, true, _params);
 				long t3 = DMLScript.FINEGRAINED_STATISTICS ? System.nanoTime() : 0;
 				
@@ -191,8 +191,8 @@ public class LibMatrixDNNConv2d
 					time2 += t3 - t2;
 				}
 				
-				// Copy the matrix matMultOutBlock of shape [K X PQ] to params.output.denseBlock + destPos
-				partialCopy1(outMM, _params.output.getDenseBlockValues(), n*K*PQ, K, PQ);
+				// Copy the outMM of shape [K x PQ] to a row in params.output 
+				partialCopy1(outMM, _params.output, n, K, PQ);
 				
 				// Add bias to current row if necessary, always dense
 				if(_params.bias != null)
@@ -209,27 +209,43 @@ public class LibMatrixDNNConv2d
 			return _params.output.recomputeNonZeros(_rl, _ru-1);
 		}
 		
-		// Copy the matrix src of shape [K X PQ] to params.output.denseBlock + destPos
-		private static void partialCopy1(MatrixBlock src, double [] dest, int destPos, int K, int PQ) {
+		// Copy the matrix src of shape [K X PQ] to the r-th row in params.output 
+		private static void partialCopy1(MatrixBlock src, MatrixBlock dest, int r, int K, int PQ) {
 			// Copying is required as LibMatrixMult.matrixMult (and/or Java) is not pointer aware.
-			// This is not required in Native implementation
 			if( src.isEmptyBlock() )
 				return;
-			if(src.isInSparseFormat()) {
-				SparseBlock sblock = src.sparseBlock;
+			if( src.sparse ) { //* <- SPARSE
+				SparseBlock srcBlock = src.sparseBlock;
+				SparseBlock sdestBlock = dest.sparseBlock;
+				double[] ddestBlock = dest.getDenseBlockValues();
+				
 				for(int k = 0; k < src.getNumRows(); k++) {
-					if( sblock.isEmpty(k) ) continue;
-					int apos = sblock.pos(k);
-					int alen = sblock.size(k);
-					int[] aix = sblock.indexes(k);
-					double[] avals = sblock.values(k);
-					int desPosK = destPos + k*PQ;
-					for(int j = apos; j < apos+alen; j++)
-						dest[desPosK+aix[j]] = avals[j];
+					if( srcBlock.isEmpty(k) ) continue;
+					int apos = srcBlock.pos(k);
+					int alen = srcBlock.size(k);
+					int[] aix = srcBlock.indexes(k);
+					double[] avals = srcBlock.values(k);
+					if( dest.sparse ) {
+						sdestBlock.setIndexRange(r,
+							0, K*PQ, avals, aix, apos, alen);
+					}
+					else {
+						int desPosK = r + k*PQ;
+						for(int j = apos; j < apos+alen; j++)
+							ddestBlock[desPosK+aix[j]] = avals[j];
+					}
+				}
+			}
+			else { //* <- DENSE
+				if( dest.sparse ) {
+					dest.getSparseBlock().setIndexRange(r, 0, K*PQ,
+						src.getDenseBlockValues(), 0, K*PQ);
+				}
+				else {
+					System.arraycopy(src.getDenseBlockValues(), 0,
+						dest.getDenseBlockValues(), r*K*PQ, K*PQ);
 				}
 			}
-			else 
-				System.arraycopy(src.getDenseBlockValues(), 0, dest, destPos, K * PQ);
 		}
 	}
 	

http://git-wip-us.apache.org/repos/asf/systemml/blob/08c7f00d/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNHelper.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNHelper.java
index 32a0eaa..b985c42 100644
--- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNHelper.java
+++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNHelper.java
@@ -62,7 +62,7 @@ public class LibMatrixDNNHelper
 		if( !params.enableNative || m1.sparse || m2.sparse ) {
 			prepNonZerosForMatrixMult(m1, recomputeNNZM1);
 			prepNonZerosForMatrixMult(m2, recomputeNNZM2);
-			LibMatrixMult.matrixMult(m1, m2, ret, false);
+			LibMatrixMult.matrixMult(m1, m2, ret, true);
 		}
 		else {
 			ret.sparse = false;

http://git-wip-us.apache.org/repos/asf/systemml/blob/08c7f00d/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java
index ac7a6a8..4af4933 100644
--- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java
+++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java
@@ -54,7 +54,7 @@ public class LibMatrixDNNIm2Col {
 				//preallocate sparse-rows (larger than average sparsity to account for skew)
 				int estnnz = (int)Math.ceil(4*input.getSparsity()*out.clen);
 				for(int r = 0; r < out.rlen; r++)
-					out.getSparseBlock().allocate(r, Math.min(estnnz, out.clen));
+					out.getSparseBlock().allocate(r, Math.max(Math.min(estnnz, out.clen),16));
 				return new SparseSparseIm2colWorkerAllChan(input, out, params, trans);
 			}
 		}
@@ -145,16 +145,15 @@ public class LibMatrixDNNIm2Col {
 	 */
 	private static class SparseSparseIm2colWorkerAllChan implements Im2colWorker {
 		private final MatrixBlock input, output;
-		private final int S, R, P, Q, W, HW, RS;
+		private final int S, R, P, Q, H, W, RS;
 		private final int stride_h, stride_w, pad_h, pad_w;
 		private final boolean trans;
 		private final boolean simple;
 		public SparseSparseIm2colWorkerAllChan(MatrixBlock input, MatrixBlock im2ColOutBlock, ConvolutionParameters params, boolean trans) {
 			this.input = input;
 			this.output = im2ColOutBlock;
-			this.HW = params.H * params.W;
 			this.RS = params.R * params.S;
-			this.W = params.W; this.R = params.R; this.S = params.S; this.P = params.P; this.Q = params.Q;
+			this.H = params.H; this.W = params.W; this.R = params.R; this.S = params.S; this.P = params.P; this.Q = params.Q;
 			this.stride_h = params.stride_h; this.stride_w = params.stride_w;
 			this.pad_h = params.pad_h; this.pad_w = params.pad_w;
 			this.trans = trans;
@@ -175,20 +174,19 @@ public class LibMatrixDNNIm2Col {
 			double[] avals = sblock.values(n);
 			
 			// Iterate over the sparse block
+			CellIndex3 ix = new CellIndex3();
 			for(int j=apos; j<apos+alen; j++) {
 				// Note: the input is of shape [N, CHW]
 				int chw = aix[j];
 				
 				// Get individual zero-based c,h,w indexes from zero-based 'chw'
-				int cInput = chw / HW;
-				int hInput = (chw - cInput*HW)/W;
-				int wInput = chw % W; 
+				ix = LibMatrixDNNHelper.computeTensorIndexes(chw, H, W, ix);
 				
 				if( simple )
-					appendInputValueToIm2colOutputSimple(output, cInput, hInput, wInput, 
+					appendInputValueToIm2colOutputSimple(output, ix.ix1, ix.ix2, ix.ix3, 
 						avals[j], R, S, RS, P, trans);
 				else
-					appendInputValueToIm2colOutput(output, cInput, hInput, wInput, avals[j], 
+					appendInputValueToIm2colOutput(output, ix.ix1, ix.ix2, ix.ix3, avals[j], 
 						R, S, P, Q, stride_h, stride_w, pad_h, pad_w, trans);
 			}
 			

http://git-wip-us.apache.org/repos/asf/systemml/blob/08c7f00d/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 6119e95..d5ed8b2 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
@@ -95,22 +95,18 @@ public class LibMatrixMult
 	 * @param m1 first matrix
 	 * @param m2 second matrix
 	 * @param ret result matrix
-	 * @param maintainNnz if false, nnzs are not recomputed and evaluated
+	 * @param fixedRet if true, output representation is fixed and nnzs not recomputed
 	 * @throws DMLRuntimeException if DMLRuntimeException occurs
 	 */
-	public static void matrixMult(MatrixBlock m1, MatrixBlock m2, MatrixBlock ret, boolean maintainNnz) 
-			throws DMLRuntimeException
-	{	
-		matrixMult(m1, m2, ret, 0, m1.rlen, maintainNnz);
+	public static void matrixMult(MatrixBlock m1, MatrixBlock m2, MatrixBlock ret, boolean fixedRet) throws DMLRuntimeException {
+		matrixMult(m1, m2, ret, 0, m1.rlen, fixedRet);
 	}
 	
-	public static void matrixMult(MatrixBlock m1, MatrixBlock m2, MatrixBlock ret, int rl, int ru) 
-			throws DMLRuntimeException
-	{
-		matrixMult(m1, m2, ret, rl, ru, true);
+	public static void matrixMult(MatrixBlock m1, MatrixBlock m2, MatrixBlock ret, int rl, int ru) throws DMLRuntimeException {
+		matrixMult(m1, m2, ret, rl, ru, false);
 	}
 	
-	public static void matrixMult(MatrixBlock m1, MatrixBlock m2, MatrixBlock ret, int rl, int ru, boolean maintainNnz) 
+	public static void matrixMult(MatrixBlock m1, MatrixBlock m2, MatrixBlock ret, int rl, int ru, boolean fixedRet) 
 		throws DMLRuntimeException
 	{
 		//check inputs / outputs
@@ -122,14 +118,16 @@ public class LibMatrixMult
 		//Timing time = new Timing(true);
 		
 		//pre-processing: output allocation
-		boolean ultraSparse = isUltraSparseMatrixMult(m1, m2);
+		boolean ultraSparse = (fixedRet && ret.sparse)
+			|| (!fixedRet && isUltraSparseMatrixMult(m1, m2));
 		boolean tm2 = checkPrepMatrixMultRightInput(m1,m2);
 		m2 = prepMatrixMultRightInput(m1, m2);
 		ret.sparse = ultraSparse;
 		ret.allocateBlock();
 		
 		//prepare row-upper for special cases of vector-matrix
-		boolean pm2 = checkParMatrixMultRightInputRows(m1, m2, Integer.MAX_VALUE);
+		boolean pm2 = !ultraSparse &&
+			checkParMatrixMultRightInputRows(m1, m2, Integer.MAX_VALUE);
 		int ru2 = (pm2 && ru==m1.rlen) ? m2.rlen : ru; 
 		int cu = m2.clen;
 		
@@ -146,7 +144,7 @@ public class LibMatrixMult
 			matrixMultDenseSparse(m1, m2, ret, pm2, 0, ru2);
 		
 		//post-processing: nnz/representation
-		if( maintainNnz ) {
+		if( !fixedRet ) {
 			if( !ret.sparse )
 				ret.recomputeNonZeros();
 			ret.examSparsity();
@@ -200,8 +198,8 @@ public class LibMatrixMult
 		}
 		
 		//prepare row-upper for special cases of vector-matrix / matrix-matrix
-		boolean pm2r = checkParMatrixMultRightInputRows(m1, m2, k);
-		boolean pm2c = checkParMatrixMultRightInputCols(m1, m2, k, pm2r);
+		boolean pm2r = !ultraSparse && checkParMatrixMultRightInputRows(m1, m2, k);
+		boolean pm2c = !ultraSparse && checkParMatrixMultRightInputCols(m1, m2, k, pm2r);
 		int num = pm2r ? m2.rlen : pm2c ? m2.clen : m1.rlen; 
 		
 		//core multi-threaded matrix mult computation
@@ -1642,25 +1640,20 @@ public class LibMatrixMult
 		{
 			SparseBlock b = m2.sparseBlock;
 			
-			for(int k = 0; k < cd; k++ ) 
-			{
-				if( !b.isEmpty(k) ) 
-				{
-					int bpos = b.pos(k);
-					int blen = b.size(k);
-					int[] bixs = b.indexes(k);
-					double[] bvals = b.values(k);
-					for( int j=bpos; j<bpos+blen; j++ )
-					{
-						double bval = bvals[j];
-						int bix = bixs[j];
-						for( int i=rl; i<ru; i++ )
-						{
-							double cvald = bval*m1.quickGetValue(i, k);
-							if( cvald != 0 ){
-								double cval = ret.quickGetValue(i, bix);
-								ret.quickSetValue(i, bix, cval+cvald);
-							}
+			for(int k = 0; k < cd; k++ ) {
+				if( b.isEmpty(k) ) continue; 
+				int bpos = b.pos(k);
+				int blen = b.size(k);
+				int[] bixs = b.indexes(k);
+				double[] bvals = b.values(k);
+				for( int j=bpos; j<bpos+blen; j++ ) {
+					double bval = bvals[j];
+					int bix = bixs[j];
+					for( int i=rl; i<ru; i++ ) {
+						double cvald = bval*m1.quickGetValue(i, k);
+						if( cvald != 0 ){
+							double cval = ret.quickGetValue(i, bix);
+							ret.quickSetValue(i, bix, cval+cvald);
 						}
 					}
 				}

http://git-wip-us.apache.org/repos/asf/systemml/blob/08c7f00d/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixNative.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixNative.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixNative.java
index 9e3a6ee..7e0a6d7 100644
--- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixNative.java
+++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixNative.java
@@ -109,7 +109,7 @@ public class LibMatrixNative
 			Statistics.incrementNativeFailuresCounter();
 		}
 		if (k == 1)
-			LibMatrixMult.matrixMult(m1, m2, ret, examSparsity);
+			LibMatrixMult.matrixMult(m1, m2, ret, !examSparsity);
 		else
 			LibMatrixMult.matrixMult(m1, m2, ret, k);
 	}


[2/2] systemml git commit: [SYSTEMML-2142] Performance sparse im2col (constraint handling)

Posted by mb...@apache.org.
[SYSTEMML-2142] Performance sparse im2col (constraint handling)

This patch improves the performance of sparse im2col operations (general
case for arbitrary pad and stride) because it dominates the runtime of
ultra-sparse conv2d operations by more than an order-of-magnitude. On a
scenario of conv2d operations over ultra sparse inputs with sparsity
1e-5, this patch improves performance by more than 2.5x.
 

Project: http://git-wip-us.apache.org/repos/asf/systemml/repo
Commit: http://git-wip-us.apache.org/repos/asf/systemml/commit/401b7965
Tree: http://git-wip-us.apache.org/repos/asf/systemml/tree/401b7965
Diff: http://git-wip-us.apache.org/repos/asf/systemml/diff/401b7965

Branch: refs/heads/master
Commit: 401b796578fb9dbdc2b835ce84263ea61910c9e3
Parents: 08c7f00
Author: Matthias Boehm <mb...@gmail.com>
Authored: Fri Feb 9 22:23:42 2018 -0800
Committer: Matthias Boehm <mb...@gmail.com>
Committed: Fri Feb 9 22:23:42 2018 -0800

----------------------------------------------------------------------
 .../runtime/matrix/data/LibMatrixDNNIm2Col.java | 46 +++++++++-----------
 1 file changed, 21 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/systemml/blob/401b7965/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java
index 4af4933..d1f4b30 100644
--- a/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java
+++ b/src/main/java/org/apache/sysml/runtime/matrix/data/LibMatrixDNNIm2Col.java
@@ -187,7 +187,7 @@ public class LibMatrixDNNIm2Col {
 						avals[j], R, S, RS, P, trans);
 				else
 					appendInputValueToIm2colOutput(output, ix.ix1, ix.ix2, ix.ix3, avals[j], 
-						R, S, P, Q, stride_h, stride_w, pad_h, pad_w, trans);
+						R, S, RS, P, Q, stride_h, stride_w, pad_h, pad_w, trans);
 			}
 			
 			output.sortSparseRows();
@@ -198,51 +198,47 @@ public class LibMatrixDNNIm2Col {
 	 * Appends the value corresponding to the given [, cInput, hInput, wInput] to the appropriate im2col location of the output
 	 * 
 	 * @param output output matrix block
-	 * @param cInput input channel index (zero-based)
-	 * @param hInput input height index (zero-based)
-	 * @param wInput input width index (zero-based)
+	 * @param c input channel index (zero-based)
+	 * @param h input height index (zero-based)
+	 * @param w input width index (zero-based)
 	 * @param value input value
 	 * @param R filter height
 	 * @param S filter width
+	 * @param RS R*S
 	 * @param P output height
 	 * @param Q output width
 	 * @param stride_h stride height
 	 * @param stride_w stride width
 	 * @param pad_h pad height
 	 * @param pad_w pad width
+	 * @param trans transposed output
 	 */
-	private static void appendInputValueToIm2colOutput(MatrixBlock output, int cInput, int hInput, int wInput, double value, 
-			int R, int S, int P, int Q, int stride_h, int stride_w, int pad_h, int pad_w, boolean trans) {
-		int RS = R*S;
+	private static void appendInputValueToIm2colOutput(MatrixBlock output, int c, int h, int w, double value, 
+		int R, int S, int RS, int P, int Q, int stride_h, int stride_w, int pad_h, int pad_w, boolean trans)
+	{
 		// For the given h,w index, insert avals[j] into respective r,s,p,q locations
-		
 		// Constraints: for(int r = 0; r < R; r++) { if(0 <= p && p < P && (hInput - r + pad_h) % stride_h == 0) { ... } }
 		// Constraint 1: p >= 0 and p = (hInput - r + pad_h)  / stride_h
 		// Therefore,  r <= hInput + pad_h 
 		// Constraint 2: p < P and p = (hInput - r + pad_h)  / stride_h
 		// Therefore,  hInput + pad_h - P*stride_h < r
 		// Math.max(0, hInput + pad_h - P*stride_h + 1) <= r <= Math.min(R-1, hInput + pad_h)
-		int rMin = Math.max(0, hInput + pad_h - P*stride_h + 1);
-		int rMax = Math.min(R-1, hInput + pad_h);
-		int sMin = Math.max(0, wInput + pad_w - Q*stride_w + 1);
-		int sMax = Math.min(S-1, wInput + pad_w);
+		int rMin = Math.max(0, h + pad_h - P*stride_h + 1);
+		int rMax = Math.min(R-1, h + pad_h);
+		int sMin = Math.max(0, w + pad_w - Q*stride_w + 1);
+		int sMax = Math.min(S-1, w + pad_w);
 		// Constraint 3: (hInput - r + pad_h) % stride_h == 0
-		while((hInput - rMin + pad_h) % stride_h != 0 && rMin <= rMax) rMin++;
-		while((wInput - sMin + pad_w) % stride_w != 0 && sMin <= sMax) sMin++;
+		rMin += Math.min((h-rMin+pad_h) % stride_h, rMax-rMin+1);
+		sMin += Math.min((w-sMin+pad_w) % stride_w, sMax-sMin+1);
 		
-		for(int r = rMin; r <= rMax; r += stride_h) {
+		for( int r=rMin, ix=c*RS+rMin*S; r<=rMax; r+=stride_h, ix+=stride_h*S ) {
 			// Only append value if h == hInput, where h = (r - pad_h) + p*stride_h and 0 <= p < P
 			// Therefore, p = (hInput - r + pad_h)  / stride_h. Use the same logic for q.
-			final int p = (hInput - r + pad_h)  / stride_h;
-			final int pQ = p*Q;
-			final int outRowIndex = cInput*RS + r*S;
-			for(int s = sMin; s <= sMax; s += stride_w) {
-				int q = (wInput - s + pad_w)  / stride_w;
-				// chw -> [crs, pq]
-				if( trans )
-					output.appendValue(pQ + q, outRowIndex + s, value);
-				else
-					output.appendValue(outRowIndex + s, pQ + q, value);
+			final int pQ = (h - r + pad_h) / stride_h * Q;
+			for(int s=sMin, ws=w-sMin+pad_w; s<=sMax; s+=stride_w, ws-=stride_w) {
+				int q = ws / stride_w; // chw -> [crs, pq]
+				output.appendValue(trans ? pQ+q : ix+s,
+					trans ? ix+s : pQ+q, value);
 			}
 		}
 	}