Java中的多线程矩阵乘法。平均时间关闭。我正确使用执行程序吗?

时间:2018-04-30 05:19:29

标签: java multithreading matrix-multiplication executorservice

我试图进行多线程矩阵乘法,其中我比较从1到100开始的不同线程数的执行时间,每次迭代增加10。

基本上我在它们的单元格中创建了两个100x100矩阵,其随机数范围从-10.0到10.0,然后将它们相乘。我将使用不同数量的线程执行25次(每次再增加10次:所以第一次迭代将使用1个线程,第二次迭代将使用10个线程,第三次将使用20个线程等...)并找到平均值完成时间并将该时间存储在文件中。

我遇到的问题是,我不确定我是否正确使用了执行程序。例如,对我来说这段代码(我还提供了此代码段下面的整个程序代码)说我已经创建了10个线程,并且在每个线程中我将使用.execute方法来运行我的代码片段。 LoopTaskA恰好是矩阵的乘法。所以我要做的是在这10个线程中分配一个乘法。这就是我在这里做的事吗?或者我在10个线程中乘以10次(即每个线程一次乘法)?

我之所以这样问是因为当我读完整个程序时,每增加一次线程数,我的平均完成时间就会增加。如果因为我分割工作量而增加线程数,是不是应该减少完成时间?

根据这个问题,我发现on this same website:可能不是吗?但我仍然不确定我做错了什么。

for(int i = 0; i < 25; i++)
{
    ExecutorService execService = Executors.newFixedThreadPool(10);
    startTime = System.nanoTime();          
    for(int j = 0; j < 10; j++)
    {
        execService.execute(new LoopTaskA(m1,m2));
    }     

import java.util.*;
import java.io.*;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MatrixMultiplication { 
    static double[][] m1 = new double[100][100];
    static double[][] m2 = new double[100][100];

public static void main(String[] args){

    long startTime;
    long endTime;
    long completionTime;
    ArrayList<Long> myTimes = new ArrayList<Long>();
    long addingNumber = 0;
    long averageTime;
    String filepath = "exe_time.csv";

    createMatrix();

    /*This for loop will create 1 thread and then use the execute method from execService
    to multiply the two 100x100 matrices together. The completionTime is how long it takes
    for the whole process to finish. We want to run this thread 25 times and then take the average
    of those completion times*/

    for(int i = 0; i < 25; i++)
    {

    ExecutorService execService = Executors.newFixedThreadPool(1);

    startTime = System.nanoTime(); 

    execService.execute(new LoopTaskA(m1,m2));

    execService.shutdown();

    endTime = System.nanoTime();

    completionTime = (endTime - startTime);

    myTimes.add(completionTime);

    System.out.println("The completion time for one iteration is: " + completionTime);

    }

    /*Takes the completion times that were stored in an arraylist and finds the average*/

    for(int i = 0; i < 25; i++)
    {
        addingNumber = addingNumber + myTimes.remove(0);
    }

    averageTime = (addingNumber / 25);
    System.out.println("The average run time in nanoseconds for 1 thread that ran 25 times is: " + averageTime);
    saveRecord(averageTime, filepath);

    /*We call createMatrix again here so we start with a fresh new matrix*/

    createMatrix();

    /*We are doing the same thing as before but now we have 10 threads and not 1*/

    for(int i = 0; i < 25; i++)
    {

    ExecutorService execService = Executors.newFixedThreadPool(10);

    startTime = System.nanoTime();

        for(int j = 0; j < 10; j++)
        {
            execService.execute(new LoopTaskA(m1,m2));
        } 

    execService.shutdown();

    endTime = System.nanoTime();

    completionTime = (endTime - startTime);

    myTimes.add(completionTime);

    System.out.println("The completion time for one iteration is: " + completionTime);

    }

    for(int i = 0; i < 25; i++)
    {
        addingNumber = addingNumber + myTimes.remove(0);
    }

    averageTime = (addingNumber / 25);
    System.out.println("The average run time in nanoseconds for 10 threads that ran 25 times is: " + averageTime);
    saveRecord(averageTime, filepath);

    createMatrix();

    /*We are doing the same thing as before but now we have 20 threads and not 10*/

    for(int i = 0; i < 25; i++)
    {

    ExecutorService execService = Executors.newFixedThreadPool(20);

    startTime = System.nanoTime();

        for(int j = 0; j < 20; j++)
        {
            execService.execute(new LoopTaskA(m1,m2));
        }

    execService.shutdown();

    endTime = System.nanoTime();

    completionTime = (endTime - startTime);

    myTimes.add(completionTime);

    System.out.println("The completion time for one iteration is: " + completionTime);

    }

    for(int i = 0; i < 25; i++)
    {
        addingNumber = addingNumber + myTimes.remove(0);
    }

    averageTime = (addingNumber / 25);
    System.out.println("The average run time in nanoseconds for 20 threads that ran 25 times is: " + averageTime);
    saveRecord(averageTime, filepath);  

 }

/*Creates the matrix input by taking a random number from the range of
    -10 to 10 and then truncates the number to two decimal places*/

public static double matrixInput(){
    double max = 10.0;
    double min = -10.0;

    Random ran = new Random();
    double random = min + (max - min) * ran.nextDouble();
    double truncatedRan = Math.floor(random*100)/100;
    return truncatedRan;

}

/*Places that random number generated in the matrixInput method into a cell of the matrix.
The goal is to create 2 random 100x100 matrices. The first 100x100 matrix is m1. The second is m2.*/

 public static void createMatrix(){

    for (int row = 0; row < m1.length; row++)
    {
        for (int col = 0; col < m1[0].length; col++)
        {
            m1[row][col] = matrixInput();
        }
    }

    for (int row = 0; row < m2.length; row++)
    {
        for (int col = 0; col < m2[0].length; col++)
        {
            m2[row][col] = matrixInput();
        }
    }

}

/*Method that creates a .csv (comma seperated vector) file which stores 
the average time*/

public static void saveRecord(long averageTime, String filepath)
{
    try
    {
        FileWriter fw = new FileWriter(filepath,true);
        BufferedWriter bw = new BufferedWriter(fw);
        PrintWriter pw = new PrintWriter(bw);

        pw.println(averageTime + ",");
        pw.flush();
        pw.close();

        System.out.println("File has been saved.");
    }   
    catch(Exception E)
    {
        System.out.println("File has NOT been saved.");
    }       
  } 
 }

 import java.util.*;
 public class LoopTaskA implements Runnable{

 double[][] m1;
 double[][] m2;

 @Override
 public void run(){     
    double sum = 0;     
    /*This is to calculate the resulting matrix.We need to know the number or rows of m1 
    and the number of columns in m2 (both of which will be 100 since we want a 100x100 matrix)*/

    double r[][] = new double [100][100];

    /*This multiplies the two 100x100 matrices together. You can think of i here as the row number (which is 100).
    The range of j will depend upon the number of columns in the resultant matrix (range of j = 100)
    The k value will depend upon the number of columns in the first matrix or the number of rows in 
    the second matrix, both of these 100*/
    for(int i = 0; i < 100; i++)
    {

        for(int j = 0; j < 100; j++)
        {
            for(int k = 0; k < 100; k++)
            {
                sum = sum + m1[i][k] * m2[k][j];
            }
            r[i][j] = Math.floor(sum*100)/100;
            sum = 0; //reset to 0 so you can do the calculation for the next value.
        }

    }

    /* for(int i = 0; i < 100; i++)
    {
        for(int j = 0; j < 100; j++)
        {
            System.out.print(r[i][j] + " ");
        }

            System.out.println();
    } */        
 }


 public LoopTaskA(double[][] m1, double[][] m2){
    this.m1 = m1;
    this.m2 = m2;
 }  
}

1 个答案:

答案 0 :(得分:0)

我发现代码中只有一个问题,您应该在shutdown之后调用awaitTermination来阻止当前线程。 shutdown不会等待先前提交的任务完成执行。

  

如果增加数量,不应该减少完成时间   线程,因为我分裂工作量?

不,可用的硬资源(例如,处理器数量)是有界的。 多线程并不总能带来更高的性能,您可以查看this问题。

此外,Executors.newFixedThreadPool()创建的ThreadPoolExecutor用于解决特定问题:

  

线程池解决了两个不同的问题:它们通常提供   在执行大量异步时提高了性能   任务,由于减少了每个任务的调用开销,并且它们提供了   一种绑定和管理资源的方法,包括线程,   在执行任务集合时消耗。

因此,从技术上讲,您正在以正确的方式使用ExecutorSevice。但是,当您增加线程数时,它并不保证您可以获得更高的性能。并且