Java 超快並行執行的多核心CPU矩陣乘法(線性代數)在數據統計方面的應用

併發與並行區別

併發:當有多個線程在操作時,如果系統只有一個CPU(單核心),則它根本不可能真正同時進行一個以上的線程,它只能把CPU運行時間劃分成若干個時間段,再將時間 段分配給各個線程執行,在一個時間段的線程代碼運行時,其它線程處於掛起狀。.這種方式我們稱之爲併發(Concurrent)。

並行:當系統有一個以上CPU(一個CPU具備多核心)時,則線程的操作有可能非併發。當一個CPU執行一個線程時,另一個CPU可以執行另一個線程,兩個線程互不搶佔CPU資源,可以同時進行,這種方式我們稱之爲並行(Parallel)。

區別:併發和並行是即相似又有區別的兩個概念,並行是指兩個或者多個事件在同一時刻發生;而併發是指兩個或多個事件在同一時間間隔內發生。在多道程序環境下,併發性是指在一段時間內宏觀上有多個程序在同時運行,但在單處理機系統中,每一時刻卻僅能有一道程序執行,故微觀上這些程序只能是分時地交替執行。倘若在計算機系統中有多個處理機,則這些可以併發執行的程序便可被分配到多個處理機上,實現並行執行,即利用每個處理機來處理一個可併發執行的程序,這樣,多個程序便可以同時執行。

實際的數據統計範例

某公司有4個工廠,分佈在不同地區,同一時段都生產了P1,P2,P3這3種產品,產量(單位;噸),

工廠\產品 P1 P2 P3
5 2 4
3 8 2
6 0 4
0 1 6

可用下面的矩陣描述

,其中4行分別表示甲乙丙丁4個工廠的生產情況,3列分佈表示3種產品P1,P2,P3的產量。

產品P1的單件利潤爲2(單位:元)佔用存儲空間爲4(單位:立方米),

產品P2的單件利潤爲1(單位:元)佔用存儲空間爲3單位:立方米),

產品P3的單件利潤爲3(單位:元)佔用存儲空間爲4單位:立方米),

試用矩陣的計算方式來統計這些數據。

產品 單件利潤/單件體積
P1 2/4
P2 1/3
P3 3/2

可用下面的矩陣描述

,其中第1列表示3種產品的單件利潤,第2列表示3種產品的單件體積。

矩陣C的第1列數據分別表示4個工廠的利潤,第2列分別表示4個工廠產品需要的存儲空間。

我們將使用Java代碼來實現數據的統計

定義存儲矩陣數據的對象:

package com.contoso;

public class Matrix {

    private final int[][] matrix;

    public Matrix(int rows, int cols) {
        matrix = new int[rows][cols];
    }

    public int getCols() {
        return matrix[0].length;
    }

    public int getRows() {
        return matrix.length;
    }

    public int getCellValue(int row, int col) {
        return matrix[row][col];
    }

    public void setCellValue(int row, int col, int value) {
        matrix[row][col] = value;
    }
}

接下來我們將使用兩種不同的實現方式來統計數據:

第1種方式:單線程統計方式

package com.contoso;

/**
 * 單線程矩陣乘法
 * 
 */
public class MatrixMultiply {

    public static void main(String[] args) {
        /*
        A矩陣:4行3列
        5 2 4 
        3 8 2 
        6 0 4 
        0 1 6 
         */
        long start = System.nanoTime();
        Matrix a = new Matrix(4, 3);
        a.setCellValue(0, 0, 5);
        a.setCellValue(0, 1, 2);
        a.setCellValue(0, 2, 4);
        a.setCellValue(1, 0, 3);
        a.setCellValue(1, 1, 8);
        a.setCellValue(1, 2, 2);
        a.setCellValue(2, 0, 6);
        a.setCellValue(2, 1, 0);
        a.setCellValue(2, 2, 4);
        a.setCellValue(3, 0, 0);
        a.setCellValue(3, 1, 1);
        a.setCellValue(3, 2, 6);
        print(a);
        /*
        B矩陣:3行2列
        2 4 
        1 3 
        3 2 
         */
        Matrix b = new Matrix(3, 2);
        b.setCellValue(0, 0, 2);
        b.setCellValue(1, 0, 1);
        b.setCellValue(2, 0, 3);
        b.setCellValue(0, 1, 4);
        b.setCellValue(1, 1, 3);
        b.setCellValue(2, 1, 2);
        print(b);
        /*
        C矩陣:4行2列  C = A * B
        */
        print(multiply(a, b));
        long end = System.nanoTime();
        double timeTaken = (end - start) / 1e9;
        System.out.println("Matrix Multiply Time Taken in seconds:" + timeTaken);
    }

    public static void print(Matrix mat) {
        for (int i = 0; i < mat.getRows(); i++) {
            for (int j = 0; j < mat.getCols(); j++) {
                System.out.printf("%d ", mat.getCellValue(i, j));
            }
            System.out.println();
        }
        System.out.println();
    }

    public static Matrix multiply(Matrix a, Matrix b) {
        if (a.getCols() != b.getRows()) {
            throw new IllegalArgumentException("rows/columns mismatch");
        }
        Matrix result = new Matrix(a.getRows(), b.getCols());
        for (int i = 0; i < a.getRows(); i++) {
            for (int j = 0; j < b.getCols(); j++) {
                for (int k = 0; k < a.getCols(); k++) {
                    result.setCellValue(i, j, result.getCellValue(i, j) + a.getCellValue(i, k) * b.getCellValue(k, j));
                }
            }
        }
        return result;
    }
}

請注意以下單線程統計需要的時間:

run:
5 2 4 
3 8 2 
6 0 4 
0 1 6 

2 4 
1 3 
3 2 

24 34 
20 40 
24 32 
19 15 

Matrix Multiply Time Taken in seconds:0.036336188
BUILD SUCCESSFUL (total time: 0 seconds)

第2中方式:超快並行執行的多核心CPU矩陣乘法在數據統計方面的應用

package com.contoso;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

/**
 * 超快並行執行的多核心CPU矩陣乘法在數據統計方面的應用
 *
 */
public class RecursiveMatrixMultiply extends RecursiveAction {

    private final Matrix a, b, c;
    private final int row;

    public RecursiveMatrixMultiply(Matrix a, Matrix b, Matrix c) {
        this(a, b, c, -1);
    }

    public RecursiveMatrixMultiply(Matrix a, Matrix b, Matrix c, int row) {
        if (a.getCols() != b.getRows()) {
            throw new IllegalArgumentException("rows/columns mismatch");
        }
        this.a = a;
        this.b = b;
        this.c = c;
        this.row = row;
    }

    @Override
    public void compute() {
        if (row == -1) {
            List<RecursiveMatrixMultiply> tasks = new ArrayList<>();
            for (int row = 0; row < a.getRows(); row++) {
                tasks.add(new RecursiveMatrixMultiply(a, b, c, row));
            }
            invokeAll(tasks);
        } else {
            multiplyRowByColumn(a, b, c, row);
        }
    }

    public static void multiplyRowByColumn(Matrix a, Matrix b, Matrix c, int row) {
        for (int j = 0; j < b.getCols(); j++) {
            for (int k = 0; k < a.getCols(); k++) {
                c.setCellValue(row, j, c.getCellValue(row, j) + a.getCellValue(row, k) * b.getCellValue(k, j));
            }
        }
    }

    public static void print(Matrix mat) {
        for (int i = 0; i < mat.getRows(); i++) {
            for (int j = 0; j < mat.getCols(); j++) {
                System.out.print(mat.getCellValue(i, j) + " ");
            }
            System.out.println();
        }
        System.out.println();
    }

    public static void main(String[] args) {
        long start = System.nanoTime();
        /*
        A矩陣:4行3列
        5 2 4 
        3 8 2 
        6 0 4 
        0 1 6 
         */
        Matrix a = new Matrix(4, 3);
        a.setCellValue(0, 0, 5);
        a.setCellValue(0, 1, 2);
        a.setCellValue(0, 2, 4);
        a.setCellValue(1, 0, 3);
        a.setCellValue(1, 1, 8);
        a.setCellValue(1, 2, 2);
        a.setCellValue(2, 0, 6);
        a.setCellValue(2, 1, 0);
        a.setCellValue(2, 2, 4);
        a.setCellValue(3, 0, 0);
        a.setCellValue(3, 1, 1);
        a.setCellValue(3, 2, 6);
        print(a);

        /*
        B矩陣:3行2列
        2 4 
        1 3 
        3 2 
         */
        Matrix b = new Matrix(3, 2);
        b.setCellValue(0, 0, 2);
        b.setCellValue(1, 0, 1);
        b.setCellValue(2, 0, 3);
        b.setCellValue(0, 1, 4);
        b.setCellValue(1, 1, 3);
        b.setCellValue(2, 1, 2);
        print(b);

        /*
        C矩陣:4行2列  C = A * B
         */
        Matrix c = new Matrix(4, 2);
        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(new RecursiveMatrixMultiply(a, b, c));
        print(c);
        long end = System.nanoTime();
        double timeTaken = (end - start) / 1e9;
        System.out.println("Recursive Matrix Multiply Time Taken in seconds:" + timeTaken);
        System.err.println("單顆CPU內核數 = " + Runtime.getRuntime().availableProcessors());
    }
}

請注意並行計算的多核心CPU矩陣乘法統計需要的時間:

run:
5 2 4 
3 8 2 
6 0 4 
0 1 6 

2 4 
1 3 
3 2 

24 34 
20 40 
24 32 
19 15 

單顆CPU內核數 = 4
Recursive Matrix Multiply Time Taken in seconds:0.003261084
BUILD SUCCESSFUL (total time: 0 seconds)

結論:多核心CPU並行矩陣乘法計算要比單線程矩陣乘法計算快11倍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章