Minor optimization for RealNDAlgebra

This commit is contained in:
Alexander Nozik 2021-01-19 22:24:42 +03:00
parent 4c256a9f14
commit d0c9d97706
11 changed files with 42 additions and 67 deletions

View File

@ -28,7 +28,7 @@ internal class ExpressionsInterpretersBenchmark {
@Benchmark @Benchmark
fun mstExpression() { fun mstExpression() {
val expr = algebra.mstInField { val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) symbol("x") * 2.0 + 2.0 / symbol("x") - 16.0
} }
invokeAndSum(expr) invokeAndSum(expr)
@ -37,7 +37,7 @@ internal class ExpressionsInterpretersBenchmark {
@Benchmark @Benchmark
fun asmExpression() { fun asmExpression() {
val expr = algebra.mstInField { val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) symbol("x") * 2.0 + 2.0 / symbol("x") - 16.0
}.compile() }.compile()
invokeAndSum(expr) invokeAndSum(expr)

View File

@ -2,9 +2,8 @@ package kscience.kmath.benchmarks
import kotlinx.benchmark.Benchmark import kotlinx.benchmark.Benchmark
import kscience.kmath.commons.linear.CMMatrixContext import kscience.kmath.commons.linear.CMMatrixContext
import kscience.kmath.commons.linear.toCM
import kscience.kmath.ejml.EjmlMatrixContext import kscience.kmath.ejml.EjmlMatrixContext
import kscience.kmath.ejml.toEjml
import kscience.kmath.linear.BufferMatrixContext import kscience.kmath.linear.BufferMatrixContext
import kscience.kmath.linear.RealMatrixContext import kscience.kmath.linear.RealMatrixContext
import kscience.kmath.linear.real import kscience.kmath.linear.real
@ -26,11 +25,11 @@ class DotBenchmark {
val matrix1 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } val matrix1 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val matrix2 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } val matrix2 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val cmMatrix1 = matrix1.toCM() val cmMatrix1 = CMMatrixContext { matrix1.toCM() }
val cmMatrix2 = matrix2.toCM() val cmMatrix2 = CMMatrixContext { matrix2.toCM() }
val ejmlMatrix1 = matrix1.toEjml() val ejmlMatrix1 = EjmlMatrixContext { matrix1.toEjml() }
val ejmlMatrix2 = matrix2.toEjml() val ejmlMatrix2 = EjmlMatrixContext { matrix2.toEjml() }
} }
@Benchmark @Benchmark
@ -49,22 +48,23 @@ class DotBenchmark {
@Benchmark @Benchmark
fun ejmlMultiplicationwithConversion() { fun ejmlMultiplicationwithConversion() {
EjmlMatrixContext {
val ejmlMatrix1 = matrix1.toEjml() val ejmlMatrix1 = matrix1.toEjml()
val ejmlMatrix2 = matrix2.toEjml() val ejmlMatrix2 = matrix2.toEjml()
EjmlMatrixContext {
ejmlMatrix1 dot ejmlMatrix2 ejmlMatrix1 dot ejmlMatrix2
} }
} }
@Benchmark @Benchmark
fun bufferedMultiplication() { fun bufferedMultiplication() {
BufferMatrixContext(RealField, Buffer.Companion::real).invoke{ BufferMatrixContext(RealField, Buffer.Companion::real).invoke {
matrix1 dot matrix2 matrix1 dot matrix2
} }
} }
@Benchmark @Benchmark
fun realMultiplication(){ fun realMultiplication() {
RealMatrixContext { RealMatrixContext {
matrix1 dot matrix2 matrix1 dot matrix2
} }

View File

@ -1,25 +0,0 @@
package kscience.kmath.benchmarks
import kscience.kmath.structures.NDField
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State
import org.openjdk.jmh.infra.Blackhole
import kotlin.random.Random
@State(Scope.Benchmark)
class LargeNDBenchmark {
val arraySize = 10000
val RANDOM = Random(222)
val src1 = DoubleArray(arraySize) { RANDOM.nextDouble() }
val src2 = DoubleArray(arraySize) { RANDOM.nextDouble() }
val field = NDField.real(arraySize)
val kmathArray1 = field.produce { (a) -> src1[a] }
val kmathArray2 = field.produce { (a) -> src2[a] }
@Benchmark
fun test10000(bh: Blackhole) {
bh.consume(field.add(kmathArray1, kmathArray2))
}
}

View File

@ -5,10 +5,8 @@ import kotlinx.benchmark.Benchmark
import kscience.kmath.commons.linear.CMMatrixContext import kscience.kmath.commons.linear.CMMatrixContext
import kscience.kmath.commons.linear.CMMatrixContext.dot import kscience.kmath.commons.linear.CMMatrixContext.dot
import kscience.kmath.commons.linear.inverse import kscience.kmath.commons.linear.inverse
import kscience.kmath.commons.linear.toCM
import kscience.kmath.ejml.EjmlMatrixContext import kscience.kmath.ejml.EjmlMatrixContext
import kscience.kmath.ejml.inverse import kscience.kmath.ejml.inverse
import kscience.kmath.ejml.toEjml
import kscience.kmath.operations.invoke import kscience.kmath.operations.invoke
import kscience.kmath.structures.Matrix import kscience.kmath.structures.Matrix
import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Scope
@ -35,16 +33,14 @@ class LinearAlgebraBenchmark {
@Benchmark @Benchmark
fun cmLUPInversion() { fun cmLUPInversion() {
CMMatrixContext { CMMatrixContext {
val cm = matrix.toCM() //avoid overhead on conversion inverse(matrix)
inverse(cm)
} }
} }
@Benchmark @Benchmark
fun ejmlInverse() { fun ejmlInverse() {
EjmlMatrixContext { EjmlMatrixContext {
val km = matrix.toEjml() //avoid overhead on conversion inverse(matrix)
inverse(km)
} }
} }
} }

View File

@ -95,8 +95,9 @@ public open class FunctionalExpressionRing<T, A : Ring<T>>(
super<FunctionalExpressionSpace>.binaryOperationFunction(operation) super<FunctionalExpressionSpace>.binaryOperationFunction(operation)
} }
public open class FunctionalExpressionField<T, A : Field<T>>(algebra: A) : public open class FunctionalExpressionField<T, A : Field<T>>(
FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>> { algebra: A,
) : FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>> {
/** /**
* Builds an Expression of division an expression by another one. * Builds an Expression of division an expression by another one.
*/ */

View File

@ -224,6 +224,7 @@ public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext
): Matrix<T> = solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum), bufferFactory, checkSingular) ): Matrix<T> = solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum), bufferFactory, checkSingular)
@OptIn(UnstableKMathAPI::class)
public fun RealMatrixContext.solveWithLUP(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> { public fun RealMatrixContext.solveWithLUP(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> {
// Use existing decomposition if it is provided by matrix // Use existing decomposition if it is provided by matrix
val bufferFactory: MutableBufferFactory<Double> = MutableBuffer.Companion::real val bufferFactory: MutableBufferFactory<Double> = MutableBuffer.Companion::real

View File

@ -3,6 +3,7 @@ package kscience.kmath.operations
import kscience.kmath.memory.MemoryReader import kscience.kmath.memory.MemoryReader
import kscience.kmath.memory.MemorySpec import kscience.kmath.memory.MemorySpec
import kscience.kmath.memory.MemoryWriter import kscience.kmath.memory.MemoryWriter
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.Buffer import kscience.kmath.structures.Buffer
import kscience.kmath.structures.MemoryBuffer import kscience.kmath.structures.MemoryBuffer
import kscience.kmath.structures.MutableBuffer import kscience.kmath.structures.MutableBuffer
@ -41,6 +42,7 @@ private val PI_DIV_2 = Complex(PI / 2, 0)
/** /**
* A field of [Complex]. * A field of [Complex].
*/ */
@OptIn(UnstableKMathAPI::class)
public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, RingWithNumbers<Complex> { public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, RingWithNumbers<Complex> {
override val zero: Complex = 0.0.toComplex() override val zero: Complex = 0.0.toComplex()
override val one: Complex = 1.0.toComplex() override val one: Complex = 1.0.toComplex()

View File

@ -11,7 +11,7 @@ public typealias RealNDElement = BufferedNDFieldElement<Double, RealField>
public class RealNDField(override val shape: IntArray) : public class RealNDField(override val shape: IntArray) :
BufferedNDField<Double, RealField>, BufferedNDField<Double, RealField>,
ExtendedNDField<Double, RealField, NDBuffer<Double>>, ExtendedNDField<Double, RealField, NDBuffer<Double>>,
RingWithNumbers<NDBuffer<Double>>{ RingWithNumbers<NDBuffer<Double>> {
override val strides: Strides = DefaultStrides(shape) override val strides: Strides = DefaultStrides(shape)
@ -24,35 +24,31 @@ public class RealNDField(override val shape: IntArray) :
return produce { d } return produce { d }
} }
private inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer<Double> = @Suppress("OVERRIDE_BY_INLINE")
RealBuffer(DoubleArray(size) { initializer(it) }) override inline fun map(
/**
* Inline transform an NDStructure to
*/
override fun map(
arg: NDBuffer<Double>, arg: NDBuffer<Double>,
transform: RealField.(Double) -> Double transform: RealField.(Double) -> Double,
): RealNDElement { ): RealNDElement {
check(arg) check(arg)
val array = buildBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) } val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) }
return BufferedNDFieldElement(this, array) return BufferedNDFieldElement(this, array)
} }
override fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement { @Suppress("OVERRIDE_BY_INLINE")
val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
return BufferedNDFieldElement(this, array) return BufferedNDFieldElement(this, array)
} }
override fun mapIndexed( @Suppress("OVERRIDE_BY_INLINE")
override inline fun mapIndexed(
arg: NDBuffer<Double>, arg: NDBuffer<Double>,
transform: RealField.(index: IntArray, Double) -> Double transform: RealField.(index: IntArray, Double) -> Double,
): RealNDElement { ): RealNDElement {
check(arg) check(arg)
return BufferedNDFieldElement( return BufferedNDFieldElement(
this, this,
buildBuffer(arg.strides.linearSize) { offset -> RealBuffer(arg.strides.linearSize) { offset ->
elementContext.transform( elementContext.transform(
arg.strides.index(offset), arg.strides.index(offset),
arg.buffer[offset] arg.buffer[offset]
@ -60,16 +56,17 @@ public class RealNDField(override val shape: IntArray) :
}) })
} }
override fun combine( @Suppress("OVERRIDE_BY_INLINE")
override inline fun combine(
a: NDBuffer<Double>, a: NDBuffer<Double>,
b: NDBuffer<Double>, b: NDBuffer<Double>,
transform: RealField.(Double, Double) -> Double transform: RealField.(Double, Double) -> Double,
): RealNDElement { ): RealNDElement {
check(a, b) check(a, b)
return BufferedNDFieldElement( val buffer = RealBuffer(strides.linearSize) { offset ->
this, elementContext.transform(a.buffer[offset], b.buffer[offset])
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) } }
) return BufferedNDFieldElement(this, buffer)
} }
override fun NDBuffer<Double>.toElement(): FieldElement<NDBuffer<Double>, *, out BufferedNDField<Double, RealField>> = override fun NDBuffer<Double>.toElement(): FieldElement<NDBuffer<Double>, *, out BufferedNDField<Double, RealField>> =

View File

@ -7,6 +7,7 @@ import kscience.kmath.structures.as2D
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class MatrixTest { class MatrixTest {
@Test @Test
fun testTranspose() { fun testTranspose() {

View File

@ -8,6 +8,7 @@ import kotlin.math.pow
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class NumberNDFieldTest { class NumberNDFieldTest {
val array1: RealNDElement = real2D(3, 3) { i, j -> (i + j).toDouble() } val array1: RealNDElement = real2D(3, 3) { i, j -> (i + j).toDouble() }
val array2: RealNDElement = real2D(3, 3) { i, j -> (i - j).toDouble() } val array2: RealNDElement = real2D(3, 3) { i, j -> (i - j).toDouble() }

View File

@ -6,6 +6,7 @@ import kscience.kmath.dimensions.DMatrixContext
import kscience.kmath.dimensions.one import kscience.kmath.dimensions.one
import kotlin.test.Test import kotlin.test.Test
@Suppress("UNUSED_VARIABLE")
internal class DMatrixContextTest { internal class DMatrixContextTest {
@Test @Test
fun testDimensionSafeMatrix() { fun testDimensionSafeMatrix() {