2015-12-29 191 views
4

我不知道是否有做在Java中8流以下的更濃縮的方式:讓一個編輯的Java 8矩陣*向量乘法

public static double[] multiply(double[][] matrix, double[] vector) { 
    int rows = matrix.length; 
    int columns = matrix[0].length; 

    double[] result = new double[rows]; 

    for (int row = 0; row < rows; row++) { 
     double sum = 0; 
     for (int column = 0; column < columns; column++) { 
      sum += matrix[row][column] 
        * vector[column]; 
     } 
     result[row] = sum; 
    } 
    return result; 
} 

。我收到了很好的答案,但是性能大約是10倍比舊的執行速度較慢,所以我加入這裏的測試代碼的情況下,有人想調查此事:

@Test 
public void profile() { 
    long start; 
    long stop; 
    int tenmillion = 10000000; 
    double[] vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 

    double[][] matrix = new double[tenmillion][10]; 

    for (int i = 0; i < tenmillion; i++) { 
     matrix[i] = vector.clone(); 
    } 
    start = System.currentTimeMillis(); 
    multiply(matrix, vector); 
    stop = System.currentTimeMillis(); 
} 

回答

6

使用流將是一個直接的方法以下內容:

public static double[] multiply(double[][] matrix, double[] vector) { 
    return Arrays.stream(matrix) 
       .mapToDouble(row -> 
        IntStream.range(0, row.length) 
          .mapToDouble(col -> row[col] * vector[col]) 
          .sum() 
       ).toArray(); 
} 

此創建矩陣(Stream<double[]>)的每一行的一個流,則每一行映射到所得計算與vector陣列產品的雙精度值。

我們必須在索引上使用Stream來計算產品,因爲不幸的是沒有內置工具將兩個Stream壓縮在一起。

+1

Tunaki嗨 - 感謝 - 這工作!儘管如此,我對於表演有點驚訝。對於有1000萬行的矩陣,它需要10倍的時間才能完成...所以我想我會堅持'舊'循環。不過謝謝。 – Ole

2

測量性能的方式對於衡量性能並不是很可靠,而且手動編寫微型基準測試通常也不是一個好主意。例如,在編譯代碼的同時,JVM可能會選擇更改執行順序,並且可能無法分配啓動和停止變量,因此您的測量結果會導致意想不到的結果。預熱JVM以及讓JIT編譯器進行所有優化也非常重要。 GC還可以在引入應用程序的吞吐量和響應時間方面發揮非常重要的作用。我強烈建議使用JMH和Caliper等專用工具進行微型基準測試。

我還用JVM熱身,隨機數據集和更多的迭代編寫了一些基準測試代碼。事實證明,Java 8流正在給出更好的結果。

/** 
* 
*/ 
public class MatrixMultiplicationBenchmark { 
    private static AtomicLong start = new AtomicLong(); 
    private static AtomicLong stop = new AtomicLong(); 
    private static Random random = new Random(); 

    /** 
    * Main method that warms-up each implementation and then runs the benchmark. 
    * 
    * @param args main class args 
    */ 
    public static void main(String[] args) { 
     // Warming up with more iterations and smaller data set 
     System.out.println("Warming up..."); 
     IntStream.range(0, 10_000_000).forEach(i -> run(10, MatrixMultiplicationBenchmark::multiplyWithStreams)); 
     IntStream.range(0, 10_000_000).forEach(i -> run(10, MatrixMultiplicationBenchmark::multiplyWithForLoops)); 

     // Running with less iterations and larger data set 
     startWatch("Running MatrixMultiplicationBenchmark::multiplyWithForLoops..."); 
     IntStream.range(0, 10).forEach(i -> run(10_000_000, MatrixMultiplicationBenchmark::multiplyWithForLoops)); 
     endWatch("MatrixMultiplicationBenchmark::multiplyWithForLoops"); 

     startWatch("Running MatrixMultiplicationBenchmark::multiplyWithStreams..."); 
     IntStream.range(0, 10).forEach(i -> run(10_000_000, MatrixMultiplicationBenchmark::multiplyWithStreams)); 
     endWatch("MatrixMultiplicationBenchmark::multiplyWithStreams"); 
    } 

    /** 
    * Creates the random matrix and vector and applies them in the given implementation as BiFunction object. 
    * 
    * @param multiplyImpl implementation to use. 
    */ 
    public static void run(int size, BiFunction<double[][], double[], double[]> multiplyImpl) { 
     // creating random matrix and vector 
     double[][] matrix = new double[size][10]; 
     double[] vector = random.doubles(10, 0.0, 10.0).toArray(); 
     IntStream.range(0, size).forEach(i -> matrix[i] = random.doubles(10, 0.0, 10.0).toArray()); 

     // applying matrix and vector to the given implementation. Returned value should not be ignored in test cases. 
     double[] result = multiplyImpl.apply(matrix, vector); 
    } 

    /** 
    * Multiplies the given vector and matrix using Java 8 streams. 
    * 
    * @param matrix the matrix 
    * @param vector the vector to multiply 
    * 
    * @return result after multiplication. 
    */ 
    public static double[] multiplyWithStreams(final double[][] matrix, final double[] vector) { 
     final int rows = matrix.length; 
     final int columns = matrix[0].length; 

     return IntStream.range(0, rows) 
       .mapToDouble(row -> IntStream.range(0, columns) 
         .mapToDouble(col -> matrix[row][col] * vector[col]) 
         .sum()).toArray(); 
    } 

    /** 
    * Multiplies the given vector and matrix using vanilla for loops. 
    * 
    * @param matrix the matrix 
    * @param vector the vector to multiply 
    * 
    * @return result after multiplication. 
    */ 
    public static double[] multiplyWithForLoops(double[][] matrix, double[] vector) { 
     int rows = matrix.length; 
     int columns = matrix[0].length; 

     double[] result = new double[rows]; 

     for (int row = 0; row < rows; row++) { 
      double sum = 0; 
      for (int column = 0; column < columns; column++) { 
       sum += matrix[row][column] * vector[column]; 
      } 
      result[row] = sum; 
     } 
     return result; 
    } 

    private static void startWatch(String label) { 
     System.out.println(label); 
     start.set(System.currentTimeMillis()); 
    } 

    private static void endWatch(String label) { 
     stop.set(System.currentTimeMillis()); 
     System.out.println(label + " took " + ((stop.longValue() - start.longValue())/1000) + "s"); 
    } 
} 

這裏是輸出

Warming up... 
Running MatrixMultiplicationBenchmark::multiplyWithForLoops... 
MatrixMultiplicationBenchmark::multiplyWithForLoops took 100s 
Running MatrixMultiplicationBenchmark::multiplyWithStreams... 
MatrixMultiplicationBenchmark::multiplyWithStreams took 89s 
+0

嗨 - 非常感謝您的反饋。這是非常令人鼓舞的。在那裏我認爲我將不得不堅持香草:)。我將不得不再玩一次。再次感謝! – Ole

+0

更好的是使用像JMH這樣的基準測試框架來處理所有的熱身階段。 – Tunaki