test [text-matching]: improve minuscule matcher benchmarks

- prolong testing to make results more representative
- exclude data initialization from the benchmark
- do not generate error message during the benchmark

GitOrigin-RevId: a354baa17a21ef4db195b7f7bf003f7d70d586ad
This commit is contained in:
Alexander Zolotov
2025-11-18 17:27:00 +00:00
committed by intellij-monorepo-bot
parent 24ff777e68
commit bfda838e41
@@ -8,11 +8,9 @@ import com.intellij.tools.ide.metrics.benchmark.Benchmark;
import com.intellij.util.containers.ContainerUtil;
import junit.framework.TestCase;
import org.jetbrains.annotations.NonNls;
import org.junit.Assert;
import java.util.ArrayList;
import java.util.List;
import java.util.function.ToIntFunction;
public class MinusculeMatcherPerformanceTest extends TestCase {
public void testPerformance() {
@@ -30,39 +28,54 @@ public class MinusculeMatcherPerformanceTest extends TestCase {
Benchmark.newBenchmark("Matching", () -> {
for (int i = 0; i < 100_000; i++) {
for (MinusculeMatcher matcher : matching) {
Assert.assertTrue(matcher.matches(longName));
assertTrue(matcher.matches(longName));
matcher.matchingDegree(longName);
}
for (MinusculeMatcher matcher : nonMatching) {
Assert.assertFalse(matcher.matches(longName));
assertFalse(matcher.matches(longName));
}
}
}).runAsStressTest().start();
}
public void testOnlyUnderscoresPerformance() {
String small = StringUtil.repeat("_", 50000);
String smallWildcard = "*" + small;
MinusculeMatcher smallMatcher = NameUtil.buildMatcher(smallWildcard, NameUtil.MatchingCaseSensitivity.NONE);
String big = StringUtil.repeat("_", small.length() + 1);
String bigWildcard = "*" + big;
MinusculeMatcher bigMatcher = NameUtil.buildMatcher(big, NameUtil.MatchingCaseSensitivity.NONE);
Benchmark.newBenchmark(getName(), () -> {
String small = StringUtil.repeat("_", 50000);
String big = StringUtil.repeat("_", small.length() + 1);
assertMatches("*" + small, big);
assertDoesntMatch("*" + big, small);
for (int i = 0; i < 10_000; i++) {
assertMatches(smallMatcher, smallWildcard, big);
assertDoesntMatch(bigMatcher, bigWildcard, small);
}
}).runAsStressTest().start();
}
public void testRepeatedLetterPerformance() {
String big = StringUtil.repeat("Aaaaaa", 50000);
String pattern = "aaaaaaaaaaaaaaaaaaaaaaaa";
MinusculeMatcher matcher = NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
String mismatchPattern = "aaaaaaaaaaaaaaaaaaaaaaaab";
MinusculeMatcher mismatchMatcher = NameUtil.buildMatcher(mismatchPattern, NameUtil.MatchingCaseSensitivity.NONE);
Benchmark.newBenchmark(getName(), () -> {
String big = StringUtil.repeat("Aaaaaa", 50000);
assertMatches("aaaaaaaaaaaaaaaaaaaaaaaa", big);
assertDoesntMatch("aaaaaaaaaaaaaaaaaaaaaaaab", big);
for (int i = 0; i < 1_000; i++) {
assertMatches(matcher, pattern, big);
assertDoesntMatch(mismatchMatcher, mismatchPattern, big);
}
}).runAsStressTest().start();
}
public void testMatchingLongHtmlWithShortHtml() {
String pattern = "*<p> aaa <div id=\"a";
String html =
"<html> <body> <H2> <FONT SIZE=\"-1\"> com.sshtools.cipher</FONT> <BR> Class AES128Cbc</H2> <PRE> java.lang.Object <IMG SRC=\"../../../resources/inherit.gif\" ALT=\"extended by\">com.maverick.ssh.cipher.SshCipher <IMG SRC=\"../../../resources/inherit.gif\" ALT=\"extended by\">com.maverick.ssh.crypto.engines.CbcBlockCipher <IMG SRC=\"../../../resources/inherit.gif\" ALT=\"extended by\"><B>com.sshtools.cipher.AES128Cbc</B> </PRE> <HR> <DL> <DT>public class <B>AES128Cbc</B><DT>extends com.maverick.ssh.crypto.engines.CbcBlockCipher</DL> <P> This cipher can optionally be added to the J2SSH Maverick API. To add the ciphers from this package simply add them to the <A HREF=\"../../../com/maverick/ssh2/Ssh2Context.html\" title=\"class in com.maverick.ssh2\"><CODE>Ssh2Context</CODE></A> <blockquote><pre> import com.sshtools.cipher.*; </pre></blockquote> <P> <P> <DL> <DT><B>Version:</B></DT> <DD>Revision: 1.20</DD> </DL> <HR> </body> </html>";
MinusculeMatcher matcher = NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
Benchmark.newBenchmark(getName(), () -> {
String pattern = "*<p> aaa <div id=\"a";
String html =
"<html> <body> <H2> <FONT SIZE=\"-1\"> com.sshtools.cipher</FONT> <BR> Class AES128Cbc</H2> <PRE> java.lang.Object <IMG SRC=\"../../../resources/inherit.gif\" ALT=\"extended by\">com.maverick.ssh.cipher.SshCipher <IMG SRC=\"../../../resources/inherit.gif\" ALT=\"extended by\">com.maverick.ssh.crypto.engines.CbcBlockCipher <IMG SRC=\"../../../resources/inherit.gif\" ALT=\"extended by\"><B>com.sshtools.cipher.AES128Cbc</B> </PRE> <HR> <DL> <DT>public class <B>AES128Cbc</B><DT>extends com.maverick.ssh.crypto.engines.CbcBlockCipher</DL> <P> This cipher can optionally be added to the J2SSH Maverick API. To add the ciphers from this package simply add them to the <A HREF=\"../../../com/maverick/ssh2/Ssh2Context.html\" title=\"class in com.maverick.ssh2\"><CODE>Ssh2Context</CODE></A> <blockquote><pre> import com.sshtools.cipher.*; </pre></blockquote> <P> <P> <DL> <DT><B>Version:</B></DT> <DD>Revision: 1.20</DD> </DL> <HR> </body> </html>";
assertDoesntMatch(pattern, html);
for (int i = 0; i < 10_000; i++) {
assertDoesntMatch(matcher, pattern, html);
}
}).runAsStressTest().start();
}
@@ -81,48 +94,62 @@ public class MinusculeMatcherPerformanceTest extends TestCase {
}
private void assertDoesntMatchFast(String pattern, String name, String subTestName) {
Benchmark.newBenchmark(getName(), () -> assertDoesntMatch(pattern, name))
.runAsStressTest()
.startAsSubtest(subTestName);
MinusculeMatcher matcher = NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
Benchmark.newBenchmark(getName(), () -> {
for (int i = 0; i < 10_000; i++) {
assertDoesntMatch(matcher, pattern, name);
}
}).runAsStressTest().startAsSubtest(subTestName);
}
public void testMatchingLongRuby() {
String pattern =
"*# -*- coding: utf-8 -*-$:. unshift(\"/Library/RubyMotion/lib\")require 'motion/project'Motion::Project::App. setup do |app| # Use `rake config' to see complete project settings. app. sdk_version = '4. 3'end";
String name =
"# -*- coding: utf-8 -*-$:.unshift(\"/Library/RubyMotion/lib\")require 'motion/project'Motion::Project::App.setup do |app| # Use `rake config' to see complete project settings. app.sdk_version = '4.3' app.frameworks -= ['UIKit']end";
MinusculeMatcher matcher = NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
Benchmark.newBenchmark(getName(), () -> {
String pattern = "*# -*- coding: utf-8 -*-$:. unshift(\"/Library/RubyMotion/lib\")require 'motion/project'Motion::Project::App. setup do |app| # Use `rake config' to see complete project settings. app. sdk_version = '4. 3'end";
String name = "# -*- coding: utf-8 -*-$:.unshift(\"/Library/RubyMotion/lib\")require 'motion/project'Motion::Project::App.setup do |app| # Use `rake config' to see complete project settings. app.sdk_version = '4.3' app.frameworks -= ['UIKit']end";
assertDoesntMatch(pattern, name);
for (int i = 0; i < 100_000; i++) {
assertDoesntMatch(matcher, pattern, name);
}
}).runAsStressTest().start();
}
public void testLongStringMatchingWithItself() {
String s =
"the class with its attributes mapped to fields of records parsed by an {@link AbstractParser} or written by an {@link AbstractWriter}.";
String wildcard = "*" + s;
String substring = s.substring(0, 10);
MinusculeMatcher matcher = NameUtil.buildMatcher(s, NameUtil.MatchingCaseSensitivity.NONE);
MinusculeMatcher matcherWildcard = NameUtil.buildMatcher(wildcard, NameUtil.MatchingCaseSensitivity.NONE);
Benchmark.newBenchmark(getName(), () -> {
assertMatches(s, s);
assertMatches("*" + s, s);
for (int i = 0; i < 100_000; i++) {
assertMatches(matcher, s, s);
assertMatches(matcherWildcard, wildcard, s);
assertPreference(s, s.substring(0, 10), s);
assertPreference("*" + s, s.substring(0, 10), s);
assertPreference(matcher, substring, s);
assertPreference(matcherWildcard, substring, s);
}
}).runAsStressTest().start();
}
private static void assertMatches(@NonNls String pattern, @NonNls String name) {
assertTrue(pattern + " doesn't match " + name + "!!!", caseInsensitiveMatcher(pattern).matches(name));
private static void assertMatches(MinusculeMatcher matcher, @NonNls String pattern, @NonNls String name) {
if (!matcher.matches(name)) {
fail(pattern + " doesn't match " + name + "!!!");
}
}
private static void assertDoesntMatch(@NonNls String pattern, @NonNls String name) {
assertFalse(pattern + " matches " + name + "!!!", caseInsensitiveMatcher(pattern).matches(name));
private static void assertDoesntMatch(MinusculeMatcher matcher, @NonNls String pattern, @NonNls String name) {
if (matcher.matches(name)) {
fail(pattern + " matches " + name + "!!!");
}
}
private static MinusculeMatcher caseInsensitiveMatcher(String pattern) {
return NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
}
static void assertPreference(@NonNls String pattern, @NonNls String less, @NonNls String more) {
MinusculeMatcher matcher = NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.FIRST_LETTER);
ToIntFunction<String> matchingDegree = matcher::matchingDegree;
int iLess = matchingDegree.applyAsInt(less);
int iMore = matchingDegree.applyAsInt(more);
assertTrue(iLess + ">=" + iMore + "; " + less + ">=" + more, iLess < iMore);
static void assertPreference(MinusculeMatcher matcher, @NonNls String less, @NonNls String more) {
int iLess = matcher.matchingDegree(less);
int iMore = matcher.matchingDegree(more);
if (iLess >= iMore) {
fail(iLess + ">=" + iMore + "; " + less + ">=" + more);
}
}
}