From bfda838e41ec5cf898fd894f019be46ab8e85e2e Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Tue, 18 Nov 2025 12:40:54 +0100 Subject: [PATCH] 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 --- .../util/MinusculeMatcherPerformanceTest.java | 105 +++++++++++------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/psi/util/MinusculeMatcherPerformanceTest.java b/platform/platform-tests/testSrc/com/intellij/psi/util/MinusculeMatcherPerformanceTest.java index 696c67f759b7..dbb8a66a4674 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/util/MinusculeMatcherPerformanceTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/util/MinusculeMatcherPerformanceTest.java @@ -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 = "*

aaa

com.sshtools.cipher
Class AES128Cbc

 java.lang.Object   \"extendedcom.maverick.ssh.cipher.SshCipher       \"extendedcom.maverick.ssh.crypto.engines.CbcBlockCipher           \"extendedcom.sshtools.cipher.AES128Cbc 

public class AES128Cbc
extends com.maverick.ssh.crypto.engines.CbcBlockCipher

This cipher can optionally be added to the J2SSH Maverick API. To add the ciphers from this package simply add them to the Ssh2Context

   import com.sshtools.cipher.*;   

Version:
Revision: 1.20

"; + MinusculeMatcher matcher = NameUtil.buildMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE); Benchmark.newBenchmark(getName(), () -> { - String pattern = "*

aaa

com.sshtools.cipher
Class AES128Cbc

 java.lang.Object   \"extendedcom.maverick.ssh.cipher.SshCipher       \"extendedcom.maverick.ssh.crypto.engines.CbcBlockCipher           \"extendedcom.sshtools.cipher.AES128Cbc 

public class AES128Cbc
extends com.maverick.ssh.crypto.engines.CbcBlockCipher

This cipher can optionally be added to the J2SSH Maverick API. To add the ciphers from this package simply add them to the Ssh2Context

   import com.sshtools.cipher.*;   

Version:
Revision: 1.20

"; - 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 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); + } } }