This commit is contained in:
26
benchmark/build.gradle
Normal file
26
benchmark/build.gradle
Normal file
@@ -0,0 +1,26 @@
|
||||
plugins {
|
||||
alias catalog.plugins.gradle.jmh
|
||||
alias catalog.plugins.lombok
|
||||
}
|
||||
|
||||
import me.champeau.jmh.JMHTask
|
||||
|
||||
dependencies {
|
||||
implementation rootProject
|
||||
|
||||
implementation catalog.jwo
|
||||
implementation catalog.xz
|
||||
implementation catalog.jackson.databind
|
||||
|
||||
jmhAnnotationProcessor catalog.lombok
|
||||
}
|
||||
|
||||
jmh {
|
||||
threads = 4
|
||||
iterations = 2
|
||||
fork = 1
|
||||
warmupIterations = 1
|
||||
warmupForks = 0
|
||||
resultFormat = 'JSON'
|
||||
}
|
||||
|
170
benchmark/src/jmh/java/net/woggioni/gbcs/benchmark/Main.java
Normal file
170
benchmark/src/jmh/java/net/woggioni/gbcs/benchmark/Main.java
Normal file
@@ -0,0 +1,170 @@
|
||||
package net.woggioni.gbcs.benchmark;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class Main {
|
||||
|
||||
@SneakyThrows
|
||||
private static Properties loadProperties() {
|
||||
Properties properties = new Properties();
|
||||
try (final var is = Main.class.getResourceAsStream("/benchmark.properties")) {
|
||||
properties.load(is);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static final Properties properties = loadProperties();
|
||||
|
||||
@State(Scope.Thread)
|
||||
public static class ExecutionPlan {
|
||||
private final Random random = new Random(101325);
|
||||
|
||||
@Getter
|
||||
private final HttpClient client = HttpClient.newHttpClient();
|
||||
|
||||
private final Map<String, byte[]> entries = new HashMap<>();
|
||||
|
||||
public final Map<String, byte[]> getEntries() {
|
||||
return Collections.unmodifiableMap(entries);
|
||||
}
|
||||
|
||||
public Map.Entry<String, byte[]> newEntry() {
|
||||
final var keyBuffer = new byte[0x20];
|
||||
random.nextBytes(keyBuffer);
|
||||
final var key = Base64.getUrlEncoder().encodeToString(keyBuffer);
|
||||
final var value = new byte[0x1000];
|
||||
random.nextBytes(value);
|
||||
return Map.entry(key, value);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public HttpRequest.Builder newRequestBuilder(String key) {
|
||||
final var requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(getServerURI().resolve(key));
|
||||
String user = getUser();
|
||||
if (user != null) {
|
||||
requestBuilder.header("Authorization", buildAuthorizationHeader(user, getPassword()));
|
||||
}
|
||||
return requestBuilder;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public URI getServerURI() {
|
||||
return new URI(properties.getProperty("gbcs.server.url"));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String getUser() {
|
||||
return Optional.ofNullable(properties.getProperty("gbcs.server.username"))
|
||||
.filter(Predicate.not(String::isEmpty))
|
||||
.orElse(null);
|
||||
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String getPassword() {
|
||||
return Optional.ofNullable(properties.getProperty("gbcs.server.password"))
|
||||
.filter(Predicate.not(String::isEmpty))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private String buildAuthorizationHeader(String user, String password) {
|
||||
final var b64 = Base64.getEncoder().encode(String.format("%s:%s", user, password).getBytes(StandardCharsets.UTF_8));
|
||||
return "Basic " + new String(b64);
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@Setup(Level.Trial)
|
||||
public void setUp() {
|
||||
try (final var client = HttpClient.newHttpClient()) {
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
final var pair = newEntry();
|
||||
final var requestBuilder = newRequestBuilder(pair.getKey())
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(pair.getValue()));
|
||||
final var response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
if (201 != response.statusCode()) {
|
||||
throw new IllegalStateException(Integer.toString(response.statusCode()));
|
||||
} else {
|
||||
entries.put(pair.getKey(), pair.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Iterator<Map.Entry<String, byte[]>> it = null;
|
||||
|
||||
private Map.Entry<String, byte[]> nextEntry() {
|
||||
if (it == null || !it.hasNext()) {
|
||||
it = getEntries().entrySet().iterator();
|
||||
}
|
||||
return it.next();
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
public void get(ExecutionPlan plan) {
|
||||
final var client = plan.getClient();
|
||||
final var entry = plan.nextEntry();
|
||||
final var requestBuilder = plan.newRequestBuilder(entry.getKey())
|
||||
.header("Accept", "application/octet-stream")
|
||||
.GET();
|
||||
final var response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
if (200 != response.statusCode()) {
|
||||
throw new IllegalStateException(Integer.toString(response.statusCode()));
|
||||
} else {
|
||||
if (!Arrays.equals(entry.getValue(), response.body())) {
|
||||
throw new IllegalStateException("Retrieved unexpected value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
public void put(Main.ExecutionPlan plan) {
|
||||
final var client = plan.getClient();
|
||||
final var entry = plan.nextEntry();
|
||||
|
||||
final var requestBuilder = plan.newRequestBuilder(entry.getKey())
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(entry.getValue()));
|
||||
|
||||
final var response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
if (201 != response.statusCode()) {
|
||||
throw new IllegalStateException(Integer.toString(response.statusCode()));
|
||||
}
|
||||
}
|
||||
}
|
1
benchmark/src/jmh/resources/benchmark.properties
Normal file
1
benchmark/src/jmh/resources/benchmark.properties
Normal file
@@ -0,0 +1 @@
|
||||
gbcs.server.url= http://localhost:8080
|
Reference in New Issue
Block a user