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()
for (cluster in chart.asIterable().clusterize(random)) {
for (cluster in chart.asIterable().clusterize(random, zeroBound = true)) {
val perc = (cluster.center / maximum).toFloat()
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(
random: RandomGenerator,
initialClusters: Int = 1,
zeroBound: Boolean = false,
identity: V,
plus: (V, V) -> V,
minus: (V, V) -> V,
@ -126,8 +127,27 @@ private fun <V : Comparable<V>> Iterable<V>.clusterize(
cluster.values.add(wrapped)
}
if (zeroBound) {
min = minOf(min, identity)
max = maxOf(max, identity)
}
if (min == max) {
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) {
@ -182,8 +202,8 @@ private fun <V : Comparable<V>> Iterable<V>.clusterize(
// TODO: could use some tweaking
private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02")
fun Iterable<Decimal>.clusterize(random: RandomGenerator, clusters: Int? = null): List<Cluster<Decimal>> {
return clusterize(random, clusters ?: 1, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error ->
fun Iterable<Decimal>.clusterize(random: RandomGenerator, clusters: Int? = null, zeroBound: Boolean = false): List<Cluster<Decimal>> {
return clusterize(random, clusters ?: 1, zeroBound, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error ->
if (clusters != null)
false
else

View File

@ -456,7 +456,7 @@ private fun formatHistoryChart(
val rand = java.util.Random()
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
if (labelNames.keys.none { (it - perc).absoluteValue < 0.08f })
@ -465,7 +465,7 @@ private fun formatHistoryChart(
}
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
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) {
val perc: Float