/** * Multicore Matrix Multiplication. Multiplies two matrices by dividing the problem * into subproblems, each core processes one part. */ /** * @authors * {tengel,andrea}@inf.ufsm.br, * Luiz-Angelo.Steffenel@univ-reims.fr, * Manuele.Kirsch-Pinheiro@univ-paris1.fr */ package weka.core.matrix; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class MulticoreMatrixMult { private static int OFFSET = 4; /** * Method who does the paralel multiplication of two Matrices. * Multiplication Logic: - Takes the number of avaliable processors and * divides it by the number of columns of the matrix B. - Each thread * multiplies separately a certain number of columns from the matrix B and * store the results at matrix X, this way, it isn't necessary to treat * problems with shared data. * * @param A * @param B * @return A * B */ public static Matrix times(Matrix A, Matrix B) { if (A.getColumnDimension() != B.getRowDimension()) { throw new IllegalArgumentException("Matrix inner dimensions must agree."); } ArrayList<Future<Matrix>> threads = new ArrayList<Future<Matrix>>(); // Array of future ExecutorService pool = null; // Thread's pool Matrix X = new Matrix(A.getRowDimension(), B.getColumnDimension()); // Matrix result final int N_THREADS = Runtime.getRuntime().availableProcessors(); // Number of threads // If # of columns of matrix B < Number of processors, executes sequencially. try { if (B.getColumnDimension() < N_THREADS * OFFSET) { pool = Executors.newFixedThreadPool(1); threads.add(pool.submit(new ParalelMatrixMultiplication(A, B, X, 0, B.getColumnDimension() - 1))); } // If # of columns of matix B >= Number of Processors else { int partitionSize = B.getColumnDimension() / N_THREADS - 1; // Number of columns for each thread // sets the thread's pool. pool = Executors.newFixedThreadPool(N_THREADS); int aux = 0; for (int i = 0; aux < N_THREADS; i += partitionSize + 1) { aux++; if ((i + partitionSize) < B.getColumnDimension() && (aux+1) <= N_THREADS) { threads.add(pool.submit(new ParalelMatrixMultiplication(A, B, X, i, i + partitionSize))); } else { threads.add(pool.submit(new ParalelMatrixMultiplication(A, B, X, i, B.getColumnDimension() - 1))); } } } } catch (Exception e) { e.printStackTrace(); } // Similar ao join(). Aguarda o termino das threads. BLOQUEANTE for (Future f : threads) { try { f.get(); } catch (Exception ex) { ex.printStackTrace(); } } pool.shutdown(); return X; } /** * Callable thread class. */ private static class ParalelMatrixMultiplication implements Callable<Matrix> { private Matrix A; private Matrix B; private Matrix X; private int startColumn; private int finalColumn; public ParalelMatrixMultiplication(Matrix A, Matrix B, Matrix X, int startColumn, int finalColumn) { this.A = A; this.B = B; this.X = X; this.startColumn = startColumn; this.finalColumn = finalColumn; } @Override public Matrix call() throws Exception { times(); return X; } /** * Linear algebraic matrix multiplication, A * B * */ private void times() { double[][] matA = A.getArray(); double[][] matX = X.getArray(); for (int k = startColumn; k <= finalColumn; k++) { // From and to determined columns double[] tmp = getColumn(B, k); // Tmp for not have to get each element each time. for (int i = 0; i < A.getRowDimension(); i++) { // A matrix lines double sum = 0.0; for (int j = 0; j < A.getColumnDimension(); j++) { // A matrix columns sum += matA[i][j] * tmp[j]; } matX[i][k] = sum; } } } /** * Gets the j column from the matrix M * * @param M The Matrix * @param j The Column * @return the vector that contains the elements of the column j */ private double[] getColumn(Matrix M, int j) { if (j < 0 || j > M.getColumnDimension()) { throw new IllegalArgumentException("Index out of boundaries. "); } double[] vector = new double[M.getRowDimension()]; for (int i = 0; i < M.getRowDimension(); i++) { vector[i] = M.get(i, j); } return vector; } } }