Multiplication De Matrice De Flux Java 8 10 Fois Plus Lente Que Pour La Boucle?
J'ai créé un module qui effectue une multiplication matricielle à l'aide de flux. Il peut être trouvé ici: https://github.com/firefly-math/firefly-math-linear-real/
J'ai essayé d'écrire un benchmark afin de comparer l'implémentation de la boucle stream à l'implémentation de la boucle for correspondante dans Apache Commons Math.
Le module de référence est ici: https://github.com/firefly-math/firefly-math-benchmark
Et le benchmark réel ici: https://github.com/firefly-math/firefly-math-benchmark/blob/master/src/main/java/com/fireflysemantics/benchmark/MultiplyBenchmark.java
Lorsque j'exécute le benchmark sur des matrices de taille 100X100 et 1000X1000, il s'avère que Apache Commons Math (utilise une boucle for) est 10 fois plus rapide (à peu près) que l'implémentation de flux correspondante.
# Run complete. Total time: 00:14:10
Benchmark Mode Cnt Score Error Units
MultiplyBenchmark.multiplyCM1000_1000 avgt 30 1040.804 ± 11.796 ms/op
MultiplyBenchmark.multiplyCM100_100 avgt 30 0.790 ± 0.010 ms/op
MultiplyBenchmark.multiplyFM1000_1000 avgt 30 11981.228 ± 405.812 ms/op
MultiplyBenchmark.multiplyFM100_100 avgt 30 7.224 ± 0.685 ms/op
Ai-je fait quelque chose de mal dans le benchmark (espérons-le :) )?
J'ajoute les méthodes testées de telle sorte que tout le monde puisse voir ce qui est comparé. Il s'agit de l'Apache Commons Math Array2DRowRealMatrix.multiplier() méthode:
/**
* Returns the result of postmultiplying {@code this} by {@code m}.
*
* @param m matrix to postmultiply by
* @return {@code this * m}
* @throws DimensionMismatchException if
* {@code columnDimension(this) != rowDimension(m)}
*/
public Array2DRowRealMatrix multiply(final Array2DRowRealMatrix m)
throws DimensionMismatchException {
MatrixUtils.checkMultiplicationCompatible(this, m);
final int nRows = this.getRowDimension();
final int nCols = m.getColumnDimension();
final int nSum = this.getColumnDimension();
final double[][] outData = new double[nRows][nCols];
// Will hold a column of "m".
final double[] mCol = new double[nSum];
final double[][] mData = m.data;
// Multiply.
for (int col = 0; col < nCols; col++) {
// Copy all elements of column "col" of "m" so that
// will be in contiguous memory.
for (int mRow = 0; mRow < nSum; mRow++) {
mCol[mRow] = mData[mRow][col];
}
for (int row = 0; row < nRows; row++) {
final double[] dataRow = data[row];
double sum = 0;
for (int i = 0; i < nSum; i++) {
sum += dataRow[i] * mCol[i];
}
outData[row][col] = sum;
}
}
return new Array2DRowRealMatrix(outData, false);
}
Et c'est l'implémentation de flux correspondante:
/**
* Returns a {@link BinaryOperator} that multiplies {@link SimpleMatrix}
* {@code m1} times {@link SimpleMatrix} {@code m2} (m1 X m2).
*
* Example {@code multiply(true).apply(m1, m2);}
*
* @param parallel
* Whether to perform the operation concurrently.
*
* @throws MathException
* Of type {@code MATRIX_DIMENSION_MISMATCH__MULTIPLICATION} if
* {@code m} is not the same size as {@code this}.
*
* @return the {@link BinaryOperator} that performs the operation.
*/
public static BinaryOperator<SimpleMatrix> multiply(boolean parallel) {
return (m1, m2) -> {
checkMultiplicationCompatible(m1, m2);
double[][] a1 = m1.toArray();
double[][] a2 = m2.toArray();
Stream<double[]> stream = Arrays.stream(a1);
stream = parallel ? stream.parallel() : stream;
final double[][] result =
stream.map(r -> range(0, a2[0].length)
.mapToDouble(i -> range(0, a2.length).mapToDouble(j -> r[j]
* a2[j][i]).sum())
.toArray()).toArray(double[][]::new);
return new SimpleMatrix(result);
};
}
TIA, Ole
1 answers
Jetez un oeil à DoublePipeline.toArray
:
public final double[] toArray() {
return Nodes.flattenDouble((Node.OfDouble) evaluateToArrayNode(Double[]::new))
.asPrimitiveArray();
}
Il semble qu'un tableau en boîte soit créé en premier, puis converti en tableau primitif.