Created
June 18, 2022 16:40
-
-
Save orbyfied/1a3d84b0892891da16d68b2d7d402d5b to your computer and use it in GitHub Desktop.
Java openjdk-17 - String Input Validation Benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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