Difference between revisions of "MatrixMultiply"

From CSE231 Wiki
Jump to navigation Jump to search
m (Added note about auto-coarsening)
 
(91 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
=Motivation=
 +
We gain experience using the parallel for loop constructs.
 +
 
=Background=
 
=Background=
  
Line 8: Line 11:
 
* [https://en.wikipedia.org/wiki/Matrix_multiplication Wikipedia]
 
* [https://en.wikipedia.org/wiki/Matrix_multiplication Wikipedia]
  
 
==Math Definition==
 
 
If <math>\mathbf{A}</math> is an <math>n \times m</math> matrix and <math>\mathbf{B}</math> is an <math>m  \times p</math> matrix
 
If <math>\mathbf{A}</math> is an <math>n \times m</math> matrix and <math>\mathbf{B}</math> is an <math>m  \times p</math> matrix
  
Line 15: Line 16:
  
 
: <math>(\mathbf{A}\mathbf{B})_{ij} = \sum_{k=1}^m A_{ik}B_{kj}</math>
 
: <math>(\mathbf{A}\mathbf{B})_{ij} = \sum_{k=1}^m A_{ik}B_{kj}</math>
 +
 +
:<math>\mathbf{C}=\begin{pmatrix}
 +
a_{11}b_{11} +\cdots + a_{1n}b_{n1} & a_{11}b_{12} +\cdots + a_{1n}b_{n2} & \cdots & a_{11}b_{1p} +\cdots + a_{1n}b_{np} \\
 +
a_{21}b_{11} +\cdots + a_{2n}b_{n1} & a_{21}b_{12} +\cdots + a_{2n}b_{n2} & \cdots & a_{21}b_{1p} +\cdots + a_{2n}b_{np} \\
 +
\vdots & \vdots & \ddots & \vdots \\
 +
a_{m1}b_{11} +\cdots + a_{mn}b_{n1} & a_{m1}b_{12} +\cdots + a_{mn}b_{n2} & \cdots & a_{m1}b_{1p} +\cdots + a_{mn}b_{np} \\
 +
\end{pmatrix}
 +
</math>
 +
 
source: [https://en.wikipedia.org/wiki/Matrix_multiplication#General_definition_of_the_matrix_product Matrix Multiplication on Wikipedia]
 
source: [https://en.wikipedia.org/wiki/Matrix_multiplication#General_definition_of_the_matrix_product Matrix Multiplication on Wikipedia]
  
==Sequential Java Implementation==
+
=Code To Investigate=
The point of the required portion of this studio is not to struggle with matrix multiplication, but rather to get some experience with the parallel for loop constructs in Habanero.
+
==Demo Video==
 +
<youtube>iEuYiy1Bx2A</youtube>
 +
==SequentialMatrixMultiplier==
 +
{{CodeToInvestigate|SequentialMatrixMultiplier|multiply|matrixmultiply.demo|demo}}
 +
 
 +
==SequentialMatrixMultiplierClient==
 +
{{CodeToInvestigate|SequentialMatrixMultiplierClient|main|matrixmultiply.client|demo}}
 +
 
 +
==MatrixMultiplyApp==
 +
{{Viz|MatrixMultiplyApp|matrixmultiply.viz|demo}}
 +
 
 +
[[File:Martix multiply app 3x5 X 5x4.png|800px]]
  
Feel free to use the provided sequential implementation in SequentialMatrixMultiplier as a reference:
+
=The Core Questions=
<nowiki> @Override
+
*What are the tasks?
public double[][] multiply(double[][] a, double[][] b) {
+
*What is the data?
double[][] result = MatrixUtils.createMultiplyResultBufferInitializedToZeros(a, b);
+
*Is the data mutable?
int n = a.length;
+
*If so, how is it shared?
int m = a[0].length;
 
int p = b[0].length;
 
for (int i = 0; i < n; i++) {
 
for (int j = 0; j < p; j++) {
 
// NOTE: result is already initialized to 0.0
 
// result[i][j] = 0.0;
 
for (int k = 0; k < m; k++) {
 
result[i][j] += a[i][k] * b[k][j];
 
}
 
}
 
}
 
return result;
 
}</nowiki>
 
  
=Where to Start=
+
=Code To Implement=
==MatrixMultiplyTestSuite==
 
  
In <code>src/test/java</code> in package <code>matrixmultiply.studio</code> you will find <code>MatrixMultiplyTestSuite</code>.
+
There are three methods you will need to implement, all of which are different ways to use parallel for loops to solve the problem. To assist you, the [https://classes.engineering.wustl.edu/cse231/core/index.php?title=MatrixMultiply#SequentialMatrixMultiplier sequential implementation] has been implemented in a [[#Demo_Video|demo video]].
  
From there you can tunnel into the three MatrixMultipliers you will need to implement.
+
==LoopLoopMatrixMultiplier==
==Javadocs==
+
{{CodeToImplement|LoopLoopMatrixMultiplier|multiply|matrixmultiply.exercise}}
[http://www.cse.wustl.edu/~cosgroved/courses/cse231/f17/apidocs/edu/wustl/cse231s/rice/habanero/Habanero.html Habanero]
 
: [http://www.cse.wustl.edu/~cosgroved/courses/cse231/f17/apidocs/edu/wustl/cse231s/rice/habanero/Habanero.html#forall-int-int-edu.rice.hj.api.HjSuspendingProcedureInt1D- forall(start, endExclusive, body)]
 
: [http://www.cse.wustl.edu/~cosgroved/courses/cse231/f17/apidocs/edu/wustl/cse231s/rice/habanero/Habanero.html#forall-edu.wustl.cse231s.rice.habanero.options.ChunkedOption-int-int-edu.rice.hj.api.HjSuspendingProcedureInt1D- forall(chunked(), start, endExclusive, body)]
 
  
=Required Studio Solutions=
+
{{Parallel|public double[][] multiply(double[][] a, double[][] b)}}
  
There are three methods you will need to implement, all of which are different ways to use parallel for loops to solve the problem. To assist you, the sequential implementation has already been completed for you. We recommend starting from the top and working your way down. There is also an optional recursive implementation and a manual grouping implementation which has been done for you (this is just to demonstrate how chunking works behind the scenes).
+
In this implementation, you will simply convert the sequential solution into a parallel one using two nested [https://www.cse.wustl.edu/~dennis.cosgrove/courses/cse231/spring22/apidocs/fj/FJ.html#join_void_fork_loop(int,int,fj.api.TaskIntConsumer) parallel fork loops].
  
==ForallForallMatrixMultiplier==
+
=== Computation Graph ===
In this implementation, you will simply convert the sequential solution into a parallel one using two forall loops.
 
  
==Forall2dMatrixMultiplier==
+
For 3x3 Matrix X 3x3 Matrix:
In this implementation, we will cut down the syntax of the two forall implementation with the use of HJ’s <code>forall2d</code> method. Functionally, this method serves the purpose of using two forall loops. The input parameters should equal the input parameters of the two separate forall loops. For example,
 
<nowiki>forall(min, max, (i) -> {
 
forall(min, max, (j) -> doSomething());
 
});</nowiki>
 
Would appear as <code>forall2d(min, max, min, max, (i, j) -> doSomething());</code> in the forall2d syntax.
 
  
==Forall2dChunkedMatrixMultiplier==
+
[[File:LoopLoopMatrixMultiplier_Computation_Graph.svg|800px]]
In this implementation, we will add a minor performance boost to the process by using the forall-chunked construct. Although similar to the traditional forall loop, it increases speedup using iteration grouping/chunking. This topic is discussed in detail in this [https://edge.edx.org/courses/RiceX/COMP322/1T2014R/courseware/a900dd0655384de3b5ef01e508ea09d7/6349730bb2a149a0b33fa23db7afddee/?activate_block_id=i4x%3A%2F%2FRiceX%2FCOMP322%2Fsequential%2F6349730bb2a149a0b33fa23db7afddee Rice video] and explained in the [http://pasiphae.cs.rice.edu/#hjlib-for-parallelchunkediterationwithimplicitfinish HJ documentation]. There is no need to specify anything, allow the runtime to determine the chunking.
 
  
In this approach, we will show you how you can specify the grouping yourself and what forAllChunked is doing behind the scenes. There is no need to implement this method, this is just here to serve as a reference for you. However, you should understand the concept of iteration grouping before moving forward.
+
==Loop2dMatrixMultiplier==
 +
{{CodeToImplement|Loop2dMatrixMultiplier|multiply|matrixmultiply.exercise}}
  
=Optional Fun Divide and Conquer Solutions=
+
{{Parallel|public double[][] multiply(double[][] a, double[][] b)}}
In this implementation, you will solve the matrix multiply problem sequentially and in parallel using recursion. Although this class should be able to take in a matrix of any size, try to imagine this as a 2x2 matrix in order to make it easier to solve. Once you solve the sequential method, the parallel method should look very similar with exception of an async/finish block.
 
  
In order to obtain the desired result matrix, you will need to recursively call the correct submatrices for each of the four result submatrices. Imagining this as a 2x2 matrix, remember that the dot products of the rows of the first matrix and the columns of the second matrix create the result matrix.
+
<!--
 +
In this implementation, we will cut down the syntax of the two forall implementation with the use of V5’s <code>forall2d</code> method. Functionally, this method serves the purpose of using two forall loops. [[Reference_Page#Forall_2d|Take a look at the reference page]] if you have questions on how to utilize this loop.
 +
-->
  
Hint: Each result submatrix should have two recursive calls, for a total of eight recursive calls.
+
[https://www.cse.wustl.edu/~dennis.cosgrove/courses/cse231/spring22/apidocs/fj/FJ.html#join_void_fork_loop_2d(int,int,int,int,fj.api.TaskBiIntConsumer) join_void_fork_loop_2d]
  
==SequentialDivideAndConquerMatrixMultiplier==
+
=== Computation Graph ===
In <code>class SubMatrix</code>, method <code>sequentialDivideAndConquerMultiplyKernel</code> you will find your base case and the sub matrices prepared for you. 
 
  
<nowiki>
+
For 3x3 Matrix X 3x3 Matrix:
private static void sequentialDivideAndConquerMultiplyKernel(SubMatrix a, SubMatrix b, SubMatrix result) {
 
if( result.size == 1 ) {
 
result.values[result.row][result.col] += a.values[a.row][a.col] * b.values[b.row][b.col];
 
} else {
 
SubMatrix a11 = a.newSub11();
 
SubMatrix a12 = a.newSub12();
 
SubMatrix a21 = a.newSub21();
 
SubMatrix a22 = a.newSub22();
 
 
SubMatrix b11 = b.newSub11();
 
SubMatrix b12 = b.newSub12();
 
SubMatrix b21 = b.newSub21();
 
SubMatrix b22 = b.newSub22();
 
 
SubMatrix result11 = result.newSub11();
 
SubMatrix result12 = result.newSub12();
 
SubMatrix result21 = result.newSub21();
 
SubMatrix result22 = result.newSub22();
 
  
throw new NotYetImplementedException();
+
[[File:Loop2dMatrixMultiplier_Computation_Graph.svg|800px]]
}
 
}</nowiki>
 
  
You simply need to make the appropriate recursive calls to compute the result on the right:
+
==Loop2dAutoCoarsenMatrixMultiplier==
 +
{{CodeToImplement|Loop2dAutoCoarsenMatrixMultiplier|multiply|matrixmultiply.exercise}}
  
:<math>\begin{pmatrix}
+
{{Parallel|public double[][] multiply(double[][] a, double[][] b)}}
\mathbf{A}_{11} & \mathbf{A}_{12} \\
 
\mathbf{A}_{21} & \mathbf{A}_{22} \\
 
\end{pmatrix} \begin{pmatrix}
 
\mathbf{B}_{11} & \mathbf{B}_{12} \\
 
\mathbf{B}_{21} & \mathbf{B}_{22} \\
 
\end{pmatrix} = \begin{pmatrix}
 
\mathbf{A}_{11} \mathbf{B}_{11} + \mathbf{A}_{12} \mathbf{B}_{21} & \mathbf{A}_{11} \mathbf{B}_{12} + \mathbf{A}_{12} \mathbf{B}_{22}\\
 
\mathbf{A}_{21} \mathbf{B}_{11} + \mathbf{A}_{22} \mathbf{B}_{21} & \mathbf{A}_{21} \mathbf{B}_{12} + \mathbf{A}_{22} \mathbf{B}_{22}\\
 
\end{pmatrix}
 
</math>
 
source: [https://en.wikipedia.org/w/index.php?title=Matrix_multiplication#Parallel_matrix_multiplication Wikipedia Parallel Matrix Multiplication]
 
  
==ParallelDivideAndConquerMatrixMultiplier==
+
[https://www.cse.wustl.edu/~dennis.cosgrove/courses/cse231/spring22/apidocs/fj/FJ.html#join_void_fork_loop_2d_auto_coarsen(int,int,int,int,fj.api.TaskBiIntConsumer) join_void_fork_loop_2d_auto_coarsen]
Again, given the following:
 
  
:<math>\begin{pmatrix}
+
This implementation will look very similar to the previous one, so don't overthink it! The real benefit can be seen in the performance difference between the two based on the coarsening being done behind the scenes.
\mathbf{A}_{11} \mathbf{B}_{11} + \mathbf{A}_{12} \mathbf{B}_{21} & \mathbf{A}_{11} \mathbf{B}_{12} + \mathbf{A}_{12} \mathbf{B}_{22}\\
+
<!--
\mathbf{A}_{21} \mathbf{B}_{11} + \mathbf{A}_{22} \mathbf{B}_{21} & \mathbf{A}_{21} \mathbf{B}_{12} + \mathbf{A}_{22} \mathbf{B}_{22}\\
+
In this implementation, we will add a minor performance boost to the process by using the forall-chunked construct. Although similar to the traditional forall loop, it increases performance using iteration grouping/chunking. This topic is discussed in detail in this [https://edge.edx.org/courses/RiceX/COMP322/1T2014R/courseware/a900dd0655384de3b5ef01e508ea09d7/6349730bb2a149a0b33fa23db7afddee/?activate_block_id=i4x%3A%2F%2FRiceX%2FCOMP322%2Fsequential%2F6349730bb2a149a0b33fa23db7afddee Rice video] and explained in the [[Reference_Page#Parallel_Loops|V5 documentation]]. There is no need to specify anything, allow the runtime to determine the chunking.
\end{pmatrix}
 
</math>
 
source: [https://en.wikipedia.org/w/index.php?title=Matrix_multiplication#Parallel_matrix_multiplication Wikipedia Parallel Matrix Multiplication]
 
  
What computation can be done in parallel? What computation must be performed sequentially?
+
NOTE: we contemplated also assigning building a 1D forall chunked version.  We deemed this more work that it was worth given that you are already building the 2d version.  Just know that forall(chunked(), ...) exists for 1d loops as well.  
  
=Provided Example Implementations=
+
Use chunking.  It is a nice feature.
==SequentialMatrixMultiplier==
+
-->
We provide the sequential iterative implementation so you can focus just on becoming familiar with using Habanero's forall loops.
 
  
==ForallGroupedMatrixMultiplier==
+
=Extra Credit Challege Divide and Conquer=
For 231 we are encouraging you to prefer the use of chunked().  However, if you want greater control over how your loops are broken up, please look to this as an example of how to use forall grouped.
+
[[Matrix_Multiply_Divide_and_Conquer_Assignment|Divide and Conquer Matrix Multiplication]]
  
===Javadocs===
+
=Testing Your Solution=
[http://www.cse.wustl.edu/~cosgroved/courses/cse231/f17/apidocs/edu/wustl/cse231s/rice/habanero/Habanero.html Habanero]
+
==Correctness==
: [http://www.cse.wustl.edu/~cosgroved/courses/cse231/f17/apidocs/edu/wustl/cse231s/rice/habanero/Habanero.html#forall-int-int-edu.rice.hj.api.HjSuspendingProcedureInt1D- forall(region, body)]
+
{{TestSuite|__MatrixMultiplyTestSuite|matrixmultiply.studio}}
  
[http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjRegion.HjRegion1D.html interface HjRegion.HjRegion1D<V>].
+
==Performance==
 +
{{Performance|MatrixMultiplicationTiming|matrixmultiply.performance}}
  
==Forall2dGroupedMatrixMultiplier==
+
Investigate the performance difference for your different implementations.  When you run MatrixMultiplicationTiming it will put a CSV of the timings into your copy buffer.  You can then paste them into a spreadsheet and chart the performance.  Feel free to tune the parameters of the test to see the impacts of, for example, different matrix sizes.
Same as above but with forall2d.
 
  
===Javadocs===
+
[[File:Matrix multiply performance.png]]
[http://www.cse.wustl.edu/~cosgroved/courses/cse231/f17/apidocs/edu/wustl/cse231s/rice/habanero/Habanero.html Habanero]
 
: [http://www.cse.wustl.edu/~cosgroved/courses/cse231/f17/apidocs/edu/wustl/cse231s/rice/habanero/Habanero.html#forall2d-edu.rice.hj.api.HjRegion.HjRegion2D-edu.rice.hj.api.HjSuspendingProcedureInt2D- forall2d(region, body)]  
 
  
[http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjRegion.HjRegion2D.html interface HjRegion.HjRegion2D<V>].
+
=Pledge, Acknowledgments, Citations=
 +
{{Pledge|matrix-multiply}}

Latest revision as of 00:21, 14 February 2023

Motivation

We gain experience using the parallel for loop constructs.

Background

Matrix multiplication is a simple mathematical operation which we will replicate in this studio. For our purposes, we will only deal with square matrices (same number of rows and columns). However, we will approach this problem with several different parallel constructs and approaches.

For those unfamiliar on how to multiply two matrices, take a look at these overviews:

If is an matrix and is an matrix

for each i=[0..n) and for each j=[0..p)

source: Matrix Multiplication on Wikipedia

Code To Investigate

Demo Video

SequentialMatrixMultiplier

class: SequentialMatrixMultiplier.java DEMO: Java.png
methods: multiply
package: matrixmultiply.demo
source folder: src/demo/java

SequentialMatrixMultiplierClient

class: SequentialMatrixMultiplierClient.java DEMO: Java.png
methods: main
package: matrixmultiply.client
source folder: src/demo/java

MatrixMultiplyApp

class: MatrixMultiplyApp.java VIZ
package: matrixmultiply.viz
source folder: student/src/demo/java

Martix multiply app 3x5 X 5x4.png

The Core Questions

  • What are the tasks?
  • What is the data?
  • Is the data mutable?
  • If so, how is it shared?

Code To Implement

There are three methods you will need to implement, all of which are different ways to use parallel for loops to solve the problem. To assist you, the sequential implementation has been implemented in a demo video.

LoopLoopMatrixMultiplier

class: LoopLoopMatrixMultiplier.java Java.png
methods: multiply
package: matrixmultiply.exercise
source folder: student/src/main/java

method: public double[][] multiply(double[][] a, double[][] b) Parallel.svg (parallel implementation required)

In this implementation, you will simply convert the sequential solution into a parallel one using two nested parallel fork loops.

Computation Graph

For 3x3 Matrix X 3x3 Matrix:

LoopLoopMatrixMultiplier Computation Graph.svg

Loop2dMatrixMultiplier

class: Loop2dMatrixMultiplier.java Java.png
methods: multiply
package: matrixmultiply.exercise
source folder: student/src/main/java

method: public double[][] multiply(double[][] a, double[][] b) Parallel.svg (parallel implementation required)


join_void_fork_loop_2d

Computation Graph

For 3x3 Matrix X 3x3 Matrix:

Loop2dMatrixMultiplier Computation Graph.svg

Loop2dAutoCoarsenMatrixMultiplier

class: Loop2dAutoCoarsenMatrixMultiplier.java Java.png
methods: multiply
package: matrixmultiply.exercise
source folder: student/src/main/java

method: public double[][] multiply(double[][] a, double[][] b) Parallel.svg (parallel implementation required)

join_void_fork_loop_2d_auto_coarsen

This implementation will look very similar to the previous one, so don't overthink it! The real benefit can be seen in the performance difference between the two based on the coarsening being done behind the scenes.

Extra Credit Challege Divide and Conquer

Divide and Conquer Matrix Multiplication

Testing Your Solution

Correctness

class: __MatrixMultiplyTestSuite.java Junit.png
package: matrixmultiply.studio
source folder: testing/src/test/java

Performance

class: MatrixMultiplicationTiming.java Noun Project stopwatch icon 386232 cc.svg
package: matrixmultiply.performance
source folder: src/main/java

Investigate the performance difference for your different implementations. When you run MatrixMultiplicationTiming it will put a CSV of the timings into your copy buffer. You can then paste them into a spreadsheet and chart the performance. Feel free to tune the parameters of the test to see the impacts of, for example, different matrix sizes.

Matrix multiply performance.png

Pledge, Acknowledgments, Citations

file: matrix-multiply-pledge-acknowledgments-citations.txt

More info about the Honor Pledge