Java 多线程矩阵乘法
我编写了一个多线程矩阵乘法。我相信我的方法是正确的,但我不是100%肯定。关于线程,我不明白为什么我不能只运行Java 多线程矩阵乘法,java,multithreading,Java,Multithreading,我编写了一个多线程矩阵乘法。我相信我的方法是正确的,但我不是100%肯定。关于线程,我不明白为什么我不能只运行(新的MatrixThread(…).start(),而不使用ExecutorService 此外,当我对多线程方法与经典方法进行基准测试时,经典方法要快得多 我做错了什么 矩阵类: import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
(新的MatrixThread(…).start()
,而不使用ExecutorService
此外,当我对多线程方法与经典方法进行基准测试时,经典方法要快得多
我做错了什么
矩阵类:
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class Matrix
{
private int dimension;
private int[][] template;
public Matrix(int dimension)
{
this.template = new int[dimension][dimension];
this.dimension = template.length;
}
public Matrix(int[][] array)
{
this.dimension = array.length;
this.template = array;
}
public int getMatrixDimension() { return this.dimension; }
public int[][] getArray() { return this.template; }
public void fillMatrix()
{
Random randomNumber = new Random();
for(int i = 0; i < dimension; i++)
{
for(int j = 0; j < dimension; j++)
{
template[i][j] = randomNumber.nextInt(10) + 1;
}
}
}
@Override
public String toString()
{
String retString = "";
for(int i = 0; i < this.getMatrixDimension(); i++)
{
for(int j = 0; j < this.getMatrixDimension(); j++)
{
retString += " " + this.getArray()[i][j];
}
retString += "\n";
}
return retString;
}
public static Matrix classicalMultiplication(Matrix a, Matrix b)
{
int[][] result = new int[a.dimension][b.dimension];
for(int i = 0; i < a.dimension; i++)
{
for(int j = 0; j < b.dimension; j++)
{
for(int k = 0; k < b.dimension; k++)
{
result[i][j] += a.template[i][k] * b.template[k][j];
}
}
}
return new Matrix(result);
}
public Matrix multiply(Matrix multiplier) throws InterruptedException
{
Matrix result = new Matrix(dimension);
ExecutorService es = Executors.newFixedThreadPool(dimension*dimension);
for(int currRow = 0; currRow < multiplier.dimension; currRow++)
{
for(int currCol = 0; currCol < multiplier.dimension; currCol++)
{
//(new MatrixThread(this, multiplier, currRow, currCol, result)).start();
es.execute(new MatrixThread(this, multiplier, currRow, currCol, result));
}
}
es.shutdown();
es.awaitTermination(2, TimeUnit.DAYS);
return result;
}
private class MatrixThread extends Thread
{
private Matrix a, b, result;
private int row, col;
private MatrixThread(Matrix a, Matrix b, int row, int col, Matrix result)
{
this.a = a;
this.b = b;
this.row = row;
this.col = col;
this.result = result;
}
@Override
public void run()
{
int cellResult = 0;
for (int i = 0; i < a.getMatrixDimension(); i++)
cellResult += a.template[row][i] * b.template[i][col];
result.template[row][col] = cellResult;
}
}
}
另外,如果需要进一步的澄清,请告诉我。创建线程涉及大量开销,即使在使用ExecutorService时也是如此。我怀疑,多线程方法之所以如此缓慢,是因为您花费99%的时间创建一个新线程,而实际计算只花费1%或更少的时间
通常,要解决这个问题,您需要将一系列操作批处理在一起,并在单个线程上运行这些操作。在这种情况下,我不是百分之百地知道该怎么做,但我建议将矩阵分解成更小的块(比如,10个更小的矩阵),并在线程上运行这些块,而不是在自己的线程中运行每个单元格。首先,在使用4个四核的四核上,您应该使用一个新的FixedThreadPool,其大小与您拥有的内核相同。其次,不要为每个矩阵创建一个新的矩阵 如果将executorservice设置为静态成员变量,则在矩阵大小为512的情况下,线程化版本的执行速度几乎始终更快
另外,将MatrixThread更改为实现Runnable而不是扩展Thread还可以将执行速度提高到我的机器上线程所在的位置,在512上的速度是创建大量线程的速度的2倍。不仅创建线程的成本很高,而且对于CPU受限的应用程序,您不需要比可用处理器更多的线程(如果需要,您必须花费处理能力在线程之间切换,这也可能导致非常昂贵的缓存未命中) 也不需要将线程发送到
execute
;它只需要一个可运行的。通过应用这些更改,您将获得巨大的性能提升:
将ExecutorService
设置为静态成员,根据当前处理器调整其大小,并向其发送ThreadFactory
,使其在main
完成后不会保持程序运行。(在体系结构上,将其作为参数发送到方法,而不是将其作为静态字段保存,可能会更简洁;我将此作为练习留给读者。☺)
使MatrixThread
实现Runnable
而不是继承Thread
。创建线程的成本很高;POJO非常便宜。您还可以将其设置为静态
,这会使实例更小(因为非静态类获得对封闭对象的隐式引用)
从更改(1)中,您不能再等待终止以确保所有任务都已完成(因为此工作人员池)。相反,请使用submit
方法,该方法返回一个Future
。收集列表中的所有Future对象,提交所有任务后,在列表上迭代,并为每个对象调用get
您的multiply
方法现在应该如下所示:
public Matrix multiply(Matrix multiplier) throws InterruptedException {
Matrix result = new Matrix(dimension);
List<Future<?>> futures = new ArrayList<Future<?>>();
for(int currRow = 0; currRow < multiplier.dimension; currRow++) {
for(int currCol = 0; currCol < multiplier.dimension; currCol++) {
Runnable worker = new MatrixThread(this, multiplier, currRow, currCol, result);
futures.add(workerPool.submit(worker));
}
}
for (Future<?> f : futures) {
try {
f.get();
} catch (ExecutionException e){
throw new RuntimeException(e); // shouldn't happen, but might do
}
}
return result;
}
它仍然不太好,但基本上多线程版本可以计算任何您需要耐心等待的内容,并且它比单线程版本更快。您的代码缺少“乘法”方法为什么要使用这样的多线程?这是完全受CPU限制的,不像有线程被阻塞等待I/O。多线程可以正常工作,但更多地取决于有多少CPU(在您的示例中,10x10乘以10x10创建100个线程…您可能只有2-8个CPU)以及矩阵有多大(它们是否适合二级/三级缓存?)。像MKL和OpenCL这样的本机库在这方面做得更好。matt b:多个硬件线程??尽管可能远不及其中的n^2个。关于扩展线程
。几乎总是一个坏主意。在这种情况下,代码甚至不会启动线程。线程
实现可运行
的事实是不幸的。谢谢你非常感谢您的帮助!代码有点混乱,但我想我能弄明白。出于某种原因,当我运行代码时,非线程版本速度更快,但它与以前相比有了更合理的差异。谢谢!嗯,将作业分成几个部分总是会有开销。对于 n
多线程版本可能总是比较慢,但是n
越大,多线程版本可能越好。此解决方案在创建n
任务时仍有相当大的开销(因此同步开销为O(n)
)。如果您可以让它将乘法分解为最多一些固定数量的任务(例如,可用处理器*2
或其他),那么对于n
的大值,程序会更快。此外,对于n
的小值,您可以只执行非线程乘法,因为它可能总是更快。
private static final ExecutorService workerPool =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
});
private static class MatrixThread implements Runnable
public Matrix multiply(Matrix multiplier) throws InterruptedException {
Matrix result = new Matrix(dimension);
List<Future<?>> futures = new ArrayList<Future<?>>();
for(int currRow = 0; currRow < multiplier.dimension; currRow++) {
for(int currCol = 0; currCol < multiplier.dimension; currCol++) {
Runnable worker = new MatrixThread(this, multiplier, currRow, currCol, result);
futures.add(workerPool.submit(worker));
}
}
for (Future<?> f : futures) {
try {
f.get();
} catch (ExecutionException e){
throw new RuntimeException(e); // shouldn't happen, but might do
}
}
return result;
}
public Matrix multiply(Matrix multiplier) throws InterruptedException {
Matrix result = new Matrix(dimension);
List<Future<?>> futures = new ArrayList<Future<?>>();
for(int currRow = 0; currRow < multiplier.dimension; currRow++) {
Runnable worker = new MatrixThread2(this, multiplier, currRow, result);
futures.add(workerPool.submit(worker));
}
for (Future<?> f : futures) {
try {
f.get();
} catch (ExecutionException e){
throw new RuntimeException(e); // shouldn't happen, but might do
}
}
return result;
}
private static class MatrixThread2 implements Runnable
{
private Matrix self, mul, result;
private int row, col;
private MatrixThread2(Matrix a, Matrix b, int row, Matrix result)
{
this.self = a;
this.mul = b;
this.row = row;
this.result = result;
}
@Override
public void run()
{
for(int col = 0; col < mul.dimension; col++) {
int cellResult = 0;
for (int i = 0; i < self.getMatrixDimension(); i++)
cellResult += self.template[row][i] * mul.template[i][col];
result.template[row][col] = cellResult;
}
}
}