Memory on JVM and JS
This commit is contained in:
parent
472dfd7b88
commit
25e8e03494
@ -16,6 +16,7 @@ dependencies {
|
|||||||
implementation project(":kmath-commons")
|
implementation project(":kmath-commons")
|
||||||
implementation project(":kmath-koma")
|
implementation project(":kmath-koma")
|
||||||
implementation group: "com.kyonifer", name:"koma-core-ejml", version: "0.12"
|
implementation group: "com.kyonifer", name:"koma-core-ejml", version: "0.12"
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:0.1.5"
|
||||||
//compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
//compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
//jmh project(':kmath-core')
|
//jmh project(':kmath-core')
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ open class NDFieldBenchmark {
|
|||||||
val dim = 1000
|
val dim = 1000
|
||||||
val n = 100
|
val n = 100
|
||||||
|
|
||||||
val bufferedField = NDField.auto(intArrayOf(dim, dim), RealField)
|
val bufferedField = NDField.auto(RealField, intArrayOf(dim, dim))
|
||||||
val specializedField = NDField.real(intArrayOf(dim, dim))
|
val specializedField = NDField.real(intArrayOf(dim, dim))
|
||||||
val genericField = NDField.buffered(intArrayOf(dim, dim), RealField)
|
val genericField = NDField.buffered(intArrayOf(dim, dim), RealField)
|
||||||
val lazyNDField = NDField.lazy(intArrayOf(dim, dim), RealField)
|
val lazyNDField = NDField.lazy(intArrayOf(dim, dim), RealField)
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
import scientifik.kmath.operations.Complex
|
|
||||||
import scientifik.kmath.operations.toComplex
|
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val dim = 1000
|
val dim = 1000
|
||||||
val n = 1000
|
val n = 1000
|
||||||
|
|
||||||
val realField = NDField.real(intArrayOf(dim, dim))
|
val realField = NDField.real(dim, dim)
|
||||||
val complexField = NDField.complex(intArrayOf(dim, dim))
|
val complexField = NDField.complex(dim, dim)
|
||||||
|
|
||||||
|
|
||||||
val realTime = measureTimeMillis {
|
val realTime = measureTimeMillis {
|
||||||
|
@ -9,9 +9,9 @@ fun main(args: Array<String>) {
|
|||||||
val n = 1000
|
val n = 1000
|
||||||
|
|
||||||
// automatically build context most suited for given type.
|
// automatically build context most suited for given type.
|
||||||
val autoField = NDField.auto(intArrayOf(dim, dim), RealField)
|
val autoField = NDField.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 = NDField.real(intArrayOf(dim, dim))
|
val specializedField = NDField.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.
|
||||||
val genericField = NDField.buffered(intArrayOf(dim, dim), RealField)
|
val genericField = NDField.buffered(intArrayOf(dim, dim), RealField)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
|||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
val kotlinVersion: String by rootProject.extra("1.3.21")
|
val kotlinVersion: String by rootProject.extra("1.3.21")
|
||||||
val ioVersion: String by rootProject.extra("0.1.4")
|
val ioVersion: String by rootProject.extra("0.1.5")
|
||||||
val coroutinesVersion: String by rootProject.extra("1.1.1")
|
val coroutinesVersion: String by rootProject.extra("1.1.1")
|
||||||
val atomicfuVersion: String by rootProject.extra("0.12.1")
|
val atomicfuVersion: String by rootProject.extra("0.12.1")
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api(project(":kmath-memory"))
|
||||||
api(kotlin("stdlib"))
|
api(kotlin("stdlib"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
package scientifik.kmath.operations
|
package scientifik.kmath.operations
|
||||||
|
|
||||||
|
import scientifik.kmath.structures.Buffer
|
||||||
|
import scientifik.kmath.structures.MutableBuffer
|
||||||
|
import scientifik.kmath.structures.ObjectBuffer
|
||||||
|
import scientifik.memory.MemoryReader
|
||||||
|
import scientifik.memory.MemorySpec
|
||||||
|
import scientifik.memory.MemoryWriter
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,7 +73,25 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Compl
|
|||||||
|
|
||||||
val theta: Double get() = atan(im / re)
|
val theta: Double get() = atan(im / re)
|
||||||
|
|
||||||
companion object
|
companion object : MemorySpec<Complex> {
|
||||||
|
override val objectSize: Int = 16
|
||||||
|
|
||||||
|
override fun MemoryReader.read(offset: Int): Complex =
|
||||||
|
Complex(readDouble(offset), readDouble(offset + 8))
|
||||||
|
|
||||||
|
override fun MemoryWriter.write(offset: Int, value: Complex) {
|
||||||
|
writeDouble(offset, value.re)
|
||||||
|
writeDouble(offset + 8, value.im)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Double.toComplex() = Complex(this, 0.0)
|
fun Double.toComplex() = Complex(this, 0.0)
|
||||||
|
|
||||||
|
fun Buffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer<Complex> {
|
||||||
|
return ObjectBuffer.create(Complex, size, init)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MutableBuffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer<Complex> {
|
||||||
|
return ObjectBuffer.create(Complex, size, init)
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package scientifik.kmath.structures
|
|||||||
import scientifik.kmath.operations.Complex
|
import scientifik.kmath.operations.Complex
|
||||||
import scientifik.kmath.operations.ComplexField
|
import scientifik.kmath.operations.ComplexField
|
||||||
import scientifik.kmath.operations.FieldElement
|
import scientifik.kmath.operations.FieldElement
|
||||||
|
import scientifik.kmath.operations.complex
|
||||||
|
|
||||||
typealias ComplexNDElement = BufferedNDFieldElement<Complex, ComplexField>
|
typealias ComplexNDElement = BufferedNDFieldElement<Complex, ComplexField>
|
||||||
|
|
||||||
@ -128,4 +129,6 @@ operator fun ComplexNDElement.plus(arg: Double) =
|
|||||||
map { it + arg }
|
map { it + arg }
|
||||||
|
|
||||||
operator fun ComplexNDElement.minus(arg: Double) =
|
operator fun ComplexNDElement.minus(arg: Double) =
|
||||||
map { it - arg }
|
map { it - arg }
|
||||||
|
|
||||||
|
fun NDField.Companion.complex(vararg shape: Int) = ComplexNDField(shape)
|
@ -3,6 +3,7 @@ package scientifik.kmath.structures
|
|||||||
import scientifik.kmath.operations.Field
|
import scientifik.kmath.operations.Field
|
||||||
import scientifik.kmath.operations.Ring
|
import scientifik.kmath.operations.Ring
|
||||||
import scientifik.kmath.operations.Space
|
import scientifik.kmath.operations.Space
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,7 +119,7 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F,
|
|||||||
/**
|
/**
|
||||||
* Create a nd-field for [Double] values or pull it from cache if it was created previously
|
* Create a nd-field for [Double] values or pull it from cache if it was created previously
|
||||||
*/
|
*/
|
||||||
fun real(shape: IntArray) = realNDFieldCache.getOrPut(shape){RealNDField(shape)}
|
fun real(vararg shape: Int) = realNDFieldCache.getOrPut(shape) { RealNDField(shape) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a nd-field with boxing generic buffer
|
* Create a nd-field with boxing generic buffer
|
||||||
@ -134,9 +135,9 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F,
|
|||||||
* Create a most suitable implementation for nd-field using reified class.
|
* Create a most suitable implementation for nd-field using reified class.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
inline fun <reified T : Any, F : Field<T>> auto(shape: IntArray, field: F): BufferedNDField<T, F> =
|
inline fun <reified T : Any, F : Field<T>> auto(field: F, vararg shape: Int): BufferedNDField<T, F> =
|
||||||
when {
|
when {
|
||||||
T::class == Double::class -> real(shape) as BufferedNDField<T, F>
|
T::class == Double::class -> real(*shape) as BufferedNDField<T, F>
|
||||||
else -> BoxingNDField(shape, field, Buffer.Companion::auto)
|
else -> BoxingNDField(shape, field, Buffer.Companion::auto)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
|||||||
* Create a optimized NDArray of doubles
|
* Create a optimized NDArray of doubles
|
||||||
*/
|
*/
|
||||||
fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }) =
|
fun real(shape: IntArray, initializer: RealField.(IntArray) -> Double = { 0.0 }) =
|
||||||
NDField.real(shape).produce(initializer)
|
NDField.real(*shape).produce(initializer)
|
||||||
|
|
||||||
|
|
||||||
fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }) =
|
fun real1D(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }) =
|
||||||
@ -55,7 +55,7 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
|||||||
field: F,
|
field: F,
|
||||||
noinline initializer: F.(IntArray) -> T
|
noinline initializer: F.(IntArray) -> T
|
||||||
): BufferedNDFieldElement<T, F> {
|
): BufferedNDFieldElement<T, F> {
|
||||||
val ndField = NDField.auto(shape, field)
|
val ndField = NDField.auto(field, *shape)
|
||||||
return BufferedNDFieldElement(ndField, ndField.produce(initializer).buffer)
|
return BufferedNDFieldElement(ndField, ndField.produce(initializer).buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.memory.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A non-boxing buffer based on [ByteBuffer] storage
|
||||||
|
*/
|
||||||
|
open class ObjectBuffer<T : Any>(protected val memory: Memory, protected val spec: MemorySpec<T>) : Buffer<T> {
|
||||||
|
|
||||||
|
override val size: Int get() = memory.size / spec.objectSize
|
||||||
|
|
||||||
|
private val reader = memory.reader()
|
||||||
|
|
||||||
|
override fun get(index: Int): T = reader.read(spec, spec.objectSize * index)
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<T> = (0 until size).asSequence().map { get(it) }.iterator()
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <T : Any> create(spec: MemorySpec<T>, size: Int) =
|
||||||
|
ObjectBuffer(Memory.allocate(size * spec.objectSize), spec)
|
||||||
|
|
||||||
|
inline fun <T : Any> create(
|
||||||
|
spec: MemorySpec<T>,
|
||||||
|
size: Int,
|
||||||
|
crossinline initializer: (Int) -> T
|
||||||
|
): ObjectBuffer<T> =
|
||||||
|
MutableObjectBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer ->
|
||||||
|
(0 until size).forEach {
|
||||||
|
buffer[it] = initializer(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MutableObjectBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) : ObjectBuffer<T>(memory, spec),
|
||||||
|
MutableBuffer<T> {
|
||||||
|
|
||||||
|
private val writer = memory.writer()
|
||||||
|
|
||||||
|
override fun set(index: Int, value: T) = writer.write(spec, spec.objectSize * index, value)
|
||||||
|
|
||||||
|
override fun copy(): MutableBuffer<T> = MutableObjectBuffer(memory.copy(), spec)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,14 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
import scientifik.kmath.operations.Complex
|
import scientifik.kmath.operations.Complex
|
||||||
|
import scientifik.kmath.operations.complex
|
||||||
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class ComplexBufferSpecTest {
|
class ComplexBufferSpecTest {
|
||||||
@Test
|
@Test
|
||||||
fun testComplexBuffer() {
|
fun testComplexBuffer() {
|
||||||
val buffer = MutableBuffer.complex(20){Complex(it.toDouble(), -it.toDouble())}
|
val buffer = Buffer.complex(20) { Complex(it.toDouble(), -it.toDouble()) }
|
||||||
assertEquals(Complex(5.0, -5.0), buffer[5])
|
assertEquals(Complex(5.0, -5.0), buffer[5])
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -63,7 +63,7 @@ class NumberNDFieldTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInternalContext() {
|
fun testInternalContext() {
|
||||||
NDField.real(array1.shape).run {
|
NDField.real(*array1.shape).run {
|
||||||
with(L2Norm) {
|
with(L2Norm) {
|
||||||
1 + norm(array1) + exp(array2)
|
1 + norm(array1) + exp(array2)
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A specification for serialization and deserialization objects to buffer (at current buffer position)
|
|
||||||
*/
|
|
||||||
interface BufferSpec<T : Any> {
|
|
||||||
/**
|
|
||||||
* Read an object from buffer in current position
|
|
||||||
*/
|
|
||||||
fun ByteBuffer.readObject(): T
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write object to [ByteBuffer] in current buffer position
|
|
||||||
*/
|
|
||||||
fun ByteBuffer.writeObject(value: T)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [BufferSpec] with fixed unit size. Allows storage of any object without boxing.
|
|
||||||
*/
|
|
||||||
interface FixedSizeBufferSpec<T : Any> : BufferSpec<T> {
|
|
||||||
val unitSize: Int
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read an object from buffer in given index (not buffer position
|
|
||||||
*/
|
|
||||||
fun ByteBuffer.readObject(index: Int): T {
|
|
||||||
position(index * unitSize)
|
|
||||||
return readObject()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Put an object in given index
|
|
||||||
*/
|
|
||||||
fun ByteBuffer.writeObject(index: Int, obj: T) {
|
|
||||||
position(index * unitSize)
|
|
||||||
writeObject(obj)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import scientifik.kmath.operations.Complex
|
|
||||||
import scientifik.kmath.operations.ComplexField
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A serialization specification for complex numbers
|
|
||||||
*/
|
|
||||||
object ComplexBufferSpec : FixedSizeBufferSpec<Complex> {
|
|
||||||
|
|
||||||
override val unitSize: Int = 16
|
|
||||||
|
|
||||||
override fun ByteBuffer.readObject(): Complex {
|
|
||||||
val re = double
|
|
||||||
val im = double
|
|
||||||
return Complex(re, im)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun ByteBuffer.writeObject(value: Complex) {
|
|
||||||
putDouble(value.re)
|
|
||||||
putDouble(value.im)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a read-only/mutable buffer which ignores boxing
|
|
||||||
*/
|
|
||||||
fun Buffer.Companion.complex(size: Int): Buffer<Complex> =
|
|
||||||
ObjectBuffer.create(ComplexBufferSpec, size)
|
|
||||||
|
|
||||||
inline fun Buffer.Companion.complex(size: Int, crossinline initializer: (Int) -> Complex): Buffer<Complex> =
|
|
||||||
ObjectBuffer.create(ComplexBufferSpec, size, initializer)
|
|
||||||
|
|
||||||
fun MutableBuffer.Companion.complex(size: Int) =
|
|
||||||
ObjectBuffer.create(ComplexBufferSpec, size)
|
|
||||||
|
|
||||||
inline fun MutableBuffer.Companion.complex(size: Int, crossinline initializer: (Int) -> Complex) =
|
|
||||||
ObjectBuffer.create(ComplexBufferSpec, size, initializer)
|
|
||||||
|
|
||||||
fun NDField.Companion.complex(shape: IntArray) = ComplexNDField(shape)
|
|
||||||
|
|
||||||
fun NDElement.Companion.complex(shape: IntArray, initializer: ComplexField.(IntArray) -> Complex) =
|
|
||||||
NDField.complex(shape).produce(initializer)
|
|
||||||
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A non-boxing buffer based on [ByteBuffer] storage
|
|
||||||
*/
|
|
||||||
class ObjectBuffer<T : Any>(private val buffer: ByteBuffer, private val spec: FixedSizeBufferSpec<T>) :
|
|
||||||
MutableBuffer<T> {
|
|
||||||
override val size: Int
|
|
||||||
get() = buffer.limit() / spec.unitSize
|
|
||||||
|
|
||||||
override fun get(index: Int): T = with(spec) { buffer.readObject(index) }
|
|
||||||
|
|
||||||
override fun iterator(): Iterator<T> = (0 until size).asSequence().map { get(it) }.iterator()
|
|
||||||
|
|
||||||
override fun set(index: Int, value: T) = with(spec) { buffer.writeObject(index, value) }
|
|
||||||
|
|
||||||
override fun copy(): MutableBuffer<T> {
|
|
||||||
val dup = buffer.duplicate()
|
|
||||||
val copy = ByteBuffer.allocate(dup.capacity())
|
|
||||||
dup.rewind()
|
|
||||||
copy.put(dup)
|
|
||||||
copy.flip()
|
|
||||||
return ObjectBuffer(copy, spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun <T : Any> create(spec: FixedSizeBufferSpec<T>, size: Int) =
|
|
||||||
ObjectBuffer(ByteBuffer.allocate(size * spec.unitSize), spec)
|
|
||||||
|
|
||||||
inline fun <T : Any> create(spec: FixedSizeBufferSpec<T>, size: Int, crossinline initializer: (Int) -> T) =
|
|
||||||
ObjectBuffer(ByteBuffer.allocate(size * spec.unitSize), spec).also { buffer ->
|
|
||||||
(0 until size).forEach {
|
|
||||||
buffer[it] = initializer(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import scientifik.kmath.operations.Real
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
object RealBufferSpec : FixedSizeBufferSpec<Real> {
|
|
||||||
override val unitSize: Int = 8
|
|
||||||
|
|
||||||
override fun ByteBuffer.readObject(): Real = Real(double)
|
|
||||||
|
|
||||||
override fun ByteBuffer.writeObject(value: Real) {
|
|
||||||
putDouble(value.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object DoubleBufferSpec : FixedSizeBufferSpec<Double> {
|
|
||||||
override val unitSize: Int = 8
|
|
||||||
|
|
||||||
override fun ByteBuffer.readObject() = double
|
|
||||||
|
|
||||||
override fun ByteBuffer.writeObject(value: Double) {
|
|
||||||
putDouble(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Double.Companion.createBuffer(size: Int) = ObjectBuffer.create(DoubleBufferSpec, size)
|
|
||||||
fun Real.Companion.createBuffer(size: Int) = ObjectBuffer.create(RealBufferSpec, size)
|
|
50
kmath-memory/build.gradle.kts
Normal file
50
kmath-memory/build.gradle.kts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("multiplatform")
|
||||||
|
}
|
||||||
|
|
||||||
|
val ioVersion: String by rootProject.extra
|
||||||
|
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
js()
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
api(kotlin("stdlib"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val commonTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test-common"))
|
||||||
|
implementation(kotlin("test-annotations-common"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jvmMain by getting {
|
||||||
|
dependencies {
|
||||||
|
api(kotlin("stdlib-jdk8"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jvmTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
implementation(kotlin("test-junit"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jsMain by getting {
|
||||||
|
dependencies {
|
||||||
|
api(kotlin("stdlib-js"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jsTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test-js"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mingwMain {
|
||||||
|
// }
|
||||||
|
// mingwTest {
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package scientifik.memory
|
||||||
|
|
||||||
|
interface Memory {
|
||||||
|
val size: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a projection of this memory (it reflects the changes in the parent memory block)
|
||||||
|
*/
|
||||||
|
fun view(offset: Int, length: Int): Memory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a copy of this memory, which does not know anything about this memory
|
||||||
|
*/
|
||||||
|
fun copy(): Memory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and possibly register a new reader
|
||||||
|
*/
|
||||||
|
fun reader(): MemoryReader
|
||||||
|
|
||||||
|
fun writer(): MemoryWriter
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MemoryReader {
|
||||||
|
val memory: Memory
|
||||||
|
|
||||||
|
fun readDouble(offset: Int): Double
|
||||||
|
fun readFloat(offset: Int): Float
|
||||||
|
fun readByte(offset: Int): Byte
|
||||||
|
fun readShort(offset: Int): Short
|
||||||
|
fun readInt(offset: Int): Int
|
||||||
|
fun readLong(offset: Int): Long
|
||||||
|
|
||||||
|
fun release()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the memory for read then release the reader
|
||||||
|
*/
|
||||||
|
inline fun Memory.read(block: MemoryReader.() -> Unit) {
|
||||||
|
reader().apply(block).apply { release() }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MemoryWriter {
|
||||||
|
val memory: Memory
|
||||||
|
|
||||||
|
fun writeDouble(offset: Int, value: Double)
|
||||||
|
fun writeFloat(offset: Int, value: Float)
|
||||||
|
fun writeByte(offset: Int, value: Byte)
|
||||||
|
fun writeShort(offset: Int, value: Short)
|
||||||
|
fun writeInt(offset: Int, value: Int)
|
||||||
|
fun writeLong(offset: Int, value: Long)
|
||||||
|
|
||||||
|
fun release()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the memory for write then release the writer
|
||||||
|
*/
|
||||||
|
inline fun Memory.write(block: MemoryWriter.() -> Unit) {
|
||||||
|
writer().apply(block).apply { release() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate the most effective platform-specific memory
|
||||||
|
*/
|
||||||
|
expect fun Memory.Companion.allocate(length: Int): Memory
|
@ -0,0 +1,34 @@
|
|||||||
|
package scientifik.memory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specification to read or write custom objects with fixed size in bytes
|
||||||
|
*/
|
||||||
|
interface MemorySpec<T : Any> {
|
||||||
|
/**
|
||||||
|
* Size of [T] in bytes after serialization
|
||||||
|
*/
|
||||||
|
val objectSize: Int
|
||||||
|
|
||||||
|
fun MemoryReader.read(offset: Int): T
|
||||||
|
fun MemoryWriter.write(offset: Int, value: T)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any> MemoryReader.read(spec: MemorySpec<T>, offset: Int): T = spec.run { read(offset) }
|
||||||
|
fun <T : Any> MemoryWriter.write(spec: MemorySpec<T>, offset: Int, value: T) = spec.run { write(offset, value) }
|
||||||
|
|
||||||
|
inline fun <reified T : Any> MemoryReader.readArray(spec: MemorySpec<T>, offset: Int, size: Int) =
|
||||||
|
Array(size) { i ->
|
||||||
|
spec.run {
|
||||||
|
read(offset + i * objectSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any> MemoryWriter.writeArray(spec: MemorySpec<T>, offset: Int, array: Array<T>) {
|
||||||
|
spec.run {
|
||||||
|
for (i in 0 until array.size) {
|
||||||
|
write(offset + i * objectSize, array[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO It is possible to add elastic MemorySpec with unknown object size
|
@ -0,0 +1,91 @@
|
|||||||
|
package scientifik.memory
|
||||||
|
|
||||||
|
import org.khronos.webgl.ArrayBuffer
|
||||||
|
import org.khronos.webgl.DataView
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate the most effective platform-specific memory
|
||||||
|
*/
|
||||||
|
actual fun Memory.Companion.allocate(length: Int): Memory {
|
||||||
|
val buffer = ArrayBuffer(length)
|
||||||
|
return DataViewMemory(DataView(buffer, 0, length))
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataViewMemory(val view: DataView) : Memory {
|
||||||
|
|
||||||
|
override val size: Int get() = view.byteLength
|
||||||
|
|
||||||
|
override fun view(offset: Int, length: Int): Memory {
|
||||||
|
require(offset >= 0) { "offset shouldn't be negative: $offset" }
|
||||||
|
require(length >= 0) { "length shouldn't be negative: $length" }
|
||||||
|
if (offset + length > size) {
|
||||||
|
throw IndexOutOfBoundsException("offset + length > size: $offset + $length > $size")
|
||||||
|
}
|
||||||
|
return DataViewMemory(DataView(view.buffer, view.byteOffset + offset, length))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun copy(): Memory {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
private val reader = object : MemoryReader {
|
||||||
|
override val memory: Memory get() = this@DataViewMemory
|
||||||
|
|
||||||
|
override fun readDouble(offset: Int): Double = view.getFloat64(offset, false)
|
||||||
|
|
||||||
|
override fun readFloat(offset: Int): Float = view.getFloat32(offset, false)
|
||||||
|
|
||||||
|
override fun readByte(offset: Int): Byte = view.getInt8(offset)
|
||||||
|
|
||||||
|
override fun readShort(offset: Int): Short = view.getInt16(offset, false)
|
||||||
|
|
||||||
|
override fun readInt(offset: Int): Int = view.getInt32(offset, false)
|
||||||
|
|
||||||
|
override fun readLong(offset: Int): Long = (view.getInt32(offset, false).toLong() shl 32) or
|
||||||
|
view.getInt32(offset + 4, false).toLong()
|
||||||
|
|
||||||
|
override fun release() {
|
||||||
|
// does nothing on JS because of GC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reader(): MemoryReader = reader
|
||||||
|
|
||||||
|
private val writer = object : MemoryWriter {
|
||||||
|
override val memory: Memory get() = this@DataViewMemory
|
||||||
|
|
||||||
|
override fun writeDouble(offset: Int, value: Double) {
|
||||||
|
view.setFloat64(offset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeFloat(offset: Int, value: Float) {
|
||||||
|
view.setFloat32(offset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeByte(offset: Int, value: Byte) {
|
||||||
|
view.setInt8(offset, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeShort(offset: Int, value: Short) {
|
||||||
|
view.setUint16(offset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeInt(offset: Int, value: Int) {
|
||||||
|
view.setInt32(offset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeLong(offset: Int, value: Long) {
|
||||||
|
view.setInt32(offset, (value shr 32).toInt(), littleEndian = false)
|
||||||
|
view.setInt32(offset + 4, (value and 0xffffffffL).toInt(), littleEndian = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun release() {
|
||||||
|
//does nothing on JS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writer(): MemoryWriter = writer
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package scientifik.memory
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate the most effective platform-specific memory
|
||||||
|
*/
|
||||||
|
actual fun Memory.Companion.allocate(length: Int): Memory {
|
||||||
|
val buffer = ByteBuffer.allocate(length)
|
||||||
|
return ByteBufferMemory(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByteBufferMemory(
|
||||||
|
val buffer: ByteBuffer,
|
||||||
|
val startOffset: Int = 0,
|
||||||
|
override val size: Int = buffer.limit()
|
||||||
|
) : Memory {
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline fun position(o: Int): Int = startOffset + o
|
||||||
|
|
||||||
|
override fun view(offset: Int, length: Int): Memory {
|
||||||
|
if (offset + length > size) error("Selecting a Memory view outside of memory range")
|
||||||
|
return ByteBufferMemory(buffer, position(offset), length)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun copy(): Memory {
|
||||||
|
val copy = ByteBuffer.allocate(buffer.capacity())
|
||||||
|
buffer.rewind()
|
||||||
|
copy.put(buffer)
|
||||||
|
copy.flip()
|
||||||
|
return ByteBufferMemory(copy)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private val reader = object : MemoryReader {
|
||||||
|
override val memory: Memory get() = this@ByteBufferMemory
|
||||||
|
|
||||||
|
override fun readDouble(offset: Int) = buffer.getDouble(position(offset))
|
||||||
|
|
||||||
|
override fun readFloat(offset: Int) = buffer.getFloat(position(offset))
|
||||||
|
|
||||||
|
override fun readByte(offset: Int) = buffer.get(position(offset))
|
||||||
|
|
||||||
|
override fun readShort(offset: Int) = buffer.getShort(position(offset))
|
||||||
|
|
||||||
|
override fun readInt(offset: Int) = buffer.getInt(position(offset))
|
||||||
|
|
||||||
|
override fun readLong(offset: Int) = buffer.getLong(position(offset))
|
||||||
|
|
||||||
|
override fun release() {
|
||||||
|
//does nothing on JVM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reader(): MemoryReader = reader
|
||||||
|
|
||||||
|
private val writer = object : MemoryWriter {
|
||||||
|
override val memory: Memory get() = this@ByteBufferMemory
|
||||||
|
|
||||||
|
override fun writeDouble(offset: Int, value: Double) {
|
||||||
|
buffer.putDouble(position(offset), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeFloat(offset: Int, value: Float) {
|
||||||
|
buffer.putFloat(position(offset), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeByte(offset: Int, value: Byte) {
|
||||||
|
buffer.put(position(offset), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeShort(offset: Int, value: Short) {
|
||||||
|
buffer.putShort(position(offset), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeInt(offset: Int, value: Int) {
|
||||||
|
buffer.putInt(position(offset), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeLong(offset: Int, value: Long) {
|
||||||
|
buffer.putLong(position(offset), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun release() {
|
||||||
|
//does nothing on JVM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writer(): MemoryWriter = writer
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package scientifik.kmath.sequential
|
package scientifik.kmath.sequential
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.channels.produce
|
import kotlinx.coroutines.channels.produce
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
@ -12,6 +13,7 @@ import scientifik.kmath.structures.BufferFactory
|
|||||||
/**
|
/**
|
||||||
* A processor that collects incoming elements into fixed size buffers
|
* A processor that collects incoming elements into fixed size buffers
|
||||||
*/
|
*/
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
class JoinProcessor<T>(
|
class JoinProcessor<T>(
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
bufferSize: Int,
|
bufferSize: Int,
|
||||||
@ -68,9 +70,11 @@ class SplitProcessor<T>(scope: CoroutineScope) : AbstractProcessor<Buffer<T>, T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
fun <T> Producer<T>.chunked(chunkSize: Int, bufferFactory: BufferFactory<T>) =
|
fun <T> Producer<T>.chunked(chunkSize: Int, bufferFactory: BufferFactory<T>) =
|
||||||
JoinProcessor<T>(this, chunkSize, bufferFactory).also { connect(it) }
|
JoinProcessor<T>(this, chunkSize, bufferFactory).also { connect(it) }
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
inline fun <reified T : Any> Producer<T>.chunked(chunkSize: Int) =
|
inline fun <reified T : Any> Producer<T>.chunked(chunkSize: Int) =
|
||||||
JoinProcessor<T>(this, chunkSize, Buffer.Companion::auto).also { connect(it) }
|
JoinProcessor<T>(this, chunkSize, Buffer.Companion::auto).also { connect(it) }
|
||||||
|
|
||||||
|
@ -15,10 +15,11 @@ pluginManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//enableFeaturePreview("GRADLE_METADATA")
|
enableFeaturePreview("GRADLE_METADATA")
|
||||||
|
|
||||||
rootProject.name = "kmath"
|
rootProject.name = "kmath"
|
||||||
include(
|
include(
|
||||||
|
":kmath-memory",
|
||||||
":kmath-core",
|
":kmath-core",
|
||||||
// ":kmath-io",
|
// ":kmath-io",
|
||||||
":kmath-coroutines",
|
":kmath-coroutines",
|
||||||
|
Loading…
Reference in New Issue
Block a user