Skip to content

Commit a574173

Browse files
committed
Optimize TextFormatUtil
Optimize TextFormatUtil to prefer bulk writes. Signed-off-by: Philippe Marschall <[email protected]>
1 parent 1245396 commit a574173

File tree

2 files changed

+63
-7
lines changed

2 files changed

+63
-7
lines changed

prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/TextFormatUtil.java

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,53 @@ static void writeTimestamp(Writer writer, long timestampMs) throws IOException {
3535
}
3636

3737
static void writeEscapedLabelValue(Writer writer, String s) throws IOException {
38-
for (int i = 0; i < s.length(); i++) {
39-
char c = s.charAt(i);
38+
// optimize for the common case where no escaping is needed
39+
int start = 0;
40+
// #indexOf is a vectorized intrinsic
41+
int backslashIndex = s.indexOf('\\', start);
42+
int quoteIndex = s.indexOf('\"', start);
43+
int newlineIndex = s.indexOf('\n', start);
44+
45+
int allEscapesIndex = backslashIndex & quoteIndex & newlineIndex;
46+
while (allEscapesIndex != -1) {
47+
int escapeStart = Integer.MAX_VALUE;
48+
if (backslashIndex != -1) {
49+
escapeStart = backslashIndex;
50+
}
51+
if (quoteIndex != -1) {
52+
escapeStart = Math.min(escapeStart, quoteIndex);
53+
}
54+
if (newlineIndex != -1) {
55+
escapeStart = Math.min(escapeStart, newlineIndex);
56+
}
57+
58+
// bulk write up to the first character that needs to be escaped
59+
if (escapeStart > start) {
60+
writer.write(s, start, escapeStart - start);
61+
}
62+
char c = s.charAt(escapeStart);
63+
start = escapeStart + 1;
4064
switch (c) {
4165
case '\\':
42-
writer.append("\\\\");
66+
writer.write("\\\\");
67+
backslashIndex = s.indexOf('\\', start);
4368
break;
4469
case '\"':
45-
writer.append("\\\"");
70+
writer.write("\\\"");
71+
quoteIndex = s.indexOf('\"', start);
4672
break;
4773
case '\n':
48-
writer.append("\\n");
74+
writer.write("\\n");
75+
newlineIndex = s.indexOf('\n', start);
4976
break;
50-
default:
51-
writer.append(c);
5277
}
78+
79+
allEscapesIndex = backslashIndex & quoteIndex & newlineIndex;
80+
}
81+
// up until the end nothing needs to be escaped anymore
82+
int remaining = s.length() - start;
83+
if (remaining > 0) {
84+
writer.write(s, start, remaining);
5385
}
5486
}
5587

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.prometheus.metrics.expositionformats;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.io.IOException;
6+
import java.io.StringWriter;
7+
import org.junit.jupiter.api.Test;
8+
9+
class TextFormatUtilTest {
10+
11+
@Test
12+
void writeEscapedLabelValue() throws IOException {
13+
assertEquals("aa\\\\bb\\\"cc\\ndd\\nee\\\\ff\\\"gg", escape("aa\\bb\"cc\ndd\nee\\ff\"gg"));
14+
assertEquals("\\\\", escape("\\"));
15+
assertEquals("\\\\\\\\", escape("\\\\"));
16+
assertEquals("text", escape("text"));
17+
}
18+
19+
private static String escape(String s) throws IOException {
20+
StringWriter writer = new StringWriter();
21+
TextFormatUtil.writeEscapedLabelValue(writer, s);
22+
return writer.toString();
23+
}
24+
}

0 commit comments

Comments
 (0)