Optimize Real NDField

This commit is contained in:
Alexander Nozik 2021-01-23 22:16:27 +03:00
parent 0baec14059
commit 1cb41f4dc2
7 changed files with 77 additions and 92 deletions

View File

@ -4,8 +4,8 @@ import kotlinx.benchmark.Benchmark
import kscience.kmath.commons.linear.CMMatrixContext import kscience.kmath.commons.linear.CMMatrixContext
import kscience.kmath.ejml.EjmlMatrixContext import kscience.kmath.ejml.EjmlMatrixContext
import kscience.kmath.linear.BufferMatrixContext import kscience.kmath.linear.BufferMatrixContext
import kscience.kmath.linear.Matrix
import kscience.kmath.linear.RealMatrixContext import kscience.kmath.linear.RealMatrixContext
import kscience.kmath.nd.Matrix
import kscience.kmath.operations.RealField import kscience.kmath.operations.RealField
import kscience.kmath.operations.invoke import kscience.kmath.operations.invoke
import kscience.kmath.structures.Buffer import kscience.kmath.structures.Buffer

View File

@ -1,11 +1,9 @@
package kscience.kmath.benchmarks package kscience.kmath.benchmarks
import kscience.kmath.nd.AbstractNDBuffer import kscience.kmath.nd.*
import kscience.kmath.nd.NDField
import kscience.kmath.nd.RealNDField
import kscience.kmath.operations.RealField import kscience.kmath.operations.RealField
import kscience.kmath.operations.invoke import kscience.kmath.operations.invoke
import kscience.kmath.structures.* import kscience.kmath.structures.Buffer
import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State import org.openjdk.jmh.annotations.State
@ -14,22 +12,16 @@ import org.openjdk.jmh.annotations.State
internal class NDFieldBenchmark { internal class NDFieldBenchmark {
@Benchmark @Benchmark
fun autoFieldAdd() { fun autoFieldAdd() {
bufferedField { autoField {
var res: AbstractNDBuffer<Double> = one var res: NDStructure<Double> = one
repeat(n) { res += one } repeat(n) { res += one }
} }
} }
@Benchmark
fun autoElementAdd() {
var res = genericField.one
repeat(n) { res += 1.0 }
}
@Benchmark @Benchmark
fun specializedFieldAdd() { fun specializedFieldAdd() {
specializedField { specializedField {
var res: AbstractNDBuffer<Double> = one var res: NDStructure<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
} }
} }
@ -38,16 +30,16 @@ internal class NDFieldBenchmark {
@Benchmark @Benchmark
fun boxingFieldAdd() { fun boxingFieldAdd() {
genericField { genericField {
var res: AbstractNDBuffer<Double> = one var res: NDStructure<Double> = one
repeat(n) { res += one } repeat(n) { res += 1.0 }
} }
} }
companion object { companion object {
const val dim: Int = 1000 const val dim: Int = 1000
const val n: Int = 100 const val n: Int = 100
val bufferedField: BufferedNDField<Double, RealField> = NDField.auto(RealField, dim, dim) val autoField = NDAlgebra.auto(RealField, dim, dim)
val specializedField: RealNDField = NDField.real(dim, dim) val specializedField: RealNDField = NDAlgebra.real(dim, dim)
val genericField: BoxingNDField<Double, RealField> = NDField.boxing(RealField, dim, dim) val genericField = NDAlgebra.field(RealField, Buffer.Companion::boxing, dim, dim)
} }
} }

View File

@ -1,10 +1,8 @@
package kscience.kmath.benchmarks package kscience.kmath.benchmarks
import kscience.kmath.nd.NDField import kscience.kmath.nd.*
import kscience.kmath.nd.RealNDField
import kscience.kmath.operations.RealField import kscience.kmath.operations.RealField
import kscience.kmath.operations.invoke import kscience.kmath.operations.invoke
import kscience.kmath.structures.BufferedNDField
import kscience.kmath.viktor.ViktorNDField import kscience.kmath.viktor.ViktorNDField
import org.jetbrains.bio.viktor.F64Array import org.jetbrains.bio.viktor.F64Array
import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Benchmark
@ -17,14 +15,14 @@ internal class ViktorBenchmark {
final val n: Int = 100 final val n: Int = 100
// automatically build context most suited for given type. // automatically build context most suited for given type.
final val autoField: BufferedNDField<Double, RealField> = NDField.auto(RealField, dim, dim) final val autoField: BufferedNDField<Double, RealField> = NDAlgebra.auto(RealField, dim, dim)
final val realField: RealNDField = NDField.real(dim, dim) final val realField: RealNDField = NDAlgebra.real(dim, dim)
final val viktorField: ViktorNDField = ViktorNDField(intArrayOf(dim, dim)) final val viktorField: ViktorNDField = ViktorNDField(intArrayOf(dim, dim))
@Benchmark @Benchmark
fun automaticFieldAddition() { fun automaticFieldAddition() {
autoField { autoField {
var res = one var res: NDStructure<Double> = one
repeat(n) { res += one } repeat(n) { res += one }
} }
} }

View File

@ -1,10 +1,7 @@
package kscience.kmath.structures package kscience.kmath.structures
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kscience.kmath.nd.NDAlgebra import kscience.kmath.nd.*
import kscience.kmath.nd.NDStructure
import kscience.kmath.nd.field
import kscience.kmath.nd.real
import kscience.kmath.nd4j.Nd4jArrayField import kscience.kmath.nd4j.Nd4jArrayField
import kscience.kmath.operations.RealField import kscience.kmath.operations.RealField
import kscience.kmath.operations.invoke import kscience.kmath.operations.invoke
@ -26,7 +23,7 @@ fun main() {
val n = 1000 val n = 1000
// automatically build context most suited for given type. // automatically build context most suited for given type.
val autoField = NDAlgebra.field(RealField, Buffer.Companion::auto, dim, dim) val autoField = NDAlgebra.auto(RealField, dim, dim)
// specialized nd-field for Double. It works as generic Double field as well // specialized nd-field for Double. It works as generic Double field as well
val specializedField = NDAlgebra.real(dim, dim) val specializedField = NDAlgebra.real(dim, dim)
//A generic boxing field. It should be used for objects, not primitives. //A generic boxing field. It should be used for objects, not primitives.
@ -41,7 +38,7 @@ fun main() {
} }
} }
measureAndPrint("Element addition") { measureAndPrint("Boxing addition") {
boxingField { boxingField {
var res: NDStructure<Double> = one var res: NDStructure<Double> = one
repeat(n) { res += 1.0 } repeat(n) { res += 1.0 }
@ -73,14 +70,4 @@ fun main() {
res.elements().forEach { it.second } res.elements().forEach { it.second }
} }
measureAndPrint("Generic addition") {
//genericField.run(action)
boxingField {
var res: NDStructure<Double> = one
repeat(n) {
res += 1.0 // couldn't avoid using `one` due to resolution ambiguity }
}
}
}
} }

View File

@ -2,6 +2,7 @@ package kscience.kmath.nd
import kscience.kmath.nd.* import kscience.kmath.nd.*
import kscience.kmath.operations.* import kscience.kmath.operations.*
import kscience.kmath.structures.Buffer
import kscience.kmath.structures.BufferFactory import kscience.kmath.structures.BufferFactory
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
@ -118,6 +119,11 @@ public fun <T, A : Field<T>> NDAlgebra.Companion.field(
vararg shape: Int, vararg shape: Int,
): BufferedNDField<T, A> = BufferedNDField(shape, field, bufferFactory) ): BufferedNDField<T, A> = BufferedNDField(shape, field, bufferFactory)
public inline fun <reified T : Any, A : Field<T>> NDAlgebra.Companion.auto(
field: A,
vararg shape: Int,
): BufferedNDField<T, A> = BufferedNDField(shape, field, Buffer.Companion::auto)
public inline fun <T, A : Field<T>, R> A.ndField( public inline fun <T, A : Field<T>, R> A.ndField(
noinline bufferFactory: BufferFactory<T>, noinline bufferFactory: BufferFactory<T>,
vararg shape: Int, vararg shape: Int,

View File

@ -10,7 +10,7 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public open class RealNDField( public class RealNDField(
shape: IntArray, shape: IntArray,
) : BufferedNDField<Double, RealField>(shape, RealField, Buffer.Companion::real), ) : BufferedNDField<Double, RealField>(shape, RealField, Buffer.Companion::real),
RingWithNumbers<NDStructure<Double>>, RingWithNumbers<NDStructure<Double>>,
@ -23,51 +23,52 @@ public open class RealNDField(
val d = value.toDouble() // minimize conversions val d = value.toDouble() // minimize conversions
return produce { d } return produce { d }
} }
//
// @Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
// override inline fun map( override inline fun map(
// arg: AbstractNDBuffer<Double>, arg: NDStructure<Double>,
// transform: RealField.(Double) -> Double, transform: RealField.(Double) -> Double,
// ): RealNDElement { ): NDBuffer<Double> {
// check(arg) val argAsBuffer = arg.ndBuffer
// val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) } val buffer = RealBuffer(strides.linearSize) { offset -> RealField.transform(argAsBuffer.buffer[offset]) }
// return BufferedNDFieldElement(this, array) return NDBuffer(strides, buffer)
// } }
//
// @Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
// override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement { override inline fun produce(initializer: RealField.(IntArray) -> Double): NDBuffer<Double> {
// val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) } val buffer = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
// return BufferedNDFieldElement(this, array) return NDBuffer(strides, buffer)
// } }
//
// @Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
// override inline fun mapIndexed( override inline fun mapIndexed(
// arg: AbstractNDBuffer<Double>, arg: NDStructure<Double>,
// transform: RealField.(index: IntArray, Double) -> Double, transform: RealField.(index: IntArray, Double) -> Double,
// ): RealNDElement { ): NDBuffer<Double> {
// check(arg) val argAsBuffer = arg.ndBuffer
// return BufferedNDFieldElement( return NDBuffer(
// this, strides,
// RealBuffer(arg.strides.linearSize) { offset -> RealBuffer(strides.linearSize) { offset ->
// elementContext.transform( elementContext.transform(
// arg.strides.index(offset), strides.index(offset),
// arg.buffer[offset] argAsBuffer.buffer[offset]
// ) )
// }) })
// } }
//
// @Suppress("OVERRIDE_BY_INLINE") @Suppress("OVERRIDE_BY_INLINE")
// override inline fun combine( override inline fun combine(
// a: AbstractNDBuffer<Double>, a: NDStructure<Double>,
// b: AbstractNDBuffer<Double>, b: NDStructure<Double>,
// transform: RealField.(Double, Double) -> Double, transform: RealField.(Double, Double) -> Double,
// ): RealNDElement { ): NDBuffer<Double> {
// check(a, b) val aBuffer = a.ndBuffer
// val buffer = RealBuffer(strides.linearSize) { offset -> val bBuffer = b.ndBuffer
// elementContext.transform(a.buffer[offset], b.buffer[offset]) val buffer = RealBuffer(strides.linearSize) { offset ->
// } elementContext.transform(aBuffer.buffer[offset], bBuffer.buffer[offset])
// return BufferedNDFieldElement(this, buffer) }
// } return NDBuffer(strides, buffer)
}
override fun power(arg: NDStructure<Double>, pow: Number): NDBuffer<Double> = map(arg) { power(it, pow) } override fun power(arg: NDStructure<Double>, pow: Number): NDBuffer<Double> = map(arg) { power(it, pow) }

View File

@ -1,5 +1,6 @@
package kscience.kmath.nd package kscience.kmath.nd
import kscience.kmath.linear.Matrix
import kscience.kmath.structures.Buffer import kscience.kmath.structures.Buffer
import kscience.kmath.structures.VirtualBuffer import kscience.kmath.structures.VirtualBuffer
@ -57,9 +58,9 @@ public interface Structure2D<T> : NDStructure<T> {
rows: Int, rows: Int,
columns: Int, columns: Int,
crossinline init: (i: Int, j: Int) -> Double, crossinline init: (i: Int, j: Int) -> Double,
): NDBuffer<Double> = NDAlgebra.real(rows, columns).produceInline { (i, j) -> ): Matrix<Double> = NDAlgebra.real(rows, columns).produceInline { (i, j) ->
init(i, j) init(i, j)
} }.as2D()
} }
} }