Почему функция isprefix быстрее, чем Startswith в С#?
Кто-нибудь знает, почему функция С# (.NET) StartsWith значительно медленнее, чем IsPrefix?
Ответы
Ответ 1
Я думаю, что он в основном извлекает текущую культуру потока.
Если вы измените тест Marc, чтобы использовать эту форму String.StartsWith
:
Stopwatch watch = Stopwatch.StartNew();
CultureInfo cc = CultureInfo.CurrentCulture;
for (int i = 0; i < LOOP; i++)
{
if (s1.StartsWith(s2, false, cc)) chk++;
}
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds + "ms; chk: " + chk);
он приближается.
Если вы используете s1.StartsWith(s2, StringComparison.Ordinal)
, это намного быстрее, чем использование CompareInfo.IsPrefix
(в зависимости от CompareInfo
, конечно). На моей коробке результаты (не научно):
- s1.StartsWith(s2): 6914ms
- s1.StartsWith(s2, false, culture): 5568ms
- compare.IsPrefix(s1, s2): 5200ms
- s1.StartsWith(s2, StringComparison.Ordinal): 1393мс
Очевидно, это потому, что это действительно просто сравнение 16-битных целых чисел в каждой точке, что довольно дешево. Если вы не хотите, чтобы проверка, чувствительная к культуре, и производительность для вас особенно важна, то перегрузка, которую я использовал.
Ответ 2
Запускает с вызовом IsPrefix внутренне. Он назначает информацию о культуре перед вызовом IsPrefix.
Ответ 3
Хороший вопрос; для теста я получаю:
9156ms; chk: 50000000
6887ms; chk: 50000000
Испытательная установка:
using System;
using System.Diagnostics;
using System.Globalization;
class Program
{
static void Main()
{
string s1 = "abcdefghijklmnopqrstuvwxyz", s2 = "abcdefg";
const int LOOP = 50000000;
int chk = 0;
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
if (s1.StartsWith(s2)) chk++;
}
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds + "ms; chk: " + chk);
chk = 0;
watch = Stopwatch.StartNew();
CompareInfo ci = CultureInfo.CurrentCulture.CompareInfo;
for (int i = 0; i < LOOP; i++)
{
if (ci.IsPrefix(s1, s2)) chk++;
}
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds + "ms; chk: " + chk);
}
}
Ответ 4
Проверьте источник IsPrefix. Дело в том, что в некоторых случаях он будет медленнее, чем StartsWith
только потому, что он фактически использует StartsWith и выполняет несколько операций.
[System.Security.SecuritySafeCritical] // auto-generated
public unsafe virtual bool IsPrefix(String source, String prefix, CompareOptions options)
{
if (source == null || prefix == null) {
throw new ArgumentNullException((source == null ? "source" : "prefix"),
Environment.GetResourceString("ArgumentNull_String"));
}
Contract.EndContractBlock();
int prefixLen = prefix.Length;
if (prefixLen == 0)
{
return (true);
}
if (options == CompareOptions.OrdinalIgnoreCase)
{
return source.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
}
if (options == CompareOptions.Ordinal)
{
return source.StartsWith(prefix, StringComparison.Ordinal);
}
if ((options & ValidIndexMaskOffFlags) != 0) {
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
}
// to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to
// the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
return (InternalFindNLSStringEx(
m_dataHandle, m_handleOrigin, m_sortName,
GetNativeCompareFlags(options) | Win32Native.FIND_STARTSWITH | ((source.IsAscii() && prefix.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
source, source.Length, 0, prefix, prefix.Length) > -1);
}