Skip to content

Commit

Permalink
ERS optimizations
Browse files Browse the repository at this point in the history
[jacodb-core] Optimize CompactPersistentLongSet

[jacodb-core] Fix method names: getPropertyNames(), getBlobNames(), getLinkNames()

In SqlErsTransaction, TODO stub implementations added.

[jacodb-core] Optimize storing tiny blobs in RAMPersistentDataContainer

[jacodb-core] Correct code in Iinc5 test class

[jacodb-core] In SafeSubstitution, ignore substitution problems for unknown classes

[jacodb-core] Add test for try-catch blocks inside Kotlin inline lambda
  • Loading branch information
Saloed committed Aug 27, 2024
1 parent 788d041 commit 48da323
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ interface Transaction : Closeable {
/**
* Returns set of blob names ever being set to an entity of specified `type`.
*/
fun getBlobNamesNames(type: String): Set<String> = emptySet()
fun getBlobNames(type: String): Set<String> = emptySet()

/**
* Returns set of link names ever being set to an entity of specified `type`.
*/
fun getLinkNamesNames(type: String): Set<String> = emptySet()
fun getLinkNames(type: String): Set<String> = emptySet()

fun all(type: String): EntityIterable

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ abstract class AbstractTransactionDecorator : Transaction {
override fun deleteEntity(id: EntityId) = delegate.deleteEntity(id)
override fun getTypeId(type: String): Int = delegate.getTypeId(type)
override fun getPropertyNames(type: String): Set<String> = delegate.getPropertyNames(type)
override fun getBlobNamesNames(type: String): Set<String> = delegate.getBlobNamesNames(type)
override fun getLinkNamesNames(type: String): Set<String> = delegate.getLinkNamesNames(type)
override fun getBlobNames(type: String): Set<String> = delegate.getBlobNames(type)
override fun getLinkNames(type: String): Set<String> = delegate.getLinkNames(type)
override fun all(type: String): EntityIterable = delegate.all(type)
override fun <T : Any> find(type: String, propertyName: String, value: T): EntityIterable =
delegate.find(type, propertyName, value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ class KVErsTransaction(
ers.getPropNameFromMapName(it)
}

override fun getBlobNamesNames(type: String): Set<String> = getAttributeName(type) {
override fun getBlobNames(type: String): Set<String> = getAttributeName(type) {
ers.getBlobNameFromMapName(it)
}

override fun getLinkNamesNames(type: String): Set<String> = getAttributeName(type) {
override fun getLinkNames(type: String): Set<String> = getAttributeName(type) {
ers.getLinkNameFromMapName(it)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.impl.storage.ers.ram


fun ByteArray.probablyCached(): ByteArray {
return if (size == 1) {
theCache[this[0].toInt() and 0xff]
} else {
this
}
}

private val theCache = Array(256) { i ->
byteArrayOf(i.toByte())
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,16 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
if (value == element) {
this
} else {
CompactPersistentLongSet(PackedPersistentLongSet().addAll(listOf(value, element)))
CompactPersistentLongSet(PackedPersistentLongSet().addAll(listOf(value, element.interned)))
}

is PackedPersistentLongSet -> {
val newValue = value.add(element)
val newValue = value.add(element.interned)
if (newValue === value) {
this
} else {
CompactPersistentLongSet(value.add(element))
CompactPersistentLongSet(newValue)
}
}

else -> throw illegalStateException()
}
}
Expand All @@ -85,16 +83,9 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
if (newValue === value) {
this
} else {
newValue.run {
if (size == 1) {
CompactPersistentLongSet(first().interned)
} else {
CompactPersistentLongSet(newValue)
}
}
CompactPersistentLongSet(if (newValue.size == 1) newValue.first() else newValue)
}
}

else -> throw illegalStateException()
}
}
Expand All @@ -103,13 +94,13 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
IllegalStateException("CompactPersistentLongSet.value can only be Long or PersistentLongSet")
}

private val Any?.interned: Any?
private val Long.interned: Long
get() =
if (this is Long && this >= 0 && this < LongInterner.boxedLongs.size) LongInterner.boxedLongs[this.toInt()]
if (this in 0 until LongInterner.boxedLongs.size) LongInterner.boxedLongs[this.toInt()]
else this

// TODO: remove this interner if specialized persistent collections would be used
object LongInterner {

val boxedLongs = Array<Any>(200000) { it.toLong() }
val boxedLongs = Array(200000) { it.toLong() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ internal class RAMPersistentDataContainer(
} else {
val blobs = blobs[blobsKey] ?: TransactionalPersistentLongMap()
blobs.withMutableBlobs(blobsKey) {
put(id.instanceId, value)
put(id.instanceId, value.probablyCached())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ internal class RAMTransaction(override val ers: RAMEntityRelationshipStorage) :

override fun getPropertyNames(type: String): Set<String> = dataContainerChecked.getPropertyNames(type)

override fun getBlobNamesNames(type: String): Set<String> = dataContainerChecked.getBlobNames(type)
override fun getBlobNames(type: String): Set<String> = dataContainerChecked.getBlobNames(type)

override fun getLinkNamesNames(type: String): Set<String> = dataContainerChecked.getLinkNames(type)
override fun getLinkNames(type: String): Set<String> = dataContainerChecked.getLinkNames(type)

override fun all(type: String): EntityIterable = dataContainerChecked.all(this, type)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ class SqlErsTransactionImpl(
return getTypeIdOrNull(type) ?: return -1
}

override fun getPropertyNames(type: String): Set<String> {
TODO("Not yet implemented")
}

override fun getBlobNames(type: String): Set<String> {
TODO("Not yet implemented")
}

override fun getLinkNames(type: String): Set<String> {
TODO("Not yet implemented")
}

override fun all(type: String): EntityIterable {
val typeId = getTypeIdOrNull(type) ?: return EntityIterable.EMPTY
val entityTable = getEntityTableByTypeIdOrNull(typeId) ?: return EntityIterable.EMPTY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@

package org.jacodb.impl.types.substition

import org.jacodb.api.jvm.*
import org.jacodb.api.jvm.JcClassOrInterface
import org.jacodb.api.jvm.JcGenericsSubstitutionFeature
import org.jacodb.api.jvm.JcSubstitutor
import org.jacodb.api.jvm.JvmType
import org.jacodb.api.jvm.JvmTypeParameterDeclaration
import org.jacodb.impl.cfg.util.OBJECT_CLASS
import org.jacodb.impl.features.classpaths.JcUnknownClass
import org.jacodb.impl.types.signature.JvmClassRefType
import org.jacodb.impl.types.typeParameters

Expand All @@ -40,27 +45,39 @@ object SafeSubstitution : JcGenericsSubstitutionFeature {
outer: JcSubstitutor?
): JcSubstitutor {
val params = clazz.typeParameters
require(params.size == parameters.size) {
"Incorrect parameters specified for class ${clazz.name}: expected ${params.size} found ${parameters.size}"
return if (clazz is JcUnknownClass) {
ignoreProblemsAndSubstitute(params, parameters, outer)
} else {
require(params.size == parameters.size) {
"Incorrect parameters specified for class ${clazz.name}: expected ${params.size} found ${parameters.size}"
}
params.substitute(parameters, outer)
}
return params.substitute(parameters, outer)
}
}

object IgnoreSubstitutionProblems : JcGenericsSubstitutionFeature {

private val jvmObjectType = JvmClassRefType(OBJECT_CLASS, true, emptyList())

override fun substitute(
clazz: JcClassOrInterface,
parameters: List<JvmType>,
outer: JcSubstitutor?
): JcSubstitutor {
val params = clazz.typeParameters
if (params.size == parameters.size) {
return params.substitute(parameters, outer)
}
val substitution = params.associateWith { it.bounds?.first() ?: jvmObjectType }
return (outer ?: JcSubstitutorImpl.empty).newScope(substitution)
return ignoreProblemsAndSubstitute(params, parameters, outer)
}
}

private val jvmObjectType = JvmClassRefType(OBJECT_CLASS, true, emptyList())

private fun ignoreProblemsAndSubstitute(
params: List<JvmTypeParameterDeclaration>,
parameters: List<JvmType>,
outer: JcSubstitutor?
): JcSubstitutor {
if (params.size == parameters.size) {
return params.substitute(parameters, outer)
}
val substitution = params.associateWith { it.bounds?.first() ?: jvmObjectType }
return (outer ?: JcSubstitutorImpl.empty).newScope(substitution)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package org.jacodb.testing.cfg
import org.jacodb.api.jvm.ext.findClass
import org.junit.jupiter.api.Assertions.assertArrayEquals
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test

class IincTest : BaseInstructionsTest() {
Expand Down Expand Up @@ -113,4 +114,7 @@ class IincTest : BaseInstructionsTest() {
@Test
fun `kotlin iinc4`() = runTest(Iinc4::class.java.name)

@Test
fun `kotlin iinc5`() = runTest(Iinc5::class.java.name)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.testing.storage.ers

import org.jacodb.impl.storage.ers.ram.CompactPersistentLongSet
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class CompactPersistentLongSetTest {

@Test
fun `add remove`() {
var set = CompactPersistentLongSet()
set = set.add(1L)
set = set.add(2L)
set = set.add(3L)
assertEquals(set.size, 3)
assertEquals(setOf(1L, 2L, 3L), set.toSet())
set = set.remove(2L)
assertEquals(set.size, 2)
assertEquals(setOf(1L, 3L), set.toSet())
set = set.remove(3L)
assertEquals(set.size, 1)
assertEquals(setOf(1L), set.toSet())
}
}
19 changes: 19 additions & 0 deletions jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/cfg/Iinc.kt
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,22 @@ class Iinc4 {
}

}

class Iinc5 {

fun box(): String {
val result = intArrayOf(0)
var i = 0

repeat(10) {
try {
result[i++]++
} catch (_: ArrayIndexOutOfBoundsException) {
result[0] += 2
i = 0
}
}

return if (result[0] == 15) "OK" else "Catch failed: ${result[0]}"
}
}

0 comments on commit 48da323

Please sign in to comment.