Многопоточное матричное умножение в Java. Среднее время выключено. Я правильно использую исполнителей? - PullRequest
0 голосов
/ 30 апреля 2018

Я пытаюсь выполнить многопоточное матричное умножение, в котором я сравниваю время выполнения для различного числа потоков, начиная с 1 до 100, с шагом 10 к каждой итерации.

В основном я создаю две матрицы 100x100 со случайными числами в диапазоне от -10,0 до 10,0 в своих ячейках, а затем умножаю их вместе. Я сделаю это 25 раз, используя различное количество потоков (снова увеличивая на 10 каждый раз: первая итерация будет использовать 1 поток, вторая итерация будет использовать 10 потоков, третья - 20 потоков и т. Д.) И найдет среднее значение. время завершения и сохранить это время в файле.

У меня проблема в том, что я не совсем уверен, правильно ли я использую Исполнителей. Например, для меня этот фрагмент кода (я также предоставил весь программный код ниже этого фрагмента) говорит, что я создал 10 потоков, и в каждом потоке я буду использовать метод .execute для запуска моей LoopTaskA, которая происходит с быть умножением матриц. Итак, я пытаюсь сделать одно умножение, разбитое на эти 10 потоков. Это то, что я здесь делаю? Или я умножаю 10 раз на 10 потоков (т.е. одно умножение на поток)?

Причина, по которой я спрашиваю об этом, заключается в том, что когда я читаю всю программу, при каждом увеличении числа потоков я получаю увеличение среднего времени завершения. Не следует ли сократить время завершения, если я увеличу количество потоков, поскольку делю рабочую нагрузку?

По этому другому вопросу я нашел на этом же сайте: может и нет? Но я все еще не уверен, что делаю неправильно.

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 голосов
/ 30 апреля 2018

Я нашел только одну проблему в вашем коде, вы должны вызвать awaitTermination, чтобы заблокировать текущий поток после shutdown. shutdown не ожидает завершения выполнения ранее отправленных задач.


Не следует ли сократить время завершения, если я увеличу количество темы, так как я делю рабочую нагрузку?

Нет, доступные жесткие ресурсы (например, количество процессоров) ограничены. Многопоточность не всегда приносит более высокую производительность, вы можете проверить этот вопрос.

Кроме того, ThreadPoolExecutor, созданный вами Executors.newFixedThreadPool(), используется для решения конкретных проблем:

Пулы потоков решают две разные проблемы: они обычно предоставляют улучшена производительность при выполнении большого количества асинхронных Задачи , из-за уменьшения накладных расходов на вызовы, и они обеспечивают средство связывания и управления ресурсами, включая потоки, расходуется при выполнении набора задач.


Итак, технически, вы используете ExecutorSevice правильный путь. Но это не гарантирует, что вы сможете получить более высокую производительность при увеличении количества потоков. И

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...