From 359c17bf29a2e9d57580d41afc8dc22fc7453fb1 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 17 Mar 2023 16:53:34 +0100 Subject: [PATCH] add unit type for bytes --- .../lucares/pdb/map/PersistentMapStats.java | 20 +---- .../y-axis-definition.component.html | 12 ++- .../org/lucares/pdb/plot/api/RangeUnit.java | 16 ++-- .../lucares/pdb/plot/api/YAxisDefinition.java | 12 +-- .../lucares/recommind/logs/YAxisTicks.java | 89 +++++++++++++++++++ .../java/org/lucares/utils/HumanBytes.java | 32 +++++++ 6 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 pdb-utils/src/main/java/org/lucares/utils/HumanBytes.java diff --git a/block-storage/src/main/java/org/lucares/pdb/map/PersistentMapStats.java b/block-storage/src/main/java/org/lucares/pdb/map/PersistentMapStats.java index d53811b..bb9e08b 100644 --- a/block-storage/src/main/java/org/lucares/pdb/map/PersistentMapStats.java +++ b/block-storage/src/main/java/org/lucares/pdb/map/PersistentMapStats.java @@ -1,6 +1,6 @@ package org.lucares.pdb.map; -import java.util.List; +import org.lucares.utils.HumanBytes; public class PersistentMapStats { private long values = 0; @@ -87,7 +87,7 @@ public class PersistentMapStats { builder.append(String.format("\navg. depth= %.2f", averageDepth)); builder.append(String.format("\navg. fill= %.2f", averageFill)); builder.append(String.format("\nvalues/node=%.2f", averageValuesInNode)); - builder.append(String.format("\nfile size= %s\n", toHumanBytes(fileSize))); + builder.append(String.format("\nfile size= %s\n", HumanBytes.toHumanBytes(fileSize))); return builder.toString(); } @@ -100,21 +100,9 @@ public class PersistentMapStats { builder.append(String.format("\navg. depth= %.2f -> %.2f", old.averageDepth, averageDepth)); builder.append(String.format("\navg. fill= %.2f -> %.2f", old.averageFill, averageFill)); builder.append(String.format("\nvalues/node=%.2f -> %.2f", old.averageValuesInNode, averageValuesInNode)); - builder.append(String.format("\nfile size= %s -> %s\n", toHumanBytes(old.fileSize), toHumanBytes(fileSize))); + builder.append(String.format("\nfile size= %s -> %s\n", HumanBytes.toHumanBytes(old.fileSize), + HumanBytes.toHumanBytes(fileSize))); return builder.toString(); } - private static String toHumanBytes(final long bytes) { - final List powers = List.of("bytes", "KB", "MB", "GB", "TB", "PB", "EB"); - - int power = 1; - String result = String.format("%d bytes", bytes); - while (bytes >= Math.pow(1024, power) && power < powers.size()) { - result = String.format("%.3f", bytes / Math.pow(1024, power)); - result = result.replaceAll("\\.?0*$", ""); - result = result + " " + powers.get(power); - power = power + 1; - } - return result; - } } diff --git a/pdb-js/src/app/y-axis-definition/y-axis-definition.component.html b/pdb-js/src/app/y-axis-definition/y-axis-definition.component.html index 406b51c..bd5fefc 100644 --- a/pdb-js/src/app/y-axis-definition/y-axis-definition.component.html +++ b/pdb-js/src/app/y-axis-definition/y-axis-definition.component.html @@ -11,11 +11,15 @@ Y{{yIndex}}-Axis Unit: - + auto (number) no unit - + + auto (bytes) + bytes + + auto (time) millis seconds @@ -25,11 +29,11 @@ - + Min: - + Max: diff --git a/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/RangeUnit.java b/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/RangeUnit.java index 19c0213..32e2ed9 100644 --- a/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/RangeUnit.java +++ b/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/RangeUnit.java @@ -7,6 +7,8 @@ public enum RangeUnit { NO_UNIT(false, Type.Number, "Value"), + AUTOMATIC_BYTES(true, Type.Number, "Value"), + BYTES(false, Type.Number, "Value"), AUTOMATIC_TIME(true, Type.Duration, "Duration"), @@ -43,6 +45,10 @@ public enum RangeUnit { return type == Type.Number || type == Type.HistogramCount; } + public boolean isBytes() { + return this == BYTES || this == AUTOMATIC_BYTES; + } + public String getLabel() { return axisLabel; } @@ -51,11 +57,9 @@ public enum RangeUnit { return type; } - public int valueForUnit(final int value) { + public long valueForUnit(final long value) { switch (this) { - case AUTOMATIC_NUMBER: - return Integer.MAX_VALUE; case NO_UNIT: case BYTES: return value; @@ -69,10 +73,12 @@ public enum RangeUnit { return value * 60 * 60 * 1000; case DAYS: return value * 24 * 60 * 60 * 1000; + case AUTOMATIC_NUMBER: case AUTOMATIC_TIME: - return Integer.MAX_VALUE; + case AUTOMATIC_BYTES: + return Long.MAX_VALUE; } - return Integer.MAX_VALUE; + return Long.MAX_VALUE; } } diff --git a/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/YAxisDefinition.java b/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/YAxisDefinition.java index 3b87c57..2b74a6c 100644 --- a/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/YAxisDefinition.java +++ b/pdb-plotting/src/main/java/org/lucares/pdb/plot/api/YAxisDefinition.java @@ -3,8 +3,8 @@ package org.lucares.pdb.plot.api; public class YAxisDefinition { private AxisScale axisScale = AxisScale.LINEAR; - private int rangeMin = 0; - private int rangeMax = 300; + private long rangeMin = 0; + private long rangeMax = 300; private RangeUnit rangeUnit = RangeUnit.AUTOMATIC_TIME; public AxisScale getAxisScale() { @@ -23,7 +23,7 @@ public class YAxisDefinition { return rangeUnit.valueForUnit(rangeMax); } - public int getRangeMin() { + public long getRangeMin() { return rangeMin; } @@ -31,15 +31,15 @@ public class YAxisDefinition { return !rangeUnit.isAutomatic() && rangeMin >= 0 && rangeMax >= 0 && rangeMin < rangeMax; } - public void setRangeMin(final int rangeMin) { + public void setRangeMin(final long rangeMin) { this.rangeMin = rangeMin; } - public int getRangeMax() { + public long getRangeMax() { return rangeMax; } - public void setRangeMax(final int rangeMax) { + public void setRangeMax(final long rangeMax) { this.rangeMax = rangeMax; } diff --git a/pdb-plotting/src/main/java/org/lucares/recommind/logs/YAxisTicks.java b/pdb-plotting/src/main/java/org/lucares/recommind/logs/YAxisTicks.java index ed54755..fef41c2 100644 --- a/pdb-plotting/src/main/java/org/lucares/recommind/logs/YAxisTicks.java +++ b/pdb-plotting/src/main/java/org/lucares/recommind/logs/YAxisTicks.java @@ -13,6 +13,7 @@ import java.util.Locale; import java.util.concurrent.TimeUnit; import org.lucares.pdb.plot.api.YAxisDefinition; +import org.lucares.utils.HumanBytes; class YAxisTicks { @@ -45,6 +46,17 @@ class YAxisTicks { default: throw new IllegalStateException("unhandled value: " + yAxisDefinition.getRangeUnit()); } + } else if (yAxisDefinition.getRangeUnit().isBytes()) { + switch (yAxisDefinition.getAxisScale()) { + case LINEAR: + result = computeLinearYTicksByte(height, yRangeMin, yRangeMax); + break; + case LOG10: + result = computeLog10YTicksByte(height, yRangeMin, yRangeMax); + break; + default: + throw new IllegalArgumentException("Unexpected value: " + yAxisDefinition.getAxisScale()); + } } else { switch (yAxisDefinition.getAxisScale()) { case LINEAR: @@ -59,6 +71,33 @@ class YAxisTicks { return result; } + private static double log2(final double d) { + return Math.log10(d) / Math.log10(2); + } + + private static List computeLog10YTicksByte(final int height, final long yRangeMin, final long yRangeMax) { + + final int fontHeight = GnuplotSettings.TICKS_FONT_SIZE * 2; + final long plotHeight = height - GnuplotSettings.GNUPLOT_TOP_BOTTOM_MARGIN; + final int heightPerPowerOf2 = (int) Math.ceil(plotHeight / log2(yRangeMax)); + + final List ticsLabels = new ArrayList<>(); + + long nextFreePositionOnYAxis = 0; + int count = 1; + for (long v = 1; v <= yRangeMax * 10; v *= 2) // increase yRangeMax by factor 10, because Gnuplot uses the next + // log10 value when scaling the y-axis + { + if (nextFreePositionOnYAxis < count * heightPerPowerOf2) { + ticsLabels.add("\"" + HumanBytes.toHumanBytes(v) + "\" " + v); + nextFreePositionOnYAxis = count * heightPerPowerOf2 + fontHeight; + } + count++; + } + + return ticsLabels; + } + private static List computeLog10YTicksTime(final int height, final long yRangeMin, final long yRangeMax) { final List ticsLabels = Arrays.asList(// @@ -100,6 +139,52 @@ class YAxisTicks { return ticsLabels; } + private static List computeLinearYTicksByte(final long height, final long yRangeMinInMs, + final long yRangeMaxInMs) { + final long plotHeight = height - GnuplotSettings.GNUPLOT_TOP_BOTTOM_MARGIN; + final long maxLabels = plotHeight / (GnuplotSettings.TICKS_FONT_SIZE * 2); + + final long range = yRangeMaxInMs - yRangeMinInMs; + final long rangePerLabel = roundToNextLinearByteStep(range / maxLabels); + + final List ticsLabels = new ArrayList<>(); + for (long i = yRangeMinInMs; i <= yRangeMaxInMs; i += rangePerLabel) { + ticsLabels.add("\"" + byteToTic(i, rangePerLabel) + "\" " + i); + } + + return ticsLabels; + } + + private static long roundToNextLinearByteStep(final long stepSize) { + final List steps = Arrays.asList(1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, 256L, 512L, + + HumanBytes.KB, HumanBytes.KB * 5, HumanBytes.KB * 10, HumanBytes.KB * 50, HumanBytes.KB * 100, + HumanBytes.KB * 128, HumanBytes.KB * 256, HumanBytes.KB * 512, + + HumanBytes.MB, HumanBytes.MB * 5, HumanBytes.MB * 10, HumanBytes.MB * 50, HumanBytes.MB * 100, + HumanBytes.MB * 128, HumanBytes.MB * 256, HumanBytes.MB * 512, + + HumanBytes.GB, HumanBytes.GB * 5, HumanBytes.GB * 10, HumanBytes.GB * 50, HumanBytes.GB * 100, + HumanBytes.GB * 128, HumanBytes.GB * 256, HumanBytes.GB * 512, + + HumanBytes.TB, HumanBytes.TB * 5, HumanBytes.TB * 10, HumanBytes.TB * 50, HumanBytes.TB * 100, + HumanBytes.TB * 128, HumanBytes.TB * 256, HumanBytes.TB * 512, + + HumanBytes.PB, HumanBytes.PB * 5, HumanBytes.PB * 10, HumanBytes.PB * 50, HumanBytes.PB * 100, + HumanBytes.PB * 128, HumanBytes.PB * 256, HumanBytes.PB * 512, + + HumanBytes.EB, HumanBytes.EB * 5, HumanBytes.EB * 10, HumanBytes.EB * 50, HumanBytes.EB * 100, + HumanBytes.EB * 128, HumanBytes.EB * 256, HumanBytes.EB * 512); + + for (final Long step : steps) { + if (stepSize < step) { + return step; + } + } + + return stepSize; + } + private static List computeLinearYTicksTime(final long height, final long yRangeMinInMs, final long yRangeMaxInMs) { @@ -156,6 +241,10 @@ class YAxisTicks { return msPerLabel; } + private static String byteToTic(final long val, final double rangePerLabel) { + return HumanBytes.toHumanBytes(val); + } + private static String msToTic(final long ms, final double msPerLabel) { if (ms < 1000) { diff --git a/pdb-utils/src/main/java/org/lucares/utils/HumanBytes.java b/pdb-utils/src/main/java/org/lucares/utils/HumanBytes.java new file mode 100644 index 0000000..1677c65 --- /dev/null +++ b/pdb-utils/src/main/java/org/lucares/utils/HumanBytes.java @@ -0,0 +1,32 @@ +package org.lucares.utils; + +import java.util.List; + +public class HumanBytes { + + public static final long KB = 1024; + + public static final long MB = KB * 1024; + + public static final long GB = MB * 1024; + + public static final long TB = GB * 1024; + + public static final long PB = TB * 1024; + + public static final long EB = TB * 1024; + + public static String toHumanBytes(final long bytes) { + final List powers = List.of("bytes", "KB", "MB", "GB", "TB", "PB", "EB"); + + int power = 1; + String result = String.format("%d bytes", bytes); + while (bytes >= Math.pow(1024, power) && power < powers.size()) { + result = String.format("%.3f", bytes / Math.pow(1024, power)); + result = result.replaceAll("\\.?0*$", ""); + result = result + " " + powers.get(power); + power = power + 1; + } + return result; + } +}