Skip to content

Commit

Permalink
Features performance fixes (#266)
Browse files Browse the repository at this point in the history
[jacodb-core] Add more optimizations of memory usage by CompactPersistentLongSet

For sets of size 2, special pair of longs added to avoid boxing.
Longs are interned if possible. Elements of PackedPersistentLongSet
are interned if possible.

[jacodb-core] Use array of features to iterate over them in FeaturesChain

[jacodb-core] Make sure UnknownClassMethodsAndFields is after UnknownClasses in featuresChain
  • Loading branch information
Saloed authored Sep 2, 2024
1 parent 1abfc75 commit 3377c0c
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import org.jacodb.impl.features.classpaths.AbstractJcResolvedResult.JcResolvedCl
import org.jacodb.impl.features.classpaths.AbstractJcResolvedResult.JcResolvedTypeResultImpl
import org.jacodb.impl.features.classpaths.ClasspathCache
import org.jacodb.impl.features.classpaths.JcUnknownClass
import org.jacodb.impl.features.classpaths.UnknownClassMethodsAndFields
import org.jacodb.impl.features.classpaths.UnknownClasses
import org.jacodb.impl.features.classpaths.isResolveAllToUnknown
import org.jacodb.impl.fs.ClassSourceImpl
Expand All @@ -71,7 +72,9 @@ class JcClasspathImpl(
if (!features.any { it is UnknownClasses }) {
features + JcClasspathFeatureImpl()
} else {
features.filter { it !is UnknownClasses } + JcClasspathFeatureImpl() + UnknownClasses
(features.filter { it !is UnknownClasses } + JcClasspathFeatureImpl() + UnknownClasses).let {
it.filter { it !is UnknownClassMethodsAndFields } + UnknownClassMethodsAndFields
}
})

override suspend fun refreshed(closeOld: Boolean): JcClasspath {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,33 @@ import org.jacodb.api.jvm.JcLookupExtFeature

class JcFeaturesChain(val features: List<JcClasspathFeature>) {

val featuresArray = features.toTypedArray()
val classLookups = features.filterIsInstance<JcLookupExtFeature>()

inline fun <reified T : JcClasspathFeature> run(call: (T) -> Unit) {
for (feature in features) {
for (feature in featuresArray) {
if (feature is T) {
call(feature)
}
}
}

inline fun <reified T : JcClasspathFeature, W> call(call: (T) -> W?): W? {
val (result: W?, event: JcFeatureEvent?) = features.firstNotNullOfOrNull { feature ->
(feature as? T)?.let(call)?.let { result -> result to feature.event(result) }
} ?: return null
event?.let {
features.forEach { feature -> feature.on(event) }
for (feature in featuresArray) {
if (feature is T) {
val result = call(feature)
if (result != null) {
val event = feature.event(result)
if (event != null) {
for (anyFeature in featuresArray) {
anyFeature.on(event)
}
}
return result
}
}
}
return result
return null
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
get() = when (value) {
null -> 0
is Long -> 1
is LongPair -> 2
is PackedPersistentLongSet -> value.size
else -> throw illegalStateException()
}
Expand All @@ -32,6 +33,7 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
return when (value) {
null -> emptyList()
is Long -> listOf(value)
is LongPair -> listOf(value.one, value.two)
is PackedPersistentLongSet -> value
else -> throw illegalStateException()
}.iterator()
Expand All @@ -48,6 +50,7 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
return when (value) {
null -> false
is Long -> value == element
is LongPair -> value.one == element || value.two == element
is PackedPersistentLongSet -> element in value
else -> throw illegalStateException()
}
Expand All @@ -60,8 +63,22 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
if (value == element) {
this
} else {
CompactPersistentLongSet(PackedPersistentLongSet().addAll(listOf(value, element.interned)))
CompactPersistentLongSet(
if (value < element) LongPair(value, element) else LongPair(element, value)
)
}

is LongPair ->
if (value.one == element || value.two == element) {
this
} else {
CompactPersistentLongSet(
PackedPersistentLongSet().addAll(
listOf(value.one.interned, value.two.interned, element.interned)
)
)
}

is PackedPersistentLongSet -> {
val newValue = value.add(element.interned)
if (newValue === value) {
Expand All @@ -70,6 +87,7 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
CompactPersistentLongSet(newValue)
}
}

else -> throw illegalStateException()
}
}
Expand All @@ -78,14 +96,26 @@ internal class CompactPersistentLongSet(private val value: Any? = null) : Collec
return when (value) {
null -> this
is Long -> if (value == element) CompactPersistentLongSet() else this
is LongPair ->
if (value.one == element) {
CompactPersistentLongSet(value.two.interned)
} else if (value.two == element) {
CompactPersistentLongSet(value.one.interned)
} else {
this
}

is PackedPersistentLongSet -> {
val newValue = value.remove(element)
if (newValue === value) {
this
} else {
CompactPersistentLongSet(if (newValue.size == 1) newValue.first() else newValue)
CompactPersistentLongSet(
if (newValue.size == 2) newValue.iterator().let { LongPair(it.next(), it.next()) } else newValue
)
}
}

else -> throw illegalStateException()
}
}
Expand All @@ -103,4 +133,6 @@ private val Long.interned: Long
object LongInterner {

val boxedLongs = Array(200000) { it.toLong() }
}
}

private data class LongPair(val one: Long, val two: Long)
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ 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.Assertions.assertTrue
import org.junit.jupiter.api.Test

class CompactPersistentLongSetTest {
Expand All @@ -36,5 +37,7 @@ class CompactPersistentLongSetTest {
set = set.remove(3L)
assertEquals(set.size, 1)
assertEquals(setOf(1L), set.toSet())
set = set.remove(1L)
assertTrue(set.isEmpty())
}
}

0 comments on commit 3377c0c

Please sign in to comment.