Skip to content

Commit 9e28dd0

Browse files
JMH benchmarks for URI filtering
Add benchmarks to ensure performance of URI filtering. The are executed as part of the Maven verify lifecycle phase when profile benchmark is active. The benchmarks show on my machine, that filtering without wildcards adds no more than 10ns and single wildcard matches no more than 40ns latency. Signed-off-by: Karsten Schnitter <k.schnitter@sap.com>
1 parent 1162154 commit 9e28dd0

3 files changed

Lines changed: 164 additions & 0 deletions

File tree

cf-java-logging-support-servlet/pom.xml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,67 @@
7474
</dependency>
7575

7676
</dependencies>
77+
78+
<build>
79+
<plugins>
80+
<!-- Register src/jmh/java as an additional test source root so it is
81+
compiled in the normal testCompile pass alongside src/test/java.
82+
The JMH annotation processor then has the full classpath available
83+
and generates its benchmark harness correctly. -->
84+
<plugin>
85+
<groupId>org.codehaus.mojo</groupId>
86+
<artifactId>build-helper-maven-plugin</artifactId>
87+
<version>${build-helper-maven-plugin.version}</version>
88+
<executions>
89+
<execution>
90+
<id>add-jmh-sources</id>
91+
<phase>generate-test-sources</phase>
92+
<goals>
93+
<goal>add-test-source</goal>
94+
</goals>
95+
<configuration>
96+
<sources>
97+
<source>src/jmh/java</source>
98+
</sources>
99+
</configuration>
100+
</execution>
101+
</executions>
102+
</plugin>
103+
</plugins>
104+
</build>
105+
106+
<profiles>
107+
<profile>
108+
<id>benchmark</id>
109+
<build>
110+
<plugins>
111+
<plugin>
112+
<groupId>org.codehaus.mojo</groupId>
113+
<artifactId>exec-maven-plugin</artifactId>
114+
<version>${exec.plugin.version}</version>
115+
<executions>
116+
<execution>
117+
<id>run-benchmarks</id>
118+
<phase>verify</phase>
119+
<goals>
120+
<goal>exec</goal>
121+
</goals>
122+
<configuration>
123+
<executable>${java.home}/bin/java</executable>
124+
<classpathScope>test</classpathScope>
125+
<arguments>
126+
<argument>-classpath</argument>
127+
<classpath/>
128+
<argument>com.sap.hcp.cf.logging.servlet.filter.benchmark.BenchmarkRunner
129+
</argument>
130+
</arguments>
131+
</configuration>
132+
</execution>
133+
</executions>
134+
</plugin>
135+
</plugins>
136+
</build>
137+
</profile>
138+
</profiles>
139+
77140
</project>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.sap.hcp.cf.logging.servlet.filter.benchmark;
2+
3+
import org.openjdk.jmh.runner.Runner;
4+
import org.openjdk.jmh.runner.RunnerException;
5+
import org.openjdk.jmh.runner.options.Options;
6+
import org.openjdk.jmh.runner.options.OptionsBuilder;
7+
8+
public class BenchmarkRunner {
9+
10+
public static void main(String[] args) throws RunnerException {
11+
Options options =
12+
new OptionsBuilder().include(RequestUriMatcherBenchmarks.class.getSimpleName()).forks(1).build();
13+
new Runner(options).run();
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.sap.hcp.cf.logging.servlet.filter.benchmark;
2+
3+
import com.sap.hcp.cf.logging.servlet.filter.RequestUriMatcher;
4+
import org.openjdk.jmh.annotations.*;
5+
6+
import java.util.concurrent.TimeUnit;
7+
8+
@BenchmarkMode(Mode.AverageTime)
9+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
10+
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
11+
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
12+
@Fork(1)
13+
public class RequestUriMatcherBenchmarks {
14+
15+
@State(Scope.Benchmark)
16+
public static class BenchmarkState {
17+
18+
// --- matchers ---
19+
public final RequestUriMatcher noPatterns = new RequestUriMatcher(null);
20+
public final RequestUriMatcher exactSingle = new RequestUriMatcher("/health");
21+
public final RequestUriMatcher wildcardDouble = new RequestUriMatcher("/actuator/**");
22+
public final RequestUriMatcher wildcardSingle = new RequestUriMatcher("/api/*/status");
23+
public final RequestUriMatcher multiPattern =
24+
new RequestUriMatcher("/health, /metrics, /actuator/**, /readyz, /livez");
25+
26+
// --- URIs ---
27+
public final String uriHealth = "/health";
28+
public final String uriApiOrders = "/api/orders";
29+
public final String uriActuatorDeep = "/actuator/health/liveness";
30+
public final String uriApiStatus = "/api/orders/status";
31+
public final String uriNoMatch = "/api/v1/orders/123/items";
32+
}
33+
34+
/** Baseline: no patterns configured — fastest possible path. */
35+
@Benchmark
36+
public boolean noPatterns(BenchmarkState s) {
37+
return s.noPatterns.matches(s.uriApiOrders);
38+
}
39+
40+
/** Single exact pattern, URI matches. */
41+
@Benchmark
42+
public boolean exactMatch(BenchmarkState s) {
43+
return s.exactSingle.matches(s.uriHealth);
44+
}
45+
46+
/** Single exact pattern, URI does not match. */
47+
@Benchmark
48+
public boolean exactNoMatch(BenchmarkState s) {
49+
return s.exactSingle.matches(s.uriApiOrders);
50+
}
51+
52+
/** Double-wildcard pattern (/actuator/**), deep URI matches. */
53+
@Benchmark
54+
public boolean doubleWildcardMatch(BenchmarkState s) {
55+
return s.wildcardDouble.matches(s.uriActuatorDeep);
56+
}
57+
58+
/** Double-wildcard pattern (/actuator/**), URI does not match. */
59+
@Benchmark
60+
public boolean doubleWildcardNoMatch(BenchmarkState s) {
61+
return s.wildcardDouble.matches(s.uriNoMatch);
62+
}
63+
64+
/** Single-segment wildcard ({@code /api/&#42;/status}), URI matches. */
65+
@Benchmark
66+
public boolean singleWildcardMatch(BenchmarkState s) {
67+
return s.wildcardSingle.matches(s.uriApiStatus);
68+
}
69+
70+
/**
71+
* Realistic actuator-exclusion scenario: five patterns, URI matches the third one (/actuator/**) — worst-case
72+
* traversal through the list.
73+
*/
74+
@Benchmark
75+
public boolean multiPatternMatch(BenchmarkState s) {
76+
return s.multiPattern.matches(s.uriActuatorDeep);
77+
}
78+
79+
/**
80+
* Realistic actuator-exclusion scenario: five patterns, URI matches none — full list traversal.
81+
*/
82+
@Benchmark
83+
public boolean multiPatternNoMatch(BenchmarkState s) {
84+
return s.multiPattern.matches(s.uriNoMatch);
85+
}
86+
}

0 commit comments

Comments
 (0)