|
8 | 8 | import io.prometheus.metrics.model.snapshots.HistogramSnapshot.HistogramDataPointSnapshot; |
9 | 9 | import io.prometheus.metrics.model.snapshots.Labels; |
10 | 10 | import io.prometheus.metrics.model.snapshots.MetricSnapshots; |
11 | | -import java.io.BufferedWriter; |
12 | | -import java.io.ByteArrayOutputStream; |
13 | 11 | import java.io.IOException; |
14 | 12 | import java.io.OutputStream; |
15 | | -import java.io.OutputStreamWriter; |
16 | | -import java.io.Writer; |
17 | | -import java.nio.charset.StandardCharsets; |
18 | 13 | import java.util.concurrent.TimeUnit; |
19 | 14 | import org.openjdk.jmh.annotations.Benchmark; |
20 | 15 | import org.openjdk.jmh.annotations.Fork; |
21 | 16 | import org.openjdk.jmh.annotations.Measurement; |
22 | | -import org.openjdk.jmh.annotations.Scope; |
23 | | -import org.openjdk.jmh.annotations.State; |
24 | 17 | import org.openjdk.jmh.annotations.Warmup; |
25 | 18 |
|
26 | 19 | /** |
27 | 20 | * Benchmarks for writing a classic histogram (10 label combinations × 12 buckets) to text formats. |
28 | | - * |
29 | | - * <p>Three variants per format: |
30 | | - * |
31 | | - * <ul> |
32 | | - * <li>{@code writeToByteArray} — OutputStream path, new BufferedWriter created per call (~41 |
33 | | - * KB/op; IO stack allocation dominates). |
34 | | - * <li>{@code writeToNull} — OutputStream path to /dev/null, new BufferedWriter still created per |
35 | | - * call (same allocation as writeToByteArray; shows ByteArrayOutputStream write cost is |
36 | | - * negligible). |
37 | | - * <li>{@code reusingWriter} — Writer path, BufferedWriter reused across calls (~18 KB/op; saves |
38 | | - * the ~25 KB BufferedWriter char[] buffer and OutputStreamWriter per call). |
39 | | - * <li>{@code reusingWriterToNull} — Writer path, BufferedWriter reused, output to /dev/null. |
40 | | - * Lowest-allocation floor; isolates pure formatting CPU cost with zero IO stack overhead. |
41 | | - * </ul> |
42 | | - * |
43 | | - * <p>Key allocation sources: |
44 | | - * |
45 | | - * <ul> |
46 | | - * <li>BufferedWriter internal char[8192] buffer: 16,384 bytes per call (eliminated by reuse). |
47 | | - * <li>OutputStreamWriter + StreamEncoder state: ~1–2 KB per call (eliminated by reuse). |
48 | | - * <li>Remaining ~18 KB/op in reuse variants: ByteArrayOutputStream byte[] growth + any per-call |
49 | | - * String allocations remaining in the format path. |
50 | | - * </ul> |
| 21 | + * Output goes to /dev/null to isolate pure formatting CPU cost with zero IO overhead. |
51 | 22 | */ |
52 | 23 | @Fork(3) |
53 | 24 | @Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) |
@@ -84,110 +55,17 @@ public class HistogramTextFormatBenchmark { |
84 | 55 | private static final PrometheusTextFormatWriter PROMETHEUS_TEXT_FORMAT_WRITER = |
85 | 56 | PrometheusTextFormatWriter.create(); |
86 | 57 |
|
87 | | - @State(Scope.Benchmark) |
88 | | - public static class WriterState { |
89 | | - |
90 | | - final ByteArrayOutputStream byteArrayOutputStream; |
91 | | - |
92 | | - public WriterState() { |
93 | | - this.byteArrayOutputStream = new ByteArrayOutputStream(); |
94 | | - } |
95 | | - } |
96 | | - |
97 | | - @State(Scope.Benchmark) |
98 | | - public static class ReusableWriterState { |
99 | | - |
100 | | - final ByteArrayOutputStream openMetricsByteArrayOutputStream; |
101 | | - final ByteArrayOutputStream prometheusByteArrayOutputStream; |
102 | | - final BufferedWriter openMetricsWriter; |
103 | | - final BufferedWriter prometheusWriter; |
104 | | - |
105 | | - public ReusableWriterState() { |
106 | | - this.openMetricsByteArrayOutputStream = new ByteArrayOutputStream(); |
107 | | - this.prometheusByteArrayOutputStream = new ByteArrayOutputStream(); |
108 | | - this.openMetricsWriter = |
109 | | - new BufferedWriter( |
110 | | - new OutputStreamWriter(openMetricsByteArrayOutputStream, StandardCharsets.UTF_8)); |
111 | | - this.prometheusWriter = |
112 | | - new BufferedWriter( |
113 | | - new OutputStreamWriter(prometheusByteArrayOutputStream, StandardCharsets.UTF_8)); |
114 | | - } |
115 | | - } |
116 | | - |
117 | | - @State(Scope.Benchmark) |
118 | | - public static class ReusableWriterToNullState { |
119 | | - |
120 | | - final BufferedWriter openMetricsWriter; |
121 | | - final BufferedWriter prometheusWriter; |
122 | | - |
123 | | - public ReusableWriterToNullState() { |
124 | | - OutputStream nullOutputStream = TextFormatUtilBenchmark.NullOutputStream.INSTANCE; |
125 | | - this.openMetricsWriter = |
126 | | - new BufferedWriter(new OutputStreamWriter(nullOutputStream, StandardCharsets.UTF_8)); |
127 | | - this.prometheusWriter = |
128 | | - new BufferedWriter(new OutputStreamWriter(nullOutputStream, StandardCharsets.UTF_8)); |
129 | | - } |
130 | | - } |
131 | | - |
132 | | - @Benchmark |
133 | | - public OutputStream openMetricsWriteToByteArray(WriterState writerState) throws IOException { |
134 | | - ByteArrayOutputStream byteArrayOutputStream = writerState.byteArrayOutputStream; |
135 | | - byteArrayOutputStream.reset(); |
136 | | - OPEN_METRICS_TEXT_FORMAT_WRITER.write( |
137 | | - byteArrayOutputStream, SNAPSHOTS, EscapingScheme.ALLOW_UTF8); |
138 | | - return byteArrayOutputStream; |
139 | | - } |
140 | | - |
141 | 58 | @Benchmark |
142 | 59 | public OutputStream openMetricsWriteToNull() throws IOException { |
143 | 60 | OutputStream nullOutputStream = TextFormatUtilBenchmark.NullOutputStream.INSTANCE; |
144 | 61 | OPEN_METRICS_TEXT_FORMAT_WRITER.write(nullOutputStream, SNAPSHOTS, EscapingScheme.ALLOW_UTF8); |
145 | 62 | return nullOutputStream; |
146 | 63 | } |
147 | 64 |
|
148 | | - @Benchmark |
149 | | - public Writer openMetricsReusingWriter(ReusableWriterState state) throws IOException { |
150 | | - state.openMetricsByteArrayOutputStream.reset(); |
151 | | - OPEN_METRICS_TEXT_FORMAT_WRITER.write( |
152 | | - state.openMetricsWriter, SNAPSHOTS, EscapingScheme.ALLOW_UTF8); |
153 | | - return state.openMetricsWriter; |
154 | | - } |
155 | | - |
156 | | - @Benchmark |
157 | | - public Writer openMetricsReusingWriterToNull(ReusableWriterToNullState state) throws IOException { |
158 | | - OPEN_METRICS_TEXT_FORMAT_WRITER.write( |
159 | | - state.openMetricsWriter, SNAPSHOTS, EscapingScheme.ALLOW_UTF8); |
160 | | - return state.openMetricsWriter; |
161 | | - } |
162 | | - |
163 | | - @Benchmark |
164 | | - public OutputStream prometheusWriteToByteArray(WriterState writerState) throws IOException { |
165 | | - ByteArrayOutputStream byteArrayOutputStream = writerState.byteArrayOutputStream; |
166 | | - byteArrayOutputStream.reset(); |
167 | | - PROMETHEUS_TEXT_FORMAT_WRITER.write( |
168 | | - byteArrayOutputStream, SNAPSHOTS, EscapingScheme.ALLOW_UTF8); |
169 | | - return byteArrayOutputStream; |
170 | | - } |
171 | | - |
172 | 65 | @Benchmark |
173 | 66 | public OutputStream prometheusWriteToNull() throws IOException { |
174 | 67 | OutputStream nullOutputStream = TextFormatUtilBenchmark.NullOutputStream.INSTANCE; |
175 | 68 | PROMETHEUS_TEXT_FORMAT_WRITER.write(nullOutputStream, SNAPSHOTS, EscapingScheme.ALLOW_UTF8); |
176 | 69 | return nullOutputStream; |
177 | 70 | } |
178 | | - |
179 | | - @Benchmark |
180 | | - public Writer prometheusReusingWriter(ReusableWriterState state) throws IOException { |
181 | | - state.prometheusByteArrayOutputStream.reset(); |
182 | | - PROMETHEUS_TEXT_FORMAT_WRITER.write( |
183 | | - state.prometheusWriter, SNAPSHOTS, EscapingScheme.ALLOW_UTF8); |
184 | | - return state.prometheusWriter; |
185 | | - } |
186 | | - |
187 | | - @Benchmark |
188 | | - public Writer prometheusReusingWriterToNull(ReusableWriterToNullState state) throws IOException { |
189 | | - PROMETHEUS_TEXT_FORMAT_WRITER.write( |
190 | | - state.prometheusWriter, SNAPSHOTS, EscapingScheme.ALLOW_UTF8); |
191 | | - return state.prometheusWriter; |
192 | | - } |
193 | 71 | } |
0 commit comments