From 97a38ba0a97d2e23ce52a1ed3c11d64bffd416a2 Mon Sep 17 00:00:00 2001 From: Elena Lepilkina Date: Mon, 7 Jun 2021 16:52:45 +0300 Subject: [PATCH] [K/N] Added benchmarks for KT-46482 and KT-46425 --- .../performance/ring/src/main/kotlin/main.kt | 4 +- .../org/jetbrains/ring/BunnymarkBenchmark.kt | 136 ++++++++++++++++++ .../org/jetbrains/ring/ChainableBenchmark.kt | 59 ++++++++ 3 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 kotlin-native/performance/ring/src/main/kotlin/org/jetbrains/ring/BunnymarkBenchmark.kt create mode 100644 kotlin-native/performance/ring/src/main/kotlin/org/jetbrains/ring/ChainableBenchmark.kt diff --git a/kotlin-native/performance/ring/src/main/kotlin/main.kt b/kotlin-native/performance/ring/src/main/kotlin/main.kt index 18547b464e5..19c7e6f9d87 100644 --- a/kotlin-native/performance/ring/src/main/kotlin/main.kt +++ b/kotlin-native/performance/ring/src/main/kotlin/main.kt @@ -210,7 +210,9 @@ class RingLauncher : Launcher() { "Casts.interfaceCast" to BenchmarkEntryWithInit.create(::CastsBenchmark, { interfaceCast() }), "LocalObjects.localArray" to BenchmarkEntryWithInit.create(::LocalObjectsBenchmark, { localArray() }), "LinkedListWithAtomicsBenchmark" to BenchmarkEntryWithInit.create(::LinkedListWithAtomicsBenchmark, { ensureNext() }), - "Inheritance.baseCalls" to BenchmarkEntryWithInit.create(::InheritanceBenchmark, { baseCalls() }) + "Inheritance.baseCalls" to BenchmarkEntryWithInit.create(::InheritanceBenchmark, { baseCalls() }), + "ChainableBenchmark.testChainable" to BenchmarkEntryWithInit.create(::ChainableBenchmark, { testChainable() }), + "BunnymarkBenchmark.testBunnymark" to BenchmarkEntryWithInit.create(::BunnymarkBenchmark, { testBunnymark() }) ) ) } diff --git a/kotlin-native/performance/ring/src/main/kotlin/org/jetbrains/ring/BunnymarkBenchmark.kt b/kotlin-native/performance/ring/src/main/kotlin/org/jetbrains/ring/BunnymarkBenchmark.kt new file mode 100644 index 00000000000..05a3a842830 --- /dev/null +++ b/kotlin-native/performance/ring/src/main/kotlin/org/jetbrains/ring/BunnymarkBenchmark.kt @@ -0,0 +1,136 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package org.jetbrains.ring + +import org.jetbrains.benchmarksLauncher.Blackhole +import kotlin.random.Random + +// Benchmark for KT-46425. +open class BunnymarkBenchmark { + + val maxX = 640f + val minX = 0f + val maxY = 480f + val minY = 0f + val gravity = 0.5f + val framesCount = 60 + val containerSize = 800_000 + + //Benchmark + fun testBunnymark() { + val bunnys = BunnyContainer(containerSize) + + for (n in 0 until bunnys.maxSize) bunnys.alloc() + + val random = Random(0) + + fun executeFrame() { + bunnys.fastForEach { bunny -> + bunny.x += bunny.speedXf + bunny.y += bunny.speedYf + bunny.speedYf += gravity + + if (bunny.x > maxX) { + bunny.speedXf *= -1 + bunny.x = maxX + } else if (bunny.x < minX) { + bunny.speedXf *= -1 + bunny.x = minX + } + + if (bunny.y > maxY) { + bunny.speedYf *= -0.85f + bunny.y = maxY + bunny.radiansf = (random.nextFloat() - 0.5f) * 0.2f + if (random.nextFloat() > 0.5) { + bunny.speedYf -= random.nextFloat() * 6 + } + } else if (bunny.y < minY) { + bunny.speedYf = 0f + bunny.y = minY + } + } + } + + for (n in 0 until framesCount) { + executeFrame() + } + } +} + +class BunnyContainer(maxSize: Int) : FSprites(maxSize) { + val speeds = FBuffer(maxSize * Float.SIZE_BYTES * 2).f32 + var FSprite.speedXf: Float get() = speeds[index * 2 + 0] ; set(value) { speeds[index * 2 + 0] = value } + var FSprite.speedYf: Float get() = speeds[index * 2 + 1] ; set(value) { speeds[index * 2 + 1] = value } +} + +open class FSprites(val maxSize: Int) { + var size = 0 + val data = FBuffer(maxSize * FSprites.STRIDE * 4) + + val f32 = data.f32 + + fun alloc() = FSprite(size++ * STRIDE) + + var FSprite.x: Float get() = f32[offset + 0]; set(value) { f32[offset + 0] = value } + var FSprite.y: Float get() = f32[offset + 1]; set(value) { f32[offset + 1] = value } + var FSprite.radiansf: Float get() = f32[offset + 4] ; set(value) { f32[offset + 4] = value } + + companion object { + const val STRIDE = 8 + } +} + +inline fun T.fastForEach(callback: T.(sprite: FSprite) -> Unit) { + var m = 0 + for (n in 0 until size) { + callback(FSprite(m)) + m += FSprites.STRIDE + } +} + +inline class FSprite(val id: Int) { + inline val offset get() = id + inline val index get() = offset / FSprites.STRIDE +} + +class FBuffer private constructor(val mem: MemBuffer, val size: Int = mem.size) { + val arrayFloat: Float32Buffer = mem.asFloat32Buffer() + + inline val f32 get() = arrayFloat + + companion object { + private fun Int.sizeAligned() = (this + 0xF) and 0xF.inv() + operator fun invoke(size: Int): FBuffer = FBuffer(MemBufferAlloc(size.sizeAligned()), size) + } +} + +class MemBuffer(val data: ByteArray) +inline val MemBuffer.size: Int get() = data.size + +class Float32Buffer(val mbuffer: MemBuffer, val byteOffset: Int, val size: Int) { + companion object { + const val SIZE = 4 + } + val MEM_OFFSET = byteOffset / SIZE + val MEM_SIZE = size / SIZE + fun getByteIndex(index: Int) = byteOffset + index * SIZE +} +val Float32Buffer.mem: MemBuffer get() = mbuffer +val Float32Buffer.offset: Int get() = MEM_OFFSET +val Float32Buffer.size: Int get() = MEM_SIZE +operator fun Float32Buffer.get(index: Int): Float = mbuffer.getFloat(getByteIndex(index)) +operator fun Float32Buffer.set(index: Int, value: Float): Unit = mbuffer.setFloat(getByteIndex(index), value) + +fun MemBufferAlloc(size: Int): MemBuffer = MemBuffer(ByteArray(size)) + +fun MemBuffer.getFloat(index: Int): Float = data.getFloatAt(index) +fun MemBuffer.setFloat(index: Int, value: Float): Unit = data.setFloatAt(index, value) + +fun MemBuffer.asFloat32Buffer(): Float32Buffer = this.sliceFloat32Buffer() +fun MemBuffer.sliceFloat32Buffer(offset: Int = 0, size: Int = (this.size / 4) - offset): Float32Buffer = this._sliceFloat32Buffer(offset, size) +fun MemBuffer._sliceFloat32Buffer(offset: Int, size: Int): Float32Buffer = + Float32Buffer(this, offset * 4, size) diff --git a/kotlin-native/performance/ring/src/main/kotlin/org/jetbrains/ring/ChainableBenchmark.kt b/kotlin-native/performance/ring/src/main/kotlin/org/jetbrains/ring/ChainableBenchmark.kt new file mode 100644 index 00000000000..9c7a82a75f6 --- /dev/null +++ b/kotlin-native/performance/ring/src/main/kotlin/org/jetbrains/ring/ChainableBenchmark.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package org.jetbrains.ring + +import org.jetbrains.benchmarksLauncher.Blackhole +import org.jetbrains.benchmarksLauncher.Random + +// Benchmark from KT-46482. +open class ChainableBenchmark { + + class IntArrayList(capacity: Int = 7) { + private var data: IntArray = IntArray(capacity) + var size: Int = 0 + private set + val capacity get() = data.size + private fun grow(minSize: Int) { + data = data.copyOf(kotlin.math.max(minSize, capacity * 3)) + } + private fun ensure(count: Int) { + if (size + count >= capacity) { + grow(size + count) + } + } + + fun add(value: Int) { + ensure(1) + data[size++] = value + } + + fun addChainable(value: Int): IntArrayList { + add(value) + return this + } + + operator fun get(index: Int): Int = data[index] + operator fun set(index: Int, value: Int) { + data[index] = value + } + } + val size = BENCHMARK_SIZE * 100 + + //Benchmark + fun testChainable() { + val list = IntArrayList() + for (i in 0..size) { + list.addChainable(i) + } + for (i in 0..size) { + list[i] = i * 2 + } + var sum = 0 + for (i in 0..size) { + sum += list[i] + } + } +}