Skip to content

Instantly share code, notes, and snippets.

@orbyfied
Created June 18, 2022 16:40
Show Gist options
  • Save orbyfied/1a3d84b0892891da16d68b2d7d402d5b to your computer and use it in GitHub Desktop.
Save orbyfied/1a3d84b0892891da16d68b2d7d402d5b to your computer and use it in GitHub Desktop.
Java openjdk-17 - String Input Validation Benchmark
package test;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.regex.Pattern;
/**
* Simple benchmark to test input validation speed.
* Using UUIDs in this example.
* @author orbyfied
*/
public class StringInputValidationBenchmark {
@Test
public void benchmarkAll() {
// generate strings
generateStrings();
// run benchmarks
benchmark_RegexDontParse();
benchmark_RegexDoParse(); /* practical */
benchmark_Exceptions(); /* practical */
}
/* --------- Generation --------- */
private static final int MAX_PASSES = 1_000_000;
// get correct uuid length to ensure UUID#fromString
// doesnt immediately stop
private static final int STRING_LEN = 36;
private static final Random random = new Random(System.currentTimeMillis());
private static final char[] chars = new char[]
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', /* '-', */
};
String[] strings = new String[MAX_PASSES];
String createRandomString(int len) {
StringBuilder builder = new StringBuilder(len);
for (int i = 0; i < len; i++)
builder.append(chars[random.nextInt(chars.length)]);
return builder.toString();
}
void generateStrings() {
for (int i = 0; i < MAX_PASSES; i++)
strings[i] = createRandomString(STRING_LEN);
}
/* ---------- Benchmark ----------- */
void performBenchmark(
String name,
Consumer<Integer> func,
int maxPasses,
long maxTime
) {
maxPasses--; // account for i++ returning last value
long start = System.nanoTime();
int i = 0;
while (
(System.nanoTime() - start) < maxTime
&& (i++) < maxPasses
)
func.accept(i);
long end = System.nanoTime();
long tns = end - start;
long tms = tns / 1_000_000;
long tpp = tns / i;
// print results
System.out.println("+- " + name + ": " + i + " Passes " +
(i >= maxPasses ? "(MAX) " : "") + " | Total Time: " + tns + "ns (" + tms + "ms)" +
" | Avg. time/pass: " + tpp + "ns (" + tpp / 1_000_000 + "ms)");
}
/* ------------ Benchmarks ----------- */
// regex
private static final Pattern uuidPattern = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}");
void benchmark_RegexDontParse() {
performBenchmark(
"UUID_RegexDontParse",
i -> {
uuidPattern.matcher(strings[i]).matches(); /* wont do anything with the result */
},
MAX_PASSES,
1_000_000_000 /* 1 sec ns */
);
}
void benchmark_RegexDoParse() {
performBenchmark(
"UUID_RegexDoParse",
i -> {
String str = strings[i];
if (uuidPattern.matcher(str).matches()) {
// account for parsing done in fromString
UUID.fromString(str);
}
},
MAX_PASSES,
1_000_000_000 /* 1 sec ns */
);
}
// exceptions
void benchmark_Exceptions() {
performBenchmark(
"UUID_Exception",
i -> {
try {
UUID.fromString(strings[i]);
} catch (IllegalArgumentException ignored) { }
},
MAX_PASSES,
1_000_000_000 /* 1 sec */
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment