Skip to content

Commit 25600fa

Browse files
danmoseleyCopilot
andcommitted
Add LeadingStrings benchmarks for binary and non-ASCII regex patterns
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5c57420 commit 25600fa

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using BenchmarkDotNet.Attributes;
6+
using MicroBenchmarks;
7+
8+
namespace System.Text.RegularExpressions.Tests
9+
{
10+
/// <summary>
11+
/// Performance tests for regex alternation patterns on binary data.
12+
/// Exercises the LeadingStrings vs FixedDistanceSets heuristic on non-text input.
13+
/// </summary>
14+
[BenchmarkCategory(Categories.Libraries, Categories.Regex, Categories.NoWASM)]
15+
public class Perf_Regex_LeadingStrings_BinaryData
16+
{
17+
[Params(
18+
RegexOptions.None,
19+
RegexOptions.Compiled
20+
#if NET7_0_OR_GREATER
21+
, RegexOptions.NonBacktracking
22+
#endif
23+
)]
24+
public RegexOptions Options { get; set; }
25+
26+
private string _binaryText;
27+
private Regex _regex;
28+
29+
// A small embedded binary-like fragment (~64 bytes) containing null bytes and typical PE patterns,
30+
// duplicated in Setup to create a ~1MB corpus. Using a fixed seed ensures identical input across TFMs.
31+
private static readonly byte[] s_binarySeed =
32+
{
33+
0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
34+
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35+
0x50, 0x45, 0x00, 0x00, 0x64, 0x86, 0x02, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x00, 0x00,
36+
0x2E, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x2E, 0x72, 0x73, 0x72, 0x63, 0x00, 0x00, 0x00,
37+
};
38+
39+
[GlobalSetup]
40+
public void Setup()
41+
{
42+
// Duplicate the seed to ~1MB
43+
const int targetSize = 1024 * 1024;
44+
char[] chars = new char[targetSize];
45+
for (int i = 0; i < targetSize; i++)
46+
chars[i] = (char)s_binarySeed[i % s_binarySeed.Length];
47+
_binaryText = new string(chars);
48+
49+
// Alternation of 4-byte sequences that appear in the seed (non-null starting)
50+
_regex = new Regex(@"MZ\x90\x00|PE\x00\x00|\.text|\.rsrc|Hello|d\x86\x02\x00", Options);
51+
}
52+
53+
[Benchmark]
54+
[MemoryRandomization]
55+
public int Count() => Perf_Regex_Industry.Count(_regex, _binaryText);
56+
}
57+
58+
/// <summary>
59+
/// Performance tests for regex alternation patterns on non-ASCII text (Russian).
60+
/// </summary>
61+
[BenchmarkCategory(Categories.Libraries, Categories.Regex, Categories.NoWASM)]
62+
public class Perf_Regex_LeadingStrings_NonAscii
63+
{
64+
[Params(
65+
RegexOptions.None,
66+
RegexOptions.Compiled
67+
#if NET7_0_OR_GREATER
68+
, RegexOptions.NonBacktracking
69+
#endif
70+
)]
71+
public RegexOptions Options { get; set; }
72+
73+
private string _input;
74+
private Regex _alternation;
75+
private Regex _alternationIgnoreCase;
76+
77+
// Opening of Anna Karenina (Tolstoy), ~1KB of natural Russian prose.
78+
// "All happy families are alike; each unhappy family is unhappy in its own way.
79+
// Everything was in confusion in the Oblonskys' house. The wife had discovered that the husband
80+
// was carrying on an intrigue with their former French governess, and she had announced to her
81+
// husband that she could not go on living in the same house with him..."
82+
private const string Text =
83+
"Все счастливые семьи похожи друг на друга, каждая несчастливая семья несчастлива по-своему. " +
84+
"Всё смешалось в доме Облонских. Жена узнала, что муж был в связи с бывшею в их доме француженкою-гувернанткой, " +
85+
"и объявила мужу, что не может жить с ним в одном доме. Положение это продолжалось уже третий день и чувствовалось " +
86+
"и самими супругами, и всеми членами семьи, и домочадцами. Все члены семьи и домочадцы чувствовали, что нет смысла " +
87+
"в их сожительстве и что на каждом постоялом дворе случайно сошедшиеся люди более связаны между собой, чем они, " +
88+
"члены семьи и домочадцы Облонских. Жена не выходила из своих комнат, мужа третий день не было дома. " +
89+
"Дети бегали по всему дому, как потерянные; англичанка поссорилась с экономкой и написала записку приятельнице, " +
90+
"прося приискать ей новое место; повар ушёл ещё вчера со двора, во время обеда; чёрная кухарка и кучер просили расчёт.";
91+
92+
[GlobalSetup]
93+
public void Setup()
94+
{
95+
var sb = new System.Text.StringBuilder(100000);
96+
for (int i = 0; i < 100; i++)
97+
sb.Append(Text);
98+
_input = sb.ToString();
99+
100+
_alternation = new Regex("семьи|Облонских|француженкою|домочадцами|сожительстве|англичанка|экономкой|кухарка", Options);
101+
_alternationIgnoreCase = new Regex("(?i)семьи|Облонских|француженкою|домочадцами|сожительстве|англичанка|экономкой|кухарка", Options);
102+
}
103+
104+
[Benchmark]
105+
[MemoryRandomization]
106+
public int Count() => Perf_Regex_Industry.Count(_alternation, _input);
107+
108+
[Benchmark]
109+
[MemoryRandomization]
110+
public int CountIgnoreCase() => Perf_Regex_Industry.Count(_alternationIgnoreCase, _input);
111+
}
112+
}

0 commit comments

Comments
 (0)