Вы можете
создать хеш-таблицу или наиболее часто совпадающий пользовательский агент и избежать совпадения с регулярным выражением.
хранить скомпилировать новый Regex(pattern, RegexOptions.Compiled)
вместо просто pattern
объединить регулярные выражения в одном регулярном выражении и использовать преимущества RegexOptions.Compiledи RegexOptions.CultureInvariantIgnoreCase
вместо сопоставления дважды (один раз с IsMatch
и один раз с Matches
) сопоставляют один раз (Matches
) и проверяют, является ли MatchCollection пустой
Это только отправная точка - я мог бы предложить больше идей по прочтению кода:)
Редактировать Еще один:
- Избегайте синтаксического анализа версии с другим регулярным выражением - только safari требует специальных обработок в соответствии с вашей конфигурацией.Попробуйте «поймать» версию с тем же регулярным выражением, что и у browserid.(Я бы сейчас сделал исключение для сафари)
Например, у вас может быть один экземпляр статического регулярного выражения, например:
private static readonly Regex _regex = new Regex(
"(?i)"
+ "(?<browserid>(?:firefox/|opera/|chrome/|chrome/|safari/|msie[+_ ]?))"
+ "(?<version>[\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
Вы можете легко получить доступправильные подгруппы, используя match.Groups["browserid"]
и match.Groups["version"]
.Это почти исключает все использование вашего списка структур браузера.
Единственное, что он по-прежнему обслуживает, это регулярное выражение исключения (regex_not).Я предлагаю сначала выполнить повторное профилирование с единственным положительным регулярным выражением и посмотреть, есть ли еще проблема с производительностью, оставшаяся до жарки мелкой рыбы.
Тест
Я написал тест (см. Ниже).Я буду обновлять это постепенно, пока не потерю интерес :) ( Я знаю, что мой набор данных не является репрезентативным. Если вы загрузите файл, я протестирую его с этим )
замена отдельных регулярных выражений одним статически скомпилированным регулярным выражением, ускоряется с 14 с до 2,1 с ( 6-кратное ускорение ); это только с заменой самого внешнего совпадения
замена regex_not / regex_version на предварительно скомпилированные регулярные выражения не имела большого значения для моего набора тестов (но я нефактические совпадающие значения используются, поэтому это имеет смысл)
.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class Program
{
private struct Browser
{
public int ID;
public string name;
public Regex regex_match, regex_not, regex_version;
public int regex_group;
}
private static readonly Regex _regex = new Regex("(?i)"
+ "(?<browserid>(?:firefox/|opera/|chrome/|chrome/|safari/|msie[+_ ]?))"
+ "(?<version>[\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
public static void Main(string[] args)
{
Browser[] browsers = new Browser[5];
for (int i = 0; i < 5; i++)
{
browsers[i].ID = i;
}
browsers[0].name = "Firefox";
browsers[1].name = "Opera";
browsers[2].name = "Chrome";
browsers[3].name = "Safari";
browsers[4].name = "Internet Explorer";
browsers[0].regex_match = new Regex("(?i)firefox/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[1].regex_match = new Regex("(?i)opera/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[2].regex_match = new Regex("(?i)chrome/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[3].regex_match = new Regex("(?i)safari/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[4].regex_match = new Regex("(?i)msie([+_ ]|)([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
// OPTIMIZATION #2
browsers[0].regex_not = new Regex("(?i)flock", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[1].regex_not = null;
browsers[2].regex_not = null;
browsers[3].regex_not = new Regex("(?i)android|arora|chrome|shiira", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[4].regex_not = new Regex("(?i)webtv|omniweb|opera", RegexOptions.Compiled | RegexOptions.CultureInvariant);
// OPTIMIZATION #2
browsers[0].regex_version = new Regex("(?i)firefox/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[1].regex_version = new Regex("(?i)opera/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[2].regex_version = new Regex("(?i)chrome/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[3].regex_version = new Regex("(?i)version/([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[4].regex_version = new Regex("(?i)msie([+_ ]|)([\\d\\.]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
browsers[0].regex_group = 1;
browsers[1].regex_group = 1;
browsers[2].regex_group = 1;
browsers[3].regex_group = 1;
browsers[4].regex_group = 2;
Dictionary<string, int> browser_counts = new Dictionary<string, int>();
var lookupBrowserId = new Dictionary<string, int> {
{ "firefox/", 0 },
{ "opera/", 1 },
{ "chrome/", 2 },
{ "safari/", 3 },
{ "msie+", 4 },
{ "msie_", 4 },
{ "msie ", 4 },
{ "msie", 4 },
};
for (int i=1; i<20; i++)
foreach (var line in System.IO.File.ReadAllLines("/etc/dictionaries-common/words"))
{
// OPTIMIZATION #1 START
Match match = _regex.Match(line);
{
if (match.Success)
{
Browser b = browsers[lookupBrowserId[match.Groups["browserid"].Value]];
// OPTIMIZATION #1 END
// OPTIMIZATION #2
if (b.regex_not != null && b.regex_not.IsMatch(line))
continue;
string strBrowser = b.name;
if (b.regex_version != null)
{
// OPTIMIZATION #2
string strVersion = b.regex_version.Match(line).Groups[b.regex_group].Value;
int intPeriod = strVersion.IndexOf('.');
if (intPeriod > 0)
{
strBrowser += " " + strVersion.Substring(0, intPeriod);
}
}
if (!browser_counts.ContainsKey(strBrowser))
{
browser_counts.Add(strBrowser, 1);
}
else
{
browser_counts[strBrowser]++;
}
break;
}
}
}
}
}