Проблема производительности многопоточного матричного умножения - PullRequest
0 голосов
/ 29 апреля 2018

Я использую 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 миллисекунд

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 29 апреля 2018

Люди думают, что использование нескольких потоков автоматически (волшебным образом) ускорит любые вычисления. Это не так 1 .

Существует ряд факторов, которые могут сделать многопоточное ускорение меньшим, чем вы ожидаете, или даже привести к замедлению.

  1. Компьютер с N ядрами (или гиперпотоками) может выполнять вычисления максимум N раз быстрее, чем компьютер с 1 ядром. Это означает, что когда у вас есть T потоков, где T> N, производительность вычислений будет ограничена N. (Кроме того, потоки добиваются прогресса из-за сокращения времени.)

  2. Компьютер имеет определенную пропускную способность памяти; то есть он может выполнять только определенное количество операций чтения / записи в секунду в основной памяти. Если у вас есть приложение, в котором требование превышает возможности подсистемы памяти, оно будет остановлено (на несколько наносекунд). Если есть много ядер, выполняющих много потоков одновременно, то имеет значение совокупный спрос.

  3. Типичное многопоточное приложение, работающее с общими переменными или структурами данных, будет использовать volatile или явную синхронизацию для этого. И то, и другое увеличивает спрос на систему памяти.

  4. Если используется явная синхронизация и два потока хотят одновременно удерживать блокировку, один из них будет заблокирован. Это состязание блокировок замедляет вычисления. Действительно, вычисления, вероятно, будут замедлены, если в блокировке было после конфликта.

  5. Создание темы стоит дорого. Даже получение существующего потока из пула потоков может быть относительно дорогим. Если задача, выполняемая с потоком, слишком мала, стоимость установки может перевесить возможное ускорение.

Существует также проблема, с которой вы можете столкнуться с плохо написанным тестом; например JVM, возможно, не будет должным образом подогреваться перед измерением времени.


Недостаточно подробно в вашем вопросе, чтобы быть уверенным, какой из вышеперечисленных факторов может повлиять на производительность вашего приложения. Но, скорее всего, это будет комбинация 1 2 и 5 ... в зависимости от того, сколько ядер используется, насколько велики кэш-память ЦП, насколько велика матрица и другие факторы.


1 - Действительно, если бы это было правдой, нам не нужно было бы покупать компьютеры с большим количеством ядер. Мы могли бы просто использовать все больше и больше тем. Если у вас достаточно памяти, вы можете выполнить бесконечное вычислений на одной машине. Биткойн-майнинг был бы пустяком. Конечно, это не так .

0 голосов
/ 29 апреля 2018

Использование многопоточности не в первую очередь для производительности, но для распараллеливания. Однако в некоторых случаях распараллеливание может повысить производительность.

Ваш компьютер не имеет бесконечных ресурсов. Добавление все большего количества потоков снизит производительность. Это все равно что запускать все больше и больше приложений, вы не ожидаете, что программа будет работать быстрее, когда вы запускаете другую программу, и вы, вероятно, не удивитесь, если она будет работать медленнее.

До определенного момента производительность будет оставаться постоянной (на вашем компьютере все еще есть ресурсы для обработки спроса), но в какой-то момент вы достигнете максимума, который ваш компьютер может обработать, и производительность снизится. Это именно то, что показывает ваш результат. Производительность остается несколько постоянной с 1 или 10 потоками, а затем постоянно падает.

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