forked from kscience/kmath
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-koma")
|
||||
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"
|
||||
//jmh project(':kmath-core')
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ open class NDFieldBenchmark {
|
||||
val dim = 1000
|
||||
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 genericField = NDField.buffered(intArrayOf(dim, dim), RealField)
|
||||
val lazyNDField = NDField.lazy(intArrayOf(dim, dim), RealField)
|
||||
|
@ -1,15 +1,13 @@
|
||||
package scientifik.kmath.structures
|
||||
|
||||
import scientifik.kmath.operations.Complex
|
||||
import scientifik.kmath.operations.toComplex
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
fun main() {
|
||||
val dim = 1000
|
||||
val n = 1000
|
||||
|
||||
val realField = NDField.real(intArrayOf(dim, dim))
|
||||
val complexField = NDField.complex(intArrayOf(dim, dim))
|
||||
val realField = NDField.real(dim, dim)
|
||||
val complexField = NDField.complex(dim, dim)
|
||||
|
||||
|
||||
val realTime = measureTimeMillis {
|
||||
|
@ -9,9 +9,9 @@ fun main(args: Array<String>) {
|
||||
val n = 1000
|
||||
|
||||
// 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
|
||||
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.
|
||||
val genericField = NDField.buffered(intArrayOf(dim, dim), RealField)
|
||||
|
||||
|
@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
|
||||
buildscript {
|
||||
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 atomicfuVersion: String by rootProject.extra("0.12.1")
|
||||
|
||||
|
@ -12,6 +12,7 @@ kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api(project(":kmath-memory"))
|
||||
api(kotlin("stdlib"))
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
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.*
|
||||
|
||||
/**
|
||||
@ -67,7 +73,25 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Compl
|
||||
|
||||
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 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.ComplexField
|
||||
import scientifik.kmath.operations.FieldElement
|
||||
import scientifik.kmath.operations.complex
|
||||
|
||||
typealias ComplexNDElement = BufferedNDFieldElement<Complex, ComplexField>
|
||||
|
||||
@ -129,3 +130,5 @@ operator fun ComplexNDElement.plus(arg: Double) =
|
||||
|
||||
operator fun ComplexNDElement.minus(arg: Double) =
|
||||
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.Ring
|
||||
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
|
||||
*/
|
||||
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
|
||||
@ -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.
|
||||
*/
|
||||
@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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
||||
* Create a optimized NDArray of doubles
|
||||
*/
|
||||
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 }) =
|
||||
@ -55,7 +55,7 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
||||
field: F,
|
||||
noinline initializer: F.(IntArray) -> T
|
||||
): BufferedNDFieldElement<T, F> {
|
||||
val ndField = NDField.auto(shape, field)
|
||||
val ndField = NDField.auto(field, *shape)
|
||||
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
|
||||
|
||||
import org.junit.Test
|
||||
import scientifik.kmath.operations.Complex
|
||||
import scientifik.kmath.operations.complex
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ComplexBufferSpecTest {
|
||||
@Test
|
||||
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])
|
||||
}
|
||||
}
|
@ -63,7 +63,7 @@ class NumberNDFieldTest {
|
||||
|
||||
@Test
|
||||
fun testInternalContext() {
|
||||
NDField.real(array1.shape).run {
|
||||
NDField.real(*array1.shape).run {
|
||||
with(L2Norm) {
|
||||
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
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.produce
|
||||
import kotlinx.coroutines.isActive
|
||||
@ -12,6 +13,7 @@ import scientifik.kmath.structures.BufferFactory
|
||||
/**
|
||||
* A processor that collects incoming elements into fixed size buffers
|
||||
*/
|
||||
@ExperimentalCoroutinesApi
|
||||
class JoinProcessor<T>(
|
||||
scope: CoroutineScope,
|
||||
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>) =
|
||||
JoinProcessor<T>(this, chunkSize, bufferFactory).also { connect(it) }
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
inline fun <reified T : Any> Producer<T>.chunked(chunkSize: Int) =
|
||||
JoinProcessor<T>(this, chunkSize, Buffer.Companion::auto).also { connect(it) }
|
||||
|
||||
|
@ -15,10 +15,11 @@ pluginManagement {
|
||||
}
|
||||
}
|
||||
|
||||
//enableFeaturePreview("GRADLE_METADATA")
|
||||
enableFeaturePreview("GRADLE_METADATA")
|
||||
|
||||
rootProject.name = "kmath"
|
||||
include(
|
||||
":kmath-memory",
|
||||
":kmath-core",
|
||||
// ":kmath-io",
|
||||
":kmath-coroutines",
|
||||
|
Loading…
Reference in New Issue
Block a user