Почему regex.IsMatch(str) быстрее, чем str.EndsWith(инвариантная культура)?

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

Ниже приведен фрагмент кода, сравнивающий

  • x.EndsWith(y, InvariantCulture)
  • Regex(y, Compiled | CultureInvariant).IsMatch(x)

Я получаю следующие цифры:

=============================
Regex   : 00:00:01.2235890. Ignore this: 16666666
EndsWith: 00:00:03.2194626. Ignore this: 16666666
=============================
Regex   : 00:00:01.0979105. Ignore this: 16666666
EndsWith: 00:00:03.2346031. Ignore this: 16666666
=============================
Regex   : 00:00:01.0687845. Ignore this: 16666666
EndsWith: 00:00:03.3199213. Ignore this: 16666666

Другими словами, EndsWith требуется в 3 раза больше времени Regex.

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

EndsWith(x, InvariantCulture) сводится к некоторой проверке аргументов, а затем extern int nativeCompareOrdinalEx(String, int, String, int, int), которую я ожидаю быть быстрымудаp > . (Как правильно указал @nhahtdh, в случае InvariantCulture он вызывает CultureInfo.InvariantCulture.CompareInfo.IsSuffix which calls InternalFindNLSStringEx. Я случайно следил за тропом Ordinal)

NB: я только узнал, что при вызове EndsWith с Ordinal вместо InvariantCulture EndsWith получает намного быстрее, чем Regex... К сожалению, нет RegexOptions.Ordinal для сравнения с.

Я также ожидал, что скомпилированное регулярное выражение будет быстрым, но как он может превзойти специализированный метод?

Le code:

string[] BunchOfIDs =
{
    "[email protected]@[email protected]@abcße",
    "[email protected]@[email protected]@abcßX",
    "[email protected]@[email protected]@abcße",
    "[email protected]@[email protected]",
    "[email protected]@[email protected]@aXcße",
    "[email protected]@[email protected]@aYcße",
};

var endsWith = "@abcße";
var endsWithRegex = new Regex("@abcße$", RegexOptions.None);

int reps = 20000000;
for (int i = 0; i < 3; i++)
{
    Console.WriteLine("=============================");
    int x = 0;
    var sw = Stopwatch.StartNew();
    for (int j = 0; j < reps; j++)
    {
        x += BunchOfIDs[j % BunchOfIDs.Length].EndsWith(endsWith, StringComparison.InvariantCulture) ? 1 : 2;
    }
    Console.WriteLine("EndsWith: " + sw.Elapsed + ". Ignore this: " + x);

    x = 0;
    sw = Stopwatch.StartNew();
    for (int j = 0; j < reps; j++)
    {
        x += endsWithRegex.IsMatch(BunchOfIDs[j % BunchOfIDs.Length]) ? 1 : 2;
    }
    Console.WriteLine("Regex   : " + sw.Elapsed + ". Ignore this: " + x);
}

Ответ 1

Это может быть

Потому что StringComparison.InvariantCulture != RegexOptions.CultureInvariant!

Этот фрагмент

var str = "ss";
var endsWith = "ß";
var endsWithRegex = new Regex("ß$",
    RegexOptions.Compiled | RegexOptions.CultureInvariant);
Console.WriteLine(str.EndsWith(endsWith, StringComparison.InvariantCulture)
    + " vs "
    + endsWithRegex.IsMatch(str));

печатает

True vs False

Итак, похоже, что RegexOptions.CultureInvariant не подразумевает вещей, подразумеваемых StringComparison.InvariantCulture. Является ли RegexOptions.CultureInvariant, возможно, больше похожим на StringComparison.Ordinal?