Skip to content

Commit 24c817e

Browse files
committed
improve pipe perfs
1 parent 1c49a79 commit 24c817e

File tree

2 files changed

+83
-17
lines changed

2 files changed

+83
-17
lines changed

dd-java-agent/agent-bootstrap/src/jmh/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeOutputStreamBenchmark.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
import org.openjdk.jmh.annotations.State;
2222
import org.openjdk.jmh.annotations.Warmup;
2323

24+
/*
25+
* Benchmark Mode Cnt Score Error Units
26+
* InjectingPipeOutputStreamBenchmark.withPipe avgt 2 16.802 us/op
27+
* InjectingPipeOutputStreamBenchmark.withoutPipe avgt 2 13.001 us/op
28+
*/
2429
@State(Scope.Benchmark)
2530
@Warmup(iterations = 1, time = 30, timeUnit = SECONDS)
2631
@Measurement(iterations = 2, time = 30, timeUnit = SECONDS)

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/buffer/InjectingPipeOutputStream.java

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import java.io.OutputStream;
66

77
/**
8-
* A circular buffer that holds n+1 bytes and with a lookbehind buffer of n bytes. The first time
9-
* that the latest n bytes matches the marker, a content is injected before.
8+
* A circular buffer with a lookbehind buffer of n bytes. The first time that the latest n bytes
9+
* matches the marker, a content is injected before.
1010
*/
1111
public class InjectingPipeOutputStream extends FilterOutputStream {
1212
private final byte[] lookbehind;
@@ -43,47 +43,108 @@ public void write(int b) throws IOException {
4343
out.write(b);
4444
return;
4545
}
46+
47+
if (bufferFilled) {
48+
out.write(lookbehind[pos]);
49+
}
50+
4651
lookbehind[pos] = (byte) b;
4752
pos = (pos + 1) % lookbehind.length;
4853

54+
if (!bufferFilled) {
55+
bufferFilled = pos == 0;
56+
}
57+
4958
if (marker[matchingPos++] == b) {
5059
if (matchingPos == marker.length) {
5160
found = true;
61+
out.write(lookbehind[pos]);
62+
pos = (pos + 1) % lookbehind.length;
5263
out.write(contentToInject);
5364
if (onContentInjected != null) {
5465
onContentInjected.run();
5566
}
56-
drain((pos + 1) % lookbehind.length, marker.length);
57-
return;
67+
drain(lookbehind.length - 1);
5868
}
5969
} else {
6070
matchingPos = 0;
6171
}
72+
}
6273

63-
if (!bufferFilled) {
64-
bufferFilled = pos == lookbehind.length - 1;
74+
@Override
75+
public void write(byte[] b, int off, int len) throws IOException {
76+
if (found) {
77+
out.write(b, off, len);
78+
return;
6579
}
80+
if (len > marker.length * 2) {
81+
int idx = arrayContains(b, marker);
82+
if (idx >= 0) {
83+
// we have a full match. just write everything
84+
found = true;
85+
drain(lookbehind.length);
86+
out.write(b, off, idx);
87+
out.write(contentToInject);
88+
if (onContentInjected != null) {
89+
onContentInjected.run();
90+
}
91+
out.write(b, off + idx, len - idx);
92+
} else {
93+
// we don't have a full match. write everything in a bulk except the lookbehind buffer
94+
// sequentially
95+
for (int i = off; i < off + marker.length; i++) {
96+
write(b[i]);
97+
}
98+
drain(lookbehind.length);
99+
out.write(b, off + marker.length, len - marker.length * 2);
100+
for (int i = len - marker.length; i < len; i++) {
101+
write(b[i]);
102+
}
103+
drain(lookbehind.length);
104+
}
105+
} else {
106+
// use slow path because the length to write is small and within the lookbehind buffer size
107+
super.write(b, off, len);
108+
}
109+
}
66110

67-
if (bufferFilled) {
68-
super.write(lookbehind[(pos + 1) % lookbehind.length]);
111+
private int arrayContains(byte[] array, byte[] search) {
112+
for (int i = 0; i < array.length - search.length; i++) {
113+
if (array[i] == search[0]) {
114+
boolean found = true;
115+
int k = i;
116+
for (int j = 1; j < search.length; j++) {
117+
k++;
118+
if (array[k] != search[j]) {
119+
found = false;
120+
break;
121+
}
122+
}
123+
if (found) {
124+
return i;
125+
}
126+
}
69127
}
128+
return -1;
70129
}
71130

72-
private void drain(int from, int size) throws IOException {
73-
while (size-- > 0) {
74-
super.write(Character.valueOf((char) lookbehind[from]));
75-
from = (from + 1) % lookbehind.length;
131+
private void drain(int len) throws IOException {
132+
if (bufferFilled) {
133+
for (int i = 0; i < len; i++) {
134+
out.write(lookbehind[(pos + i) % lookbehind.length]);
135+
}
136+
} else {
137+
out.write(this.lookbehind, 0, pos);
76138
}
139+
pos = 0;
140+
matchingPos = 0;
141+
bufferFilled = false;
77142
}
78143

79144
@Override
80145
public void close() throws IOException {
81146
if (!found) {
82-
if (bufferFilled) {
83-
drain((pos + 2) % lookbehind.length, marker.length - 1);
84-
} else {
85-
drain(0, pos);
86-
}
147+
drain(lookbehind.length);
87148
}
88149
super.close();
89150
}

0 commit comments

Comments
 (0)