乘法

2009-10-15 34 views
5

我已经编码了....我相信我的方法是正确的,但我不是100%确定的。关于线程,我不明白为什么我不能运行(new MatrixThread(...)).start()而不是使用ExecutorService乘法

此外,当我标杆...方法相对于传统方法,经典的是快...

我在做什么错?

P.S.请让我知道是否需要进一步澄清。

+0

你的代码缺少“Multiply”方法 – 2009-10-15 18:12:11

+1

你为什么要多线程这样的东西?这完全是CPU绑定的,它不像你等待I/O的线程被阻塞。 – 2009-10-15 18:14:06

+0

多线程可能正常工作,但是要更多地取决于多少CPU(10x10乘以10x10在您的示例中创建100个线程......您可能只有2-8个CPU)以及矩阵有多大(它们是否适合L2/L3缓存?)。像MKL和OpenCL这样的本地库在这方面做得更好。 – basszero 2009-10-15 18:18:26

回答

5

您正在创建大量的线程。创建线程不仅花费昂贵,而且对于CPU绑定的应用程序,您不需要比可用处理器更多的线程(如果这样做,则必须在线程之间切换处理能力,这也可能导致缓存错过的是非常贵)。

发送线程到execute也是不必要的;它只需要一个Runnable。您可以通过应用这些变化得到一个大的性能提升:

  1. 充分利用ExecutorService静态成员,它的大小为当前处理器,并发送一个ThreadFactory所以它不守程序main后运行已完成。 (这很可能是建筑清洁送它作为一个参数的方法,而不是将它作为一个静态字段,我将它作为一个读者练习☺。)

    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; 
         } 
        }); 
    
  2. MatrixThread实施Runnable而比继承Thread。线程创建起来很昂贵; POJO非常便宜。你也可以使它变得更小(因为非静态类获得对封闭对象的隐式引用)。

    private static class MatrixThread implements Runnable 
    
  3. 从变化(1),你可以不再awaitTermination以确保所有任务都完成(因为这名工人池)。相反,使用返回Future<?>submit方法。收集列表中的所有未来对象,并在提交所有任务时,遍历列表并针对每个对象调用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; 
} 

会不会是比单线程版本更快?那么,在我可以说是蹩脚的盒子上,多线程版本对于n < 1024.较慢。

尽管如此,这只是抓表面。该真正的问题是,你创建一个MatrixThread很多情况下 - 你的内存消耗O(n²),这是一个非常不好的迹象。将内部for循环移入MatrixThread.run将使性能提高craploads(理想情况下,您不会创建比您有工作线程更多的任务)。


编辑:当我有更紧迫的事情要做,我无法抗拒进一步优化这一点。我想出了这个(...窘况丑陋的代码段),“只有”创造就业机会O(n)

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; 
     } 
    } 
} 

它仍然不是很大,但基本上是多线程版本可以计算什么,你会耐心等待足以等待,它会比单线程版本更快。

+0

非常感谢您的帮助!代码有点令人困惑,但我想我可以弄明白。出于某种原因,当我运行代码时,非线程版本仍然更快,但它比我之前的版本更合理。 谢谢! – 2009-10-16 00:02:25

+0

好吧,在几个部分分裂工作总是有开销。对于'n'的小值,多线程版本可能总是比较慢,但是'n'越大,多线程版本可能越好。这个解决方案仍然有很多开销,因为它创建了'n'个任务(因此具有'O(n)'的同步开销)。如果你能把它分解成最多一些固定数量的任务(比如'可用处理器* 2'或其他),那么对于大数值的'n',程序会变得更快。 – gustafc 2009-10-16 05:24:29

+0

另外,对于'n'的小值,你可以做非线程乘法运算,因为它可能总是更快。 – gustafc 2009-10-16 05:26:02

6

即使在使用ExecutorService时,创建线程也会产生一堆开销。我怀疑你为什么多线程方法很慢的原因是你花99%创建一个新的线程,只有1%或更少,做实际的数学。

通常,为了解决这个问题,你需要将一大堆操作加在一起并在单个线程上运行。我不是100%在这种情况下如何做到这一点,但我建议将矩阵分成更小的块(比如10个更小的矩阵)并在线程上运行,而不是在每个单元中运行它们。

1

首先,您应该在您使用的quadcore上使用大小与您拥有的核心数量相同的newFixedThreadPool。4.其次,不要为每个矩阵创建一个新核心。

如果你做的ExecutorService静态成员变量在我的512

也是一个矩阵大小得到的线程版本几乎一致更快的执行速度,改变MatrixThread来实现Runnable接口,而不是扩展Thread的执行还可以加快其中螺纹在我的机器上2x在512上的速度是最快的

+0

非常感谢,我会牢记这一点! – 2009-10-16 00:04:04