首页 > 解决方案 > 多线程矩阵乘法性能问题

问题描述

我正在使用java进行多线程乘法。我正在练习多线程编程。以下是我从 stackoverflow 的另一篇文章中获取的代码。

public class MatMulConcur {

private final static int NUM_OF_THREAD =1 ;
private static Mat matC;

public static Mat matmul(Mat matA, Mat matB) {
matC = new Mat(matA.getNRows(),matB.getNColumns());
return mul(matA,matB);
}

private static Mat mul(Mat matA,Mat matB) {

int numRowForThread;
int numRowA = matA.getNRows();
int startRow = 0;

Worker[] myWorker = new Worker[NUM_OF_THREAD];

for (int j = 0; j < NUM_OF_THREAD; j++) {
    if (j<NUM_OF_THREAD-1){
        numRowForThread = (numRowA / NUM_OF_THREAD);
    } else {
        numRowForThread = (numRowA / NUM_OF_THREAD) + (numRowA % NUM_OF_THREAD);
    }
    myWorker[j] = new Worker(startRow, startRow+numRowForThread,matA,matB);
    myWorker[j].start();
    startRow += numRowForThread;
}

for (Worker worker : myWorker) {
    try {
        worker.join();
    } catch (InterruptedException e) {

    }
  }
  return matC;
 }

private static class Worker extends Thread {

private int startRow, stopRow;
private Mat matA, matB;

public Worker(int startRow, int stopRow, Mat matA, Mat matB) {
    super();
    this.startRow = startRow;
    this.stopRow = stopRow;
    this.matA = matA;
    this.matB = matB;
}

@Override
public void run() {
    for (int i = startRow; i < stopRow; i++) {
        for (int j = 0; j < matB.getNColumns(); j++) {
            double sum = 0;
            for (int k = 0; k < matA.getNColumns(); k++) {
                sum += matA.get(i, k) * matB.get(k, j);
            }
            matC.set(i, j, sum);
        }
    }
  }
}

我为 1,10,20,...,100 个线程运行了这个程序,但性能反而下降了。以下是时间表

  1. 线程 1 需要 18 毫秒
  2. 线程 10 需要 18 毫秒
  3. 线程 20 需要 35 毫秒
  4. 线程 30 需要 38 毫秒
  5. 线程 40 需要 43 毫秒
  6. 线程 50 需要 48 毫秒
  7. 线程 60 需要 57 毫秒
  8. 线程 70 需要 66 毫秒
  9. 线程 80 需要 74 毫秒
  10. 线程 90 耗时 87 毫秒
  11. 线程 100 需要 98 毫秒

任何想法?

标签: javamultithreading

解决方案


人们认为使用多个线程会自动(神奇地!)使任何计算变得更快。这不是这样1

有许多因素可以使多线程加速低于您的预期,或者确实导致减速。

  1. 具有 N 个内核(或超线程)的计算机执行计算的速度最多是具有 1 个内核的计算机的 N 倍。这意味着当您有 T 个线程且 T > N 时,计算性能将被限制为 N。(除此之外,由于时间切片,线程会取得进展。)

  2. 计算机有一定的内存带宽;即它每秒只能在主存上执行一定数量的读/写操作。如果您的应用程序的需求超过了内存子系统可以实现的能力,它将停止(几纳秒)。如果有多个内核同时执行多个线程,那么重要的是总需求。

  3. 处理共享变量或数据结构的典型多线程应用程序将使用volatile或显式同步来执行此操作。这两者都增加了对内存系统的需求。

  4. 当使用显式同步并且两个线程想要同时持有一个锁时,其中一个会被阻塞。这种锁竞争会减慢计算速度。实际上,如果过去存在对锁的争用,计算可能会减慢。

  5. 线程创建是昂贵的。即使从线程池中获取现有线程也可能相对昂贵。如果您使用线程执行的任务太小,则设置成本可能会超过可能的加速。

还有一个问题是您可能会遇到编写不佳的基准测试的问题;例如,在进行计时测量之前,JVM 可能没有正确预热。


您的问题中没有足够的细节来确定上述哪些因素可能会影响您的应用程序的性能。但它很可能是 1 2 和 5 的组合……取决于使用了多少核心,CPU 内存缓存有多大,矩阵有多大,以及其他因素。


1 - 事实上,如果这是真的,那么我们就不需要购买具有大量内核的计算机。我们可以使用越来越多的线程。如果你有足够的内存,你可以在一台机器上进行无限量的计算。比特币挖矿将是轻而易举的事。当然,这不是真的


推荐阅读