Greatly improve performance of clusterization by hinting that min/max should be performed with implied zero value

This commit is contained in:
DBotThePony 2025-02-10 14:19:25 +07:00
parent b1783343bc
commit acc182eb26
Signed by: DBot
GPG Key ID: DCC23B5715498507
3 changed files with 26 additions and 6 deletions

View File

@ -104,7 +104,7 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con
map[1f] = maximum.formatPower() map[1f] = maximum.formatPower()
for (cluster in chart.asIterable().clusterize(random)) { for (cluster in chart.asIterable().clusterize(random, zeroBound = true)) {
val perc = (cluster.center / maximum).toFloat() val perc = (cluster.center / maximum).toFloat()
if (map.keys.none { (it - perc).absoluteValue < 0.08f }) { if (map.keys.none { (it - perc).absoluteValue < 0.08f }) {

View File

@ -90,6 +90,7 @@ private class MutableCluster<V : Comparable<V>>(var center: V) {
private fun <V : Comparable<V>> Iterable<V>.clusterize( private fun <V : Comparable<V>> Iterable<V>.clusterize(
random: RandomGenerator, random: RandomGenerator,
initialClusters: Int = 1, initialClusters: Int = 1,
zeroBound: Boolean = false,
identity: V, identity: V,
plus: (V, V) -> V, plus: (V, V) -> V,
minus: (V, V) -> V, minus: (V, V) -> V,
@ -126,8 +127,27 @@ private fun <V : Comparable<V>> Iterable<V>.clusterize(
cluster.values.add(wrapped) cluster.values.add(wrapped)
} }
if (zeroBound) {
min = minOf(min, identity)
max = maxOf(max, identity)
}
if (min == max) { if (min == max) {
return listOf(Cluster.Impl(listOf(min), min)) return listOf(Cluster.Impl(listOf(min), min))
} else {
// if "clusters" already satisfy constraints...
clusters.forEach { it.calculateCenter(identity, plus, minus, divInt, abs) }
values.forEach { it.updateError(abs, minus) }
val maxError = values.maxOf { it.error }
if (!heuristics(min, max, maxError)) {
return clusters.stream()
.filter { it.values.isNotEmpty() }
.map { Cluster.Impl(it.values.map { it.value }, it.center) }
.sorted { o1, o2 -> o2.values.size.compareTo(o1.values.size) } // большие кластеры должны идти первыми
.toList()
}
} }
while (true) { while (true) {
@ -182,8 +202,8 @@ private fun <V : Comparable<V>> Iterable<V>.clusterize(
// TODO: could use some tweaking // TODO: could use some tweaking
private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02") private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02")
fun Iterable<Decimal>.clusterize(random: RandomGenerator, clusters: Int? = null): List<Cluster<Decimal>> { fun Iterable<Decimal>.clusterize(random: RandomGenerator, clusters: Int? = null, zeroBound: Boolean = false): List<Cluster<Decimal>> {
return clusterize(random, clusters ?: 1, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error -> return clusterize(random, clusters ?: 1, zeroBound, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error ->
if (clusters != null) if (clusters != null)
false false
else else

View File

@ -456,7 +456,7 @@ private fun formatHistoryChart(
val rand = java.util.Random() val rand = java.util.Random()
if (maxTransferred.isNotZero && transferredMult > 0.2f) { if (maxTransferred.isNotZero && transferredMult > 0.2f) {
for (cluster in widget.transferred.clusterize(rand)) { for (cluster in widget.transferred.clusterize(rand, zeroBound = true)) {
val perc = (cluster.center / maxTransferred).toFloat() * transferredMult val perc = (cluster.center / maxTransferred).toFloat() * transferredMult
if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f }) if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f })
@ -465,7 +465,7 @@ private fun formatHistoryChart(
} }
if (maxReceived.isNotZero && receivedMult > 0.2f) { if (maxReceived.isNotZero && receivedMult > 0.2f) {
for (cluster in widget.received.clusterize(rand)) { for (cluster in widget.received.clusterize(rand, zeroBound = true)) {
val perc = zero + (cluster.center / maxReceived).toFloat() * receivedMult val perc = zero + (cluster.center / maxReceived).toFloat() * receivedMult
if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f }) if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f })
@ -473,7 +473,7 @@ private fun formatHistoryChart(
} }
} }
val clusters = diff.asIterable().clusterize(rand) val clusters = diff.asIterable().clusterize(rand, zeroBound = true)
for (cluster in clusters) { for (cluster in clusters) {
val perc: Float val perc: Float