как ускорить этот код?исчисление итерации по строкам и столбцам матрицы - PullRequest
0 голосов
/ 02 марта 2012

У меня есть кусок кода, который выполняет вычисления на матрице, просматривая ее строки и столбцы.Выполненное исчисление представляет собой меру косинусного расстояния с кодом, который я нашел в Интернете (не удалось получить ссылку прямо сейчас).

Может быть 10000 строк и столбцов.Матрица симметрична, поэтому мне просто нужно повторить половину.Значения с плавающей точкой.

Проблема: она очень медленная (кажется, это займет от 3 до 6 часов).Кто-нибудь может указать мне на улучшения?Thx!

Примечание к коду: он использует абстрактный класс для гибкости: таким образом, вычисление косинуса, определенное в отдельном классе, может быть легко заменено другим.

Код:

import Jama.Matrix;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.ExecutionException;

public abstract class AbstractSimilarity {

    HashSet<Triple<Double, Integer, Integer>> set = new HashSet();
    public ArrayList<Thread> listThreads = new ArrayList();

    public void transform(Matrix matrixToBeTransformed) throws InterruptedException, 
ExecutionException {

        int numDocs = termDocumentMatrix.getColumnDimension();

        Main.similarityMatrix = new Matrix(numDocs, numDocs);

        System.out.println("size of the matrix: " + numDocs + "x " + numDocs);

        //1. iteration through all rows of the matrixToBeTransformed
        for (int i = numDocs - 1; i >0 ; i--) {
            System.out.println("matrix treatment... " + ((float) i / (float) numDocs * 100) + "%");

            //2. isolates the row i of this matrixToBeTransformed
            Matrix sourceDocMatrix = matrixToBeTransformed.getMatrix(
                    0, matrixToBeTransformed.getRowDimension() - 1, i, i);



            // 3. Iterates through all columns of the matrixToBeTransformed
//            for (int j = 0; j < numDocs; j++) {
//                if (j < i) {
//
//                    //4. isolates the column j of this matrixToBeTransformed
//                    Matrix targetDocMatrix = matrixToBeTransformed.getMatrix(
//                            0, matrixToBeTransformed.getRowDimension() - 1, j, j);


                    //5. computes the similarity between this given row and this given column and writes it in a resultMatrix
//                    Main.resultMatrix.set(i, j, computeSimilarity(sourceDocMatrix, targetDocMatrix));
//                } else {
//                    Main.resultMatrix.set(i, j, 0);

//                }
//
//            }
        }

Класс, который определяет вычисление, которое будет сделано:

import Jama.Matrix;

public class CosineSimilarity extends AbstractSimilarity{

  @Override
  protected double computeSimilarity(Matrix sourceDoc, Matrix targetDoc) {
    double dotProduct = sourceDoc.arrayTimes(targetDoc).norm1();
    double eucledianDist = sourceDoc.normF() * targetDoc.normF();
    return dotProduct / eucledianDist;
  }

}

1 Ответ

2 голосов
/ 02 марта 2012

Похоже, вы имеете дело с алгоритмом ^ 3.п ^ 2, потому что вы заполняете (половину) матрицы.Времена n снова, потому что методы для заполнения каждого элемента (точка продукта / fnorm) занимают время n.Хорошая новость заключается в том, что, поскольку вычисления не зависят друг от друга, вы можете использовать многопоточность, чтобы ускорить его.

public class DoCalc extends Thread
{
  public Matrix localM;
  int startRow;
  int endRow;
  public DoCalc(Matrix mArg, int startArg, int endArg)
  {
    localM=mArg;
    startRow=startArg;
    endRow=endArg;
  }

  public void doCalc()
  {
    //Pseudo-code
    for each row from startRow to endRow
      for each column 0 to size-1
        result[i][j] = similarityCalculation
  }
  public void run()
  {
    doCalc();
  }
}

public void transform(Matrix toBeTransformed)
{
  int numDocs = termDocumentMatrix.getColumnDimension();

  Main.similarityMatrix = new Matrix(numDocs, numDocs);
  Vector<DoCalc> running = new Vector<DoCalc>();
  int blockSize = 10;
  for (int x = 0; x < numDocs-1;x+=blockSize)
  {
    DoCalc tempThread = new DoCalc(toBeTransformed,x,(x+blockSize>numDocs-1)?numDocs-1:x+blockSize);
    tempThread.start();
    running.add(tempThread);
  }

  for (DoCalc dc : running)
    dc.join();

}

Важные примечания:

Это очень наивная реализация.Если вы попытаетесь запустить его с массивами вашего размера, он породит 1000 потоков.Вы можете либо поиграть с blockSize, либо взглянуть на объединение потоков.

В лучшем случае это даст вам многократное увеличение скорости, 4x и т. Д. Если вы хотите увеличить порядок на порядок, вам нужно будет правильно профилировать/ или измените свой алгоритм на что-то более эффективное.Учитывая задачу, которую вы пытаетесь выполнить (выполнение относительно дорогой задачи для каждого элемента в матрице), последнее может оказаться невозможным.

Редактирование: многопоточность значительно увеличит скорость, только если вы связаны с процессором ииметь многоядерный процессор с относительно простаивающими ядрами.

...