From 9043b47dd8c7dfa2af5fb1d28b988ec85b896333 Mon Sep 17 00:00:00 2001 From: Michal Domagala Date: Mon, 6 Jan 2025 09:21:31 +0100 Subject: [PATCH] Use buffered writer (#1241) Without buffered reader, every single output character through given stack goes to "StreamEncoder public void write(int c)" and single element char array is created. It makes pressure to GC and CPU BufferedReader reduce the pressure write:130, StreamEncoder (sun.nio.cs) write:201, OutputStreamWriter (java.io) writeMetadata:338, PrometheusTextFormatWriter (io.prometheus.metrics.expositionformats) writeGauge:129, PrometheusTextFormatWriter (io.prometheus.metrics.expositionformats) write:68, PrometheusTextFormatWriter (io.prometheus.metrics.expositionformats) write:48, PrometheusOutputFormat$1 (org.springframework.boot.actuate.metrics.export.prometheus) scrape:87, PrometheusScrapeEndpoint (org.springframework.boot.actuate.metrics.export.prometheus) StreamEncoder public void write(int c) throws IOException { char[] cbuf = new char[1]; cbuf[0] = (char) c; Signed-off-by: Michal Domagala Co-authored-by: Michal Domagala --- .../PrometheusTextFormatWriter.java | 38 ++++++++----------- .../expositionformats/TextFormatUtil.java | 12 ++---- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java index eb07abc73..8602b0ba5 100644 --- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java +++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java @@ -20,6 +20,7 @@ import io.prometheus.metrics.model.snapshots.StateSetSnapshot; import io.prometheus.metrics.model.snapshots.SummarySnapshot; import io.prometheus.metrics.model.snapshots.UnknownSnapshot; +import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -59,7 +60,7 @@ public void write(OutputStream out, MetricSnapshots metricSnapshots) throws IOEx // See https://prometheus.io/docs/instrumenting/exposition_formats/ // "unknown", "gauge", "counter", "stateset", "info", "histogram", "gaugehistogram", and // "summary". - OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); + Writer writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); for (MetricSnapshot snapshot : metricSnapshots) { if (snapshot.getDataPoints().size() > 0) { if (snapshot instanceof CounterSnapshot) { @@ -95,7 +96,7 @@ public void write(OutputStream out, MetricSnapshots metricSnapshots) throws IOEx writer.flush(); } - public void writeCreated(OutputStreamWriter writer, MetricSnapshot snapshot) throws IOException { + public void writeCreated(Writer writer, MetricSnapshot snapshot) throws IOException { boolean metadataWritten = false; MetricMetadata metadata = snapshot.getMetadata(); for (DataPointSnapshot data : snapshot.getDataPoints()) { @@ -111,8 +112,7 @@ public void writeCreated(OutputStreamWriter writer, MetricSnapshot snapshot) thr } } - private void writeCounter(OutputStreamWriter writer, CounterSnapshot snapshot) - throws IOException { + private void writeCounter(Writer writer, CounterSnapshot snapshot) throws IOException { if (snapshot.getDataPoints().size() > 0) { MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, "_total", "counter", metadata); @@ -124,7 +124,7 @@ private void writeCounter(OutputStreamWriter writer, CounterSnapshot snapshot) } } - private void writeGauge(OutputStreamWriter writer, GaugeSnapshot snapshot) throws IOException { + private void writeGauge(Writer writer, GaugeSnapshot snapshot) throws IOException { MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, "", "gauge", metadata); for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getDataPoints()) { @@ -134,8 +134,7 @@ private void writeGauge(OutputStreamWriter writer, GaugeSnapshot snapshot) throw } } - private void writeHistogram(OutputStreamWriter writer, HistogramSnapshot snapshot) - throws IOException { + private void writeHistogram(Writer writer, HistogramSnapshot snapshot) throws IOException { MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, "", "histogram", metadata); for (HistogramSnapshot.HistogramDataPointSnapshot data : snapshot.getDataPoints()) { @@ -182,8 +181,7 @@ private ClassicHistogramBuckets getClassicBuckets( } private void writeGaugeCountSum( - OutputStreamWriter writer, HistogramSnapshot snapshot, MetricMetadata metadata) - throws IOException { + Writer writer, HistogramSnapshot snapshot, MetricMetadata metadata) throws IOException { // Prometheus text format does not support gaugehistogram's _gcount and _gsum. // So we append _gcount and _gsum as gauge metrics. boolean metadataWritten = false; @@ -212,8 +210,7 @@ private void writeGaugeCountSum( } } - private void writeSummary(OutputStreamWriter writer, SummarySnapshot snapshot) - throws IOException { + private void writeSummary(Writer writer, SummarySnapshot snapshot) throws IOException { boolean metadataWritten = false; MetricMetadata metadata = snapshot.getMetadata(); for (SummarySnapshot.SummaryDataPointSnapshot data : snapshot.getDataPoints()) { @@ -248,7 +245,7 @@ private void writeSummary(OutputStreamWriter writer, SummarySnapshot snapshot) } } - private void writeInfo(OutputStreamWriter writer, InfoSnapshot snapshot) throws IOException { + private void writeInfo(Writer writer, InfoSnapshot snapshot) throws IOException { MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, "_info", "gauge", metadata); for (InfoSnapshot.InfoDataPointSnapshot data : snapshot.getDataPoints()) { @@ -258,8 +255,7 @@ private void writeInfo(OutputStreamWriter writer, InfoSnapshot snapshot) throws } } - private void writeStateSet(OutputStreamWriter writer, StateSetSnapshot snapshot) - throws IOException { + private void writeStateSet(Writer writer, StateSetSnapshot snapshot) throws IOException { MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, "", "gauge", metadata); for (StateSetSnapshot.StateSetDataPointSnapshot data : snapshot.getDataPoints()) { @@ -292,8 +288,7 @@ private void writeStateSet(OutputStreamWriter writer, StateSetSnapshot snapshot) } } - private void writeUnknown(OutputStreamWriter writer, UnknownSnapshot snapshot) - throws IOException { + private void writeUnknown(Writer writer, UnknownSnapshot snapshot) throws IOException { MetricMetadata metadata = snapshot.getMetadata(); writeMetadata(writer, "", "untyped", metadata); for (UnknownSnapshot.UnknownDataPointSnapshot data : snapshot.getDataPoints()) { @@ -303,13 +298,13 @@ private void writeUnknown(OutputStreamWriter writer, UnknownSnapshot snapshot) } } - private void writeNameAndLabels( - OutputStreamWriter writer, String name, String suffix, Labels labels) throws IOException { + private void writeNameAndLabels(Writer writer, String name, String suffix, Labels labels) + throws IOException { writeNameAndLabels(writer, name, suffix, labels, null, 0.0); } private void writeNameAndLabels( - OutputStreamWriter writer, + Writer writer, String name, String suffix, Labels labels, @@ -327,8 +322,7 @@ private void writeNameAndLabels( } private void writeMetadata( - OutputStreamWriter writer, String suffix, String typeString, MetricMetadata metadata) - throws IOException { + Writer writer, String suffix, String typeString, MetricMetadata metadata) throws IOException { if (metadata.getHelp() != null && !metadata.getHelp().isEmpty()) { writer.write("# HELP "); writer.write(metadata.getPrometheusName()); @@ -365,7 +359,7 @@ private void writeEscapedHelp(Writer writer, String s) throws IOException { } } - private void writeScrapeTimestampAndNewline(OutputStreamWriter writer, DataPointSnapshot data) + private void writeScrapeTimestampAndNewline(Writer writer, DataPointSnapshot data) throws IOException { if (data.hasScrapeTimestamp()) { writer.write(' '); diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java index 54daaaa3e..e48f545c5 100644 --- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java +++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java @@ -2,16 +2,15 @@ import io.prometheus.metrics.model.snapshots.Labels; import java.io.IOException; -import java.io.OutputStreamWriter; import java.io.Writer; public class TextFormatUtil { - static void writeLong(OutputStreamWriter writer, long value) throws IOException { + static void writeLong(Writer writer, long value) throws IOException { writer.append(Long.toString(value)); } - static void writeDouble(OutputStreamWriter writer, double d) throws IOException { + static void writeDouble(Writer writer, double d) throws IOException { if (d == Double.POSITIVE_INFINITY) { writer.write("+Inf"); } else if (d == Double.NEGATIVE_INFINITY) { @@ -22,7 +21,7 @@ static void writeDouble(OutputStreamWriter writer, double d) throws IOException } } - static void writeTimestamp(OutputStreamWriter writer, long timestampMs) throws IOException { + static void writeTimestamp(Writer writer, long timestampMs) throws IOException { writer.write(Long.toString(timestampMs / 1000L)); writer.write("."); long ms = timestampMs % 1000; @@ -55,10 +54,7 @@ static void writeEscapedLabelValue(Writer writer, String s) throws IOException { } static void writeLabels( - OutputStreamWriter writer, - Labels labels, - String additionalLabelName, - double additionalLabelValue) + Writer writer, Labels labels, String additionalLabelName, double additionalLabelValue) throws IOException { writer.write('{'); for (int i = 0; i < labels.size(); i++) {