Нашел вот такой тест:
И собственно возник вопрос:
Почему все именно так?
На мой взгляд, разница не должна быть на столько ощутимой.
Может немного неожиданный ответ, но:
Сравнение строк без учетом регистра менее производительно, чем с учетом регистра потому, что при сравнении без учета регистра нужно применять больше правил при сравнении каждого символа.
В случае Windows в исходники нативного API заглянуть не получится, но .net core, насколько я понял, использует для сравнения ICU. Код самого сравнения - там проход по строке с применением правил для каждого символа + пара частных случаев.
Важно то, что метод сравнения строк нельзя выбирать по результатам бенчмарка. Это разные методы, и они дают разный результат на одних и тех же входных данных. Это как сравнивать сложение, деление и интегрирование. Стоит использовать то, что вам нужно. А не использовать всегда Ordinal.
К результатам конкретного бенчмарка, использованного в вопросе стоит относится с сомнением, т.к. автор бенчмарка допустил сразу все возможные ошибки
DateTime.Now
Провёл бенчмарк с помощью BenchmarkDotnet и получил следующие результаты:
[CoreJob, CoreRtJob]
[RPlotExporter, RankColumn, MemoryDiagnoser]
public class StringEqualityBenchmark
{
[Params(10, 100, 1000, 10_000, 100000)] // размер строки в байтах.
public int N;
private string firstString;
private string secondString;
[GlobalSetup]
public void Setup()
{
{
var bytes = new byte[N];
new Random(42).NextBytes(bytes);
firstString = Encoding.UTF8.GetString(bytes);
}
{
var bytes = new byte[N];
new Random(42).NextBytes(bytes);
secondString = Encoding.UTF8.GetString(bytes);
}
}
[Benchmark]
public bool SimpleEquals() => firstString == secondString;
[Benchmark]
public bool ObjectEquals() => object.Equals(firstString, secondString);
[Benchmark]
public bool ReferenceEquals() => object.ReferenceEquals(firstString, secondString);
[Benchmark]
public bool ToLowerEquals() => firstString.ToLower() == secondString.ToLower();
[Benchmark]
public bool ToUpperEquals() => firstString.ToUpper() == secondString.ToUpper();
[Benchmark]
public bool InvariantCultureIgnoreCaseEquals() => string.Equals(firstString, secondString, StringComparison.InvariantCultureIgnoreCase);
[Benchmark]
public bool InvariantCultureEquals() => string.Equals(firstString, secondString, StringComparison.InvariantCulture);
[Benchmark]
public bool OrdinalIgnoreCaseEquals() => string.Equals(firstString, secondString, StringComparison.OrdinalIgnoreCase);
[Benchmark]
public bool OrdinalEquals() => string.Equals(firstString, secondString, StringComparison.Ordinal);
[Benchmark]
public int StringCompare() => string.Compare(firstString, secondString);
[Benchmark]
public int StringOrdinalCompare() => string.CompareOrdinal(firstString, secondString);
}
Из этого всего следует, что самое быстрое сравнение - естественно, по равенству ссылок, добавил в бенчмарк для наглядности. Использование ToLower и ToUpper почти не отличаются по производительности, где-то одно быстрее, где-то другое. Но оба плохи тем, что создают копию строки, что может быть катастрофично если вызывается часто и/или для больших строк.
Самым лучшим вариантом является использование ordinal (согласно предложению от MS)
Игнорирование кейса это дополнительные условия проверки, поэтому она выполняется дольше, но не требует дополнительной памяти, как можно увидеть из результатов бенчмарка.
Для того чтобы понять, что конкретно происходит при сравнении строк, нужно найти исходники для:
ComNlsInfo::InternalCompareStringOrdinalIgnoreCase
или
ComNlsInfo::InternalCompareString
т.к. происходит вызов нативного апи
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Как правильно оформить select-запрос в SQL, который берет только последнею запись со столбца finished?
Создаю форум на laravel, использую стандартный набор (Eloquent, Blade) Задача такова: Есть разделы, у разделов категории, у категорий темы, у тем сообщенияНа...