2015-11-08 3 views
7

Почему длина ввода в регулярном выражении не влияет на производительность и как это возможно?Почему regex не заботится о длине строки

Сгенерированная строка такова: 128 случайных символов. затем два числа в скобках. и это повторяется много раз.

128 radnom characters....(-2435346|45436) 128 radnom characters....(-32525562|-325346) 

Регулярное выражение получает все числа внутри скобок. вот шаблон.

\(([-+]?\d+\|[-+]?\d+)\) 

Так матчи будет как

-2435346|45436 
-32525562|-325346 
etc... 

Вот код, который делает тест. Я запускаю секундомер после ввода, потому что я хочу только оценить время согласования.

Random rand = new Random(); 
Func<string> getRandString = // generates 128 random characters. 
    () => Enumerable.Range(0, 128).Select(x => (char) rand.Next()).Aggregate("", (a, b) => a + b); 
Func<int> getRandInteger =() => rand.Next(); // generates random number. 
string format = "{0}({1}|{2})"; 

// Generate the big string. 
StringBuilder bigstr = new StringBuilder(); 
for (int i = 0; i < 100; i++) // repeat 100 times. 
{ 
    bigstr.Append(string.Format(format, getRandString(), getRandInteger(), getRandInteger())); 
} 
string input = bigstr.ToString(); 

Stopwatch stopwatch = Stopwatch.StartNew(); 
var matches = Regex.Matches(input, @"\(([-+]?\d+\|[-+]?\d+)\)"); 
stopwatch.Stop(); 
Console.WriteLine("Time Elapsed :\t{0}\nInputLength :\t{1}\nMatches Count :\t{2}", stopwatch.Elapsed, input.Length, matches.Count); 

Это выход в моей консоли, если я повторить петлю 10 раз.

Time Elapsed : 00:00:00.0004132 
InputLength : 1500 
Matches Count : 10 

Если я повторяю цикл 1000 раз.

Time Elapsed : 00:00:00.0004373 // seriously? 
InputLength : 149937 
Matches Count : 1000 

Если я повторяю цикл 1000000 раз.

Time Elapsed : 00:00:00.0004900 // wtf? 
InputLength : 149964452 
Matches Count : 1000000 

Скриншот если вы не верите

enter image description here

Является ли это какое-то ленивые оценки? если да, то как это может показать количество матчей? как всегда я делал это под отладчиком, и я мог видеть матчи.

Есть ли что-то особенное в моем шаблоне регулярных выражений, что делает его быстрым? но как длина строки не влияет на производительность? Я не могу понять.

+0

Здесь нет ничего особенного. Ваш regex движок будет перемещать строку и сохранять все состояния, которые соответствуют вашему регулярному выражению, и вы являетесь эталоном в 1000-кратной более длинной строке, которая не является большой проблемой на данный момент. гораздо более крупные строки.Или, может быть, ваш скамьярикон несправедлив. – Kasramvd

+1

Возможно, вас заинтересует [этот мой ответ] (http://stackoverflow.com/a/32618592/3764814), если вы хотите увидеть некоторые подробности о строчном алгоритме поиска, используемом движком .NET regex. –

+1

Правильный бенчмаркинг: https://andreyakinshin.gitbooks.io/performancebookdotnet/content/science/microbenchmarking.html –

ответ

9

Одна из причин, по которой это происходит, заключается в том, что matches.Count оценивается lazily. Когда вы остановите свой секундомер, помощник готов выполнить сопоставление, но ничего не нашел. Только когда вы вызываете matches.Count, он начинает работать, но вы уже остановили время.

Простым решением является перемещение matches.Count, чтобы вызвать часть кода, которую вы используете.

Другая причина заключается в том, что вы включаете время, необходимое для анализа регулярного выражения. Это относительно дорогой операцией, так что вы должны сделать это перед включением секундомера для лучшего времени:

Regex testRegex = new Regex(@"\(([-+]?\d+\|[-+]?\d+)\)"); 
Stopwatch stopwatch = Stopwatch.StartNew(); 
var matches = testRegex.Matches(input); 
var matchesCount = matches.Count; 
stopwatch.Stop(); 
Console.WriteLine("Time Elapsed :\t{0}\nInputLength :\t{1}\nMatches Count :\t{2}", stopwatch.Elapsed, input.Length, matchesCount); 

Сейчас рост времени на 10 матчей против 10000 матчей становится значительным (00:00:00.0129844 против 00:00:00.0843982), хотя не такой большой, как можно было бы ожидать разницы во входной длине в тысячу раз.

+0

LOL, я думал, что время, затраченное на создание строки. какая глупая ошибка! Я также сделал это под отладчиком, но я поставил точку останова после 'Regex.Matches'. Спасибо, в любом случае! –

+1

@ M.kazemAkhgary Здесь [реализация 'MatchCollection'] (http://reflector.webtropy.com/default.aspx/[email protected]/[email protected]/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/fx/ SRC/Regex/System/Текст/RegularExpressions/RegexMatchCollection @ CS/1305376/RegexMatchCollection @ сСт). Из кода видно, что метод GetMatch, который выполняет всю работу, называется лениво. – dasblinkenlight

+0

@dasblinkenlight Эта ссылка, которую вы предоставили, не работает для меня (веб-страница неверна, и весь код имеет тот же цвет, что и фон), [здесь ссылка на тот же метод в исходном источнике] (http: // sourcesource .microsoft.com/System/регулярное выражение/система/текст/regularexpressions/RegexMatchCollection.cs.html # 682620f47b442b05) –

Смежные вопросы