perf: Decrease getUniques(uniqueType) by adding a EnumMap to store uniques by type, with basically zero lookup time

This commit is contained in:
yairm210
2024-09-15 19:47:43 +03:00
parent 95d0b65140
commit 0533347371
2 changed files with 29 additions and 12 deletions

View File

@ -12,6 +12,7 @@ import com.unciv.models.translations.getModifiers
import com.unciv.models.translations.getPlaceholderParameters
import com.unciv.models.translations.getPlaceholderText
import com.unciv.models.translations.removeConditionals
import java.util.EnumMap
class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val sourceObjectName: String? = null) {
@ -242,22 +243,27 @@ class LocalUniqueCache(val cache: Boolean = true) {
}
}
class UniqueMap() {
//todo Once all untyped Uniques are converted, this should be HashMap<UniqueType, *>
// For now, we can have both map types "side by side" each serving their own purpose,
// and gradually this one will be deprecated in favor of the other
private val innerUniqueMap = HashMap<String, ArrayList<Unique>>()
open class UniqueMap() {
protected val innerUniqueMap = HashMap<String, ArrayList<Unique>>()
// *shares* the list of uniques with the other map, to save on memory and allocations
// This is a memory/speed tradeoff, since there are *600 unique types*,
// 750 including deprecated, and EnumMap creates a N-sized array where N is the number of objects in the enum
val typedUniqueMap = EnumMap<UniqueType, ArrayList<Unique>>(UniqueType::class.java)
constructor(uniques: Sequence<Unique>) : this() {
addUniques(uniques.asIterable())
}
/** Adds one [unique] unless it has a ConditionalTimedUnique conditional */
fun addUnique(unique: Unique) {
open fun addUnique(unique: Unique) {
val existingArrayList = innerUniqueMap[unique.placeholderText]
if (existingArrayList != null) existingArrayList.add(unique)
else innerUniqueMap[unique.placeholderText] = arrayListOf(unique)
if (unique.type == null) return
if (typedUniqueMap[unique.type] != null) return
typedUniqueMap[unique.type] = innerUniqueMap[unique.placeholderText]
}
/** Calls [addUnique] on each item from [uniques] */
@ -280,13 +286,21 @@ class UniqueMap() {
fun hasTagUnique(tagUnique: String) =
innerUniqueMap.containsKey(tagUnique)
fun getUniques(uniqueType: UniqueType) =
innerUniqueMap[uniqueType.placeholderText]?.asSequence() ?: emptySequence()
// 160ms vs 1000-1250ms/30s
fun getUniques(uniqueType: UniqueType) = typedUniqueMap[uniqueType]
?.asSequence()
?: emptySequence()
fun getMatchingUniques(uniqueType: UniqueType, state: StateForConditionals = StateForConditionals.EmptyState) =
getUniques(uniqueType)
.filter { it.conditionalsApply(state) && !it.isTimedTriggerable }
.flatMap { it.getMultiplied(state) }
// Same as .filter | .flatMap, but more cpu/mem performant (7.7 GB vs ?? for test)
.flatMap {
when {
it.isTimedTriggerable -> emptySequence()
!it.conditionalsApply(state) -> emptySequence()
else -> it.getMultiplied(state)
}
}
fun hasMatchingUnique(uniqueType: UniqueType, state: StateForConditionals = StateForConditionals.EmptyState) =
getUniques(uniqueType).any { it.conditionalsApply(state) }
@ -300,7 +314,6 @@ class UniqueMap() {
}
}
class TemporaryUnique() : IsPartOfGameInfoSerialization {
constructor(uniqueObject: Unique, turns: Int) : this() {